QL教程3-抓住纵火犯

前言

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

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

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

抓住纵火犯

了解QL谓词和类,以解决作为侦探的第二个谜团

正当我们成功找到小偷并将金冠归还城堡的时候,村庄内又发生了另外一桩可怕的罪行。一大早,几个人在村北的一块地里面纵火,烧毁了所有的庄稼!

现在的我们因为之前找回王冠的事件,已经在村里面拥有了QL侦探专家的声誉,因此我们被村民们恳求再次找到罪魁祸首。

这一次,我们有一些额外的信息,村庄的南北之间存在着强烈的竞争,因此纵火犯住在南方。

阅读以下示例以了解如何在 QL 中定义谓词和类。这些使我们的查询逻辑更容易理解,并有助于简化侦探工作。

选择南方人

这一次我们只需要考虑特定的村民群体,即居住在村庄南部的村民,因此我们可以定义一个新的isSouthern谓词getLocation() = "south"

predicate isSouthern(Person p) {
  p.getLocation() = "south"
}

谓词isSouthern(p)采用单个参数p并检查p是否满足属性p.getLocation()='south'

Note:

  • 谓词的名称总是以小写字母开头
  • 我们还可以用结果定义谓词,在这种情况下,关键字predicate被替换为结果的类型,即我们写函数的时候的返回结果result的类型。
int getAge(){
    result=...
}

现在我们可以列出所有的南方人(南方人震怒)

import tutorial
predicate isSouthern(Person p) {
    p.getLocation()="south"
}
from Person p
where isSouthern(p)
select p

这是一个简化逻辑的好方法,但我们可以继续优化。目前的逻辑是查看每一个人,然后将其筛选限制为我们所需要的,即isSouthern(p)。与此相反的是,我们可以定义一个新的类Southerner,其中直接包含我们想要考虑的人

class Southerner extends Person {
  Southerner() { isSouthern(this) }
}

这里即面向对象里面的继承和多态

QL中的类表示一个逻辑属性:当一个值满足该属性时,它就是该类的成员。也就是说,一个值可以在许多类中(在一个特定的类中并不能阻止它也在其他类中)

该表达式isSouthern(this)定义了类表示的逻辑属性,称为特征谓词。它使用一个特殊变量this,如果该属性isSouthern(this)成立,则该Person是南方人

Note:

  • 如果你熟悉面向对象的编程,你可能会秦湘语将特征谓词视为一个类的构造函数。然后事实并非如此,它是一个不创建任何对象的逻辑属性

我们总是需要在现有的(更大的)类中定义一个新的满足我们需要的类。(可以理解为,表达宽泛的类大多数时候被我们所继承,而我们创建的新类中会表现出多态),在我们的例子中,Southerner是一种特殊的Person,因为Person的范围太大了,所以当我们需要找到范围相对小的南方人的时候,需要继承Person并筛选。

使用这个类,我们现在可以简单快速的得到所有生活在南方的人

from Southerner s
select s

可能你现在已经注意到,有些谓词是附加的,例如p.getAge(),而另外有一些则不是,例如isSouthern(p)。这是因为getAge()是成员谓词,即只适用于类成员的谓词。我们可以在类中定义这样的成员谓词。在这种情况下,getAge()Person类中被定义。与此相反,isSouthern是单独定义的,不在任何类中。如果有过面向对象编程的同学这里应该很容易理解。

成员谓词特别有用,因为我们可以轻松地将它们链接在一起。例如,p.getAge().sqrt()首先获取年龄,然后计算该数字的平方根

旅行限制

我们要考虑的另外一个因素是:当王冠被盗之后村落实施的旅行限制。

最初,村民在村内的出行没有限制。因此,该谓词适用于任何人和任何地区,以下查询列出了所有村民,因为他们都可以前往北方:isAllowdIn(string region)

from Person p
where p.isAllowedIn("north")
select p

然而,在最近的盗窃事件发生后,村民们对潜伏在村子周围的犯罪分子更加担心,他们不再允许10岁以下的儿童外出旅行。

这意味着isAllowedIn(string region)不再适用于所有人和所有地区,因此如果是孩子,我们应该暂时覆盖原始谓词。

首先定义一个Child包含所有10岁以下村民的类,然后将其重新定义为成员谓词,以保证孩子仅在自己的区域内移动,这由region = this.getLocation()表示

class Child extends Person{
    Child(){ this.getAge()<10}
    override predicate isAllowedIn(string region) {
        region=this.getLocation()
    }
}

现在尝试将isAllowedIn(string region)应用到村民身上,如果村民不是孩子,那么原始的定义就会生效,当村民是孩子时,原始的定义就会被重写,即isAllowedIn

我们知道纵火犯住在南方,他们一定能够到北方旅行(这样才能够成功纵火)。编写QL查询以查找可能的嫌疑人,我们还可以扩展select条件来列出嫌疑人的年龄,这样你就可以清楚地看到所有的孩子都已经从列表中排除。

import tutorial
predicate isSouthern(Person p) {
    p.getLocation()="south"
}
class Southerner extends Person{
    Southerner(){isSouthern(this)}
}
class Child extends Person{
    Child(){ this.getAge()<10}
    override predicate isAllowedIn(string region) {
        region=this.getLocation()
    }
}
from Southerner s
where s.isAllowedIn("north")
select s,s.getAge()

现在我们可以继续收集更多线索,找出到底是哪一个嫌疑人引发了火灾

识别秃头罪犯

当我们询问北方村落的人们有关纵火犯的更多信息,幸运的是,住在田边的农民看到火灾刚开始的时候有两个人逃跑。他只看到他们的头顶,发现他们都是秃头。

这是一个非常有用的线索,于是我们编写了一个QL查询来选择所有秃头的人

from Southerner s
where s.isAllowedIn("north") and not exists( string temp | s.getHairColor()=temp )
select s,s.getAge()

当然我们可以将其封装一下

predicate isBald(Person p) {
    not exists(string c | p.getHairColor()=c )
}

最后调用即可

from Southerner s
where s.isAllowedIn("north") and isBald(s)
select s,s.getAge()

通过这个查询我们来选择允许进入北方的秃头南方人

yes!我们现在已经找到了两个纵火犯!他们被村民们逮捕了,在村民中你的声望更高了 😃

END

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

GIF GIF
posted @ 2022-01-16 16:20  春告鳥  阅读(178)  评论(0编辑  收藏  举报