QL教程2-找到小偷

前言

在我们使用codeql进行代码审计之前,不妨先学习一些QL的基础语法,磨刀不误砍柴工。

官方教程链接:https://codeql.github.com/docs/writing-codeql-queries/ql-tutorials/

在这个教程中我们作为一个侦探,为了解决遇到的问题使用QL进行相应的调查

寻找小偷

我们作为侦探在虚拟的村庄中寻找小偷,在这个过程中我们学习使用逻辑连接词,量词和聚合函数

有一个小村庄隐藏在深山中,村子分为东南西北四个部分,中央矗立着一座黑暗而神秘的城堡。城堡内,在最高的塔楼里,锁着国王珍贵的金冠。一天晚上,发生了一起可怕的罪行,一个小偷闯入塔楼并偷走了王冠!

你知道小偷一定是村里的人,因为只有村里的人知道王冠,经过一些专业的侦探工作,你将会获得村里所有人的名单以及他们的一些个人详细信息。

尽管如此,你还是不知道是谁偷走了王冠,所以你在村子里走来走去寻找线索,村民们的行为非常可疑,您确信他们掌握了有关小偷的信息。但他们拒绝直接与您分享他们的知识,只会通过你的提问来回答"是"或者"不是"

如果大家有玩过剧本杀的话,可以发现有点像剧本杀里面的海龟汤,出题人只回答是,不是两个答案 😃,我们通过提问来构建整个故事的脉络

作为侦探,你开始提出一些创造性的问题并记录下答案,以便稍后将它们与你后面得到的信息进行比较

需要手动搜索的信息太多,因此您决定使用新获得的 QL 技能来帮助您进行调查……

我们定义了许多的QL谓词来帮助你从表中提取数据,QL谓词是一个小型查询,它表达了各种数据之间的关系并描述了它们之间的一些属性。在这种情况下,谓词为你提供有关一个人的信息,例如他们的身高或者年龄。

QL官方将这些谓词存储在 QL 库tutorial.qll中。要访问此库,请在VScode中输入import tutorial

库用来存储常用谓词,这也是方便代码的复用,就像是写python代码的时候import一样。方便我们不必在每次需要时都定义谓词。

例如,使用t.getHeight()返回t的高度。

接下来我们开始寻找小偷

  • 村民对小偷身高超过150厘米吗?的回答。要使用此信息,您可以编写以下查询来列出所有身高超过 150 厘米的村民。这些都是可能的嫌疑人。
import tutorial
from Person t
where t.getHeight()>150
select t

接着我们使用逻辑连接词来编写更加复杂的查询,以便组合不同的信息

  • 例如,如果您知道小偷 30 岁以上并且有一头棕色头发,您可以使用以下where子句来链接两个谓词
import tutorial
from Person t
where t.getAge()>30 and t.getHairColor()="brown"
select t
  • 如果小偷不住在城堡的北边
import tutorial
from Person t
where not t.getLocation() = "north"
select t
  • 如果小偷有棕色头发或黑色头发
where t.getHairColor() = "brown" or t.getHairColor() = "black"
  • 您还可以将这些连接词组合成更长的语句
where t.getAge() > 30
  and (t.getHairColor() = "brown" or t.getHairColor() = "black")
  and not t.getLocation() = "north"

这里需要注意的是,没有括号的话,and的优先级高于or,所以我们需要使用括号来保证查询按照我们的预期进行

然而谓词并不总是只返回一个值。例如,如果一个人的黑色头发变成灰色了,p.getHairColor()将返回两个值:黑色和灰色。

小偷秃了怎么办?在这种情况下,小偷没有头发,所以getHairColor()谓词根本不返回任何结果!

如果你知道小偷肯定不是秃子,那么一定有一种颜色与小偷的头发颜色相匹配。在 QL 中表达这一点的一种方法是引入一个新的string类型变量 c ,并选择那些t与t.getHairColor()的值匹配的变量c。

from Person t, string c
where t.getHairColor() = c
select t

这里需要注意的是,我们只是引入了临时变量c,在后面where子句中就不再需要用到它了,在这种情况下,我们最好使用exists

from Person t
where exists(string c | t.getHairColor() = c)
select t

exists引入了一个string类型的临时变量c,并且只有至少有一个满足string c | t.getHairColor() = c条件

现在我们回到追查小偷的主线任务上,用刚才我们学到的方法,编写一个查询来查找满足前八个问题答案的人

import tutorial
from Person t
where 
t.getHeight()>150 and 
not t.getHairColor()="blond" and 
exists( string s | t.getHairColor()=s ) and 
not t.getAge()<30 and 
t.getLocation()="east" and 
(t.getHairColor()="black" or t.getHairColor()="brown") and 
not (t.getHeight()>180 and t.getHeight()<190) and 
exists(Person temp | t.getAge()<temp.getAge())
select t

可以看到尽管如此我们还是没有找到小偷

不过范围已经大大缩小了,我们离解开谜团已经越来越近了,想要在嫌疑人名单中找出谁是真正的小偷,我们必须收集更多的信息并在下一步中完善查询。

如果我们想要找到村里面最老,最年轻,最高或者最矮的人应该怎么办?在上面我们知道可以使用exists。实际上我们还可以用max或者min来执行该操作。

通常,聚合是对多条数据执行操作并返回单个值作为其输出的函数,常见的聚合是count,max,min,avgsum,使用聚合的一般方法是:

<aggregate>(<variable declarations> | <logical formula> | <expression>)

例如,使用max聚合来查找村里面最年长的人的年龄:

import tutorial
select max(int i  | exists(Person p | p.getAge()=i ) | i)

可以得到最年长的是的年龄是97岁,在这个聚合中考虑所有的整数i,并且限制i为与村里人年龄相匹配的值,然后返回最大的匹配整数

但是我们应当如何在实际查询中使用它呢?

如果小偷是村里最年长的人,那么你知道小偷的年龄等于村民的最大年龄,这样就可以构建查询为:

import tutorial
from Person t
where t.getAge()=max(int i  | exists(Person p | p.getAge()=i ) | i)
select t

这时得到结果为Susannah

但是这样的过程缺点也很明显,即通用的聚合语法很长而且很不方便。在大多数情况下,我们可以省略聚合的某些部分,一个特别有用的QL特性是有序聚合,这允许我们使用order by对表达式进行排序

例如,我们使用order by有序聚合选择最老的村民

import tutorial
select max(Person p  |  | p order by p.getAge())

接下来是更多关于聚合的案例

  • 村东最矮的人

min(Person p | p.getLocation() = "east" | p order by p.getHeight())

  • 村南人口数

count(Person p | p.getLocation() = "south" | p)

  • 村民平均身高

avg(Person p | | p.getHeight())

  • 所有棕色头发村民的年龄总和

sum(Person p | p.getHairColor() = "brown" | p.getAge())

学习了这些之后,我们就可以出发抓住罪魁祸首了,将剩余的问题使用QL进行查询:

import tutorial
from Person t
where not t=max(Person temp |  | temp order by temp.getHeight())
and t.getHeight()<avg(float i | exists(Person temp | temp.getHeight() = i) | i) 
and t=max(Person temp | temp.getLocation()="east"| temp order by temp.getAge())
select t

事实上,即便不加上前面的八个查询,我们也能够得到最后小偷的身份,Hester,他这是那个小偷!

END

建了一个微信的安全交流群,欢迎添加我微信备注进群,一起来聊天吹水哇,以及一个会发布安全相关内容的公众号,欢迎关注 😃

GIF GIF
posted @ 2022-01-15 19:38  春告鳥  阅读(479)  评论(0编辑  收藏  举报