CodeQL系列之基础语法

CodeQL系列之基础语法

0x01 语法规则

import <language> /* 导入对应的语言包 */

/* 一些谓词、类的设置 即定义一些方法或者类*/

from /* 声明变量等 即定义各种变量*/
where /* 设置逻辑表达式 即代码逻辑*/
select /* 打印结果 即输出*/

0x02 常用的一些方法

0x02-1 Field

类或者实例的字段
import java

from Field f
select f

image-20220607170137144

image-20220607170242714

0x02-2 一些常见后缀的含义

XXXType是类型:IntegralType

XXXStmt是语句:ReturnStmt、SwitchStmt、IfStmt

XXXLiteral是类型:StringLiteral、IntegerLiteral、NullLiteral

TypeString 是字符串类型

0x02-3 predicate

定义没有返回类型的谓词(也就是方法)

image-20220103212829892

0x02-4 result

可以理解为函数的返回值
int test(){
    result = 5
}

select test()

image-20220112143351715

0x02-5 bindingset

函数有参数时,需要绑定
bindingset[a, b]
int test(int a, int b){
    result = a + b
}

select test(1, 2)

image-20220112143530039

0x02-6 getDeclaringType

获取匹配到的元素是在哪个类里定义的
import java

from Field f, FieldRead read
where
  f.hasName("chastityUtil") and
  f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.audit", "AuditAuthController") and
  f = read.getField()
select f.getDeclaration()

image-20220607165019525

0x02-7 getName

获取匹配到的元素的名字
import java

from Field f, FieldRead read
where
  f.hasName("chastityUtil") and
  f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.audit", "AuditAuthController") and
  f = read.getField()
select f, f.getName()

image-20220607163454754

0x02-8 getLocation

获取匹配到的元素所在文件的路径
import semmle.code.java.frameworks.spring.SpringController
from Call call, Callable parseExpression
where
    call.getCallee() = parseExpression and
    parseExpression.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON") and
    parseExpression.hasName("parseObject")
select call.getLocation()

image-20220607164340888

0x02-9 getType

获取匹配到的元素的类型
import java

from Field f, FieldRead read
where
  f.hasName("chastityUtil") and
  f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.audit", "AuditAuthController") and
  f = read.getField()
select f.getType()

image-20220607165609775

0x02-10 exists

判断语句里是否为True

下面的例子因为a不等于b,所以exists的结果为False,所以select为空

import java

from int a
where
    a = 9 and
    exists(int b | 
        b = 8 and 
        a = b
    ) 
select a

image-20220607174607190

下面的例子因为a等于b,所以exists的结果为True,所以select打印了a

image-20220607174645657

0x02-11 Expr

表示各种表达式的通用超类。

image-20211230211724488

0x02-12 hasQualifiedName

个人理解:has(是的意思),QualifiedName是全称的意思,即判断某类的全称是什么,那么就要完整的包名和类名了。

image-20211230213344882

import java

from Field f
where
  f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.audit", "AuditAuthController") 
select f

image-20220607175208710

打印出了AuditAuthController类的所有Field

image-20220607175244459

0x02-13 BooleanLiteral

布尔类型

BooleanLiteral 的值为true or false

image-20220103152621465

0x02-14 getFile

获取匹配到的元素所在的文件
import java

from Field f
where
  f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.audit", "AuditAuthController") 
select f.getFile()

image-20220607175402156

0x02-15 数据类型

StringLiteral、IntegerLiteral、NullLiteral等

StringLiteral –> 字符串类型

IntegerLiteral –> 数字类型

NullLiteral –> 空类型

import java

from MethodAccess ma, Method method
where
  ma.getMethod().overrides*(method) and
  method.hasName("append") and
  method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.lang", "StringBuilder") and
  ma.getAnArgument() instanceof StringLiteral
select ma, method, ma.getAnArgument()

匹配到的元素的参数都是字符串类型

image-20220607194656559

0x02-16 concat

字符串的拼接
import semmle.code.java.frameworks.spring.SpringController

from SpringRequestMappingMethod route

where exists(route.getARequestParameter())

select route, route.getValue(), concat(string s | s = route.getARequestParameter().toString() | s, " ")

image-20220607194936284

0x02-17 getAnAnnotation

获取匹配到元素的注解
class SpringControllerAnnotation2 extends AnnotationType {
    SpringControllerAnnotation2() {
      // `@Controller` used directly as an annotation.
      this.hasQualifiedName("org.springframework.stereotype", "Controller")
      or
      // `@Controller` can be used as a meta-annotation on other annotation types.
      this.getAnAnnotation().getType() instanceof SpringControllerAnnotation2
    }
}

this.getAnAnnotation().getType() 获取注解,例如@RestController

image-20220607200652946

this.hasQualifiedName(“org.springframework.stereotype”, “Controller”) 注解其实本质是接口,所在的包是在org.springframework.stereotype.Controller

image-20220607200503054

所以就能够找到注解@Controller了

0x03 Call和Callable

0x03-1 Call.getCaller和Call.getCallee

Call表示调用Callable的这个过程(方法调用,构造器调用等等),Callable表示可调用的方法或构造器的集合。

Call类提供两个谓词函数来获取调用方和被调用方。getCaller()与getCallee()。

getCaller 调用方,例如下面例子的getRequestDatas

getCallee 被调用方,例如下面例子的parseObject

Callable 表示可调用的方法或构造器的集合,例如下面例子的parseObject

Call 表示过程,例如下面例子的JSONObject.parseObject(result)

Callable 等价于 call.getCallee()

import semmle.code.java.frameworks.spring.SpringController
from Call call, Callable parseExpression
where
    call.getCallee() = parseExpression and
    parseExpression.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON") and
    parseExpression.hasName("parseObject")
select call, parseExpression, call.getCallee(), call.getCaller()

image-20220607201214967

0x03-2 Call.getAnArgument

获取所有的参数
import semmle.code.java.frameworks.spring.SpringController
from Call call, Callable parseExpression
where
    call.getCallee() = parseExpression and
    parseExpression.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON") and
    parseExpression.hasName("parseObject")
select call.getAnArgument()

image-20220607201433001

0x03-3 Call.getArgument

getArgument(0) 获取第一个参数
getArgument(1) 获取第二个参数
import semmle.code.java.frameworks.spring.SpringController
from Call call, Callable parseExpression
where
    call.getCallee() = parseExpression and
    parseExpression.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON") and
    parseExpression.hasName("parseObject")
select call.getArgument(0)

image-20220607201546950

0x03-4 Call.getNumArgument

参数个数
import semmle.code.java.frameworks.spring.SpringController
from Call call, Callable parseExpression
where
    call.getCallee() = parseExpression and
    parseExpression.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON") and
    parseExpression.hasName("parseObject")
select call, call.getNumArgument()

image-20220607201711239

0x03-5 callable.getSignature()

获取参数类型
from Call call, Callable callable
where
    call.getCallee() = callable and 
    callable = getRelatedMethods("javax.imageio", "ImageIO", "read")
select call, callable.getSignature()

image-20220218151557072

0x04 数组

0x04-1 ArrayCreationExpr

匹配数组
import java

from ArrayCreationExpr ace 
select ace, ace.getFile(), ace.getLocation()

image-20220107152711004

image-20220107152736869

0x04-2 getAChildExpr

返回数组类型
import java

from ArrayCreationExpr ace 
select ace, ace.getAChildExpr(), ace.getFile(), ace.getLocation()

image-20220107152811970

0x04-3 getParent

返回赋值的变量
import java

from ArrayCreationExpr ace
where ace.getAChildExpr().getType().hasName("String")
select ace, ace.getParent()

image-20220107162520138

0x04-4 限定字符数组

import java

from ArrayCreationExpr ace 
where ace.getAChildExpr().getType().hasName("String")
select ace, ace.getAChildExpr(), ace.getFile(), ace.getLocation()

image-20220107153325644

0x04-5 ArrayInit

获取数组的一些值

由ArrayCreationExpr.getInit()返回

image-20220107155004342

0x04-6 getInit(index)

获取数组里下标为x的值
import java

from ArrayCreationExpr ace
where ace.getAChildExpr().getType().hasName("String")
select ace, ace.getInit().getInit(2)

image-20220107155034108

0x04-7 getSize()

获取数组的长度

image-20220107155139129

0x04-8 ArrayAccess

ArrayAccess 等价于 a[i++]

个人理解:Array是数组的意思,Access是访问的意思,合起来就是访问数组。那么就是得到数组里的值,所以通过下标来获取数组里的值。因此等价于a[i++]

image-20211230210948622

ArrayAccess.getIndexExpr() 等价于 i++

image-20220607203338965

UnaryAssignExpr 等价于 ++或者–

image-20211230211336130

import java

from ArrayAccess a
where a.getIndexExpr() instanceof UnaryAssignExpr
select a, a.getIndexExpr()

image-20211230211421777

ArrayAccess就是ids[i++]

image-20220607203509424

0x05 强制类型转换

0x05-1 castexpr

一个强制类型转换的表达式

image-20220103144051080

查找从浮点类型到整数类型的强制转换

import java

from CastExpr c
where
  c.getExpr().getType() instanceof FloatingPointType and
  c.getType() instanceof IntegralType
select c, c.getExpr(), c.getType(), c.getExpr().getType()

image-20220607204304755

0x06 实例化

0x06-1 ClassInstanceExpr

类实例化的表达式

image-20211230213106346

0x06-2 getConstructedType

构造函数的类型

image-20211230213257615

import java

from ClassInstanceExpr new
where new.getConstructedType().hasQualifiedName("com.jeecms.auth.domain.vo", "CoreUserAgent")
select new, new.getConstructedType()

image-20220607210054268

0x07 if语句

0x07-1 IfStmt

IfStmt –> if语句

getCondition –> if的判断语句

getThen –> 判断条件为true时执行的代码块

getElse –> 判断条件为false时执行的代码块

image-20220103145013178

import java

from IfStmt i
select i, i.getThen(), i.getCondition(), i.getTrueSuccessor(), i.getElse()

image-20220607210424667

predicate isMybatisAnnotationSqlInjection(DataFlow::Node sink){
    exists(MyBaTisSQLInjectAnnotationMethod m, MethodAccess ma, MyBatisSQLInjectAnnotation msa, string sqlStmt | 
        ma.getMethod() = m and 
        if msa.getAValue().getType() instanceof Array
        then 
            sqlStmt = msa.getAValue().getAChildExpr().toString()
        else 
            sqlStmt = msa.getAValue().toString()
        and
        myBatisSQLInjectValue(sqlStmt).matches("${%}") and 
        m.getAnAnnotation() = msa and 
        ma.getAnArgument() = sink.asExpr()
    )
}

0x08 等于或不等于表达式

0x08-1 EqualityTest

EqualityTest –> xxx == yyy 或者 xxx != yyy

getAnOperand –> ==或者!=两边的表达式

getLeftOperand –> 左边的表达式

getRightOperand –> 右边的表达式

image-20220103152806169

import java

from EqualityTest eq
// where eq.getAnOperand() instanceof BooleanLiteral
select eq, eq.getAnOperand(), eq.getLeftOperand(), eq.getRightOperand()

image-20220607210701729

0x09 继承类、接口类

0x09-1 RefType

适用于各种参考类型的通用父类,包括类、接口、类型参数和数组。

image-20211230215613394

getASupertype+ 获取到父类,并不断往上找父类

image-20211230215727537

import java

from RefType type
where type.getASupertype+().hasQualifiedName("org.springframework.web.method.support", "HandlerMethodArgumentResolver")
select type, type.getASupertype()

image-20220607212048143

0x09-2 匹配出指定类的所有子类

指定类为LoginSubmitController,是在包com.jeecms.auth.base里定义的

import java

bindingset[packageName, className]
RefType getRelatedClasses(string packageName, string className) {
  result.hasQualifiedName(packageName, className)
  or
  // 父类也符合指定的类
  result.getASupertype*().hasQualifiedName(packageName, className)
  or
  result.getASupertype*().hasQualifiedName(packageName, className + "<>")
}

from RefType r 
where r = getRelatedClasses("com.jeecms.auth.base", "LoginSubmitController")
select r

结果里把所有继承于LoginSubmitController的类都匹配出来了

image-20220607213116937

0x09-3 extendsOrImplements

获取接口的实现类

type2类实现了type类

from RefType type, RefType type2
where 
    type.hasName("BrandTuanActivityQueryService") and
    type2.extendsOrImplements(type)
select type2, type2.getASupertype()

image-20220607213640895

0x10 类变量

0x10-1 FieldRead

类变量被调用
import java

from Field f, FieldRead read
where
  f.hasName("cmsOrgService") and
  f.getDeclaringType().hasQualifiedName("com.jeecms.admin.controller.auth", "CmsOrgController") and
  f = read.getField()
select f, read, f.getName(), f.getDeclaringType(), f.getType()

image-20220607214407704

0x11 方法

0x11-1 MethodAccess 和 Method

MethodAccess 方法调用

image-20220607215030947

Method 方法的定义

image-20220607215107531

import java

from MethodAccess ma, Method method
where
  ma.getMethod() = method and
  method.hasName("findListByParentId") and
  method.getDeclaringType().hasQualifiedName("com.jeecms.system.service", "CmsOrgService")
select ma, method

image-20220607215241217

0x11-2 Method.isPublic() Method.isPrivate() Method.isProtected()等

限定匹配到的方法必须是公共函数、私有函数等

image-20220104112800181

import java

from Method m
where m.isProtected()
select m, m.getFile(), m.getLocation()

image-20220607215738149

0x11-3 getNumberOfParameters

方法的参数个数

Method.getNumberOfParameters 返回参数的个数

import java

from Method m
where 
    m.getNumberOfParameters() = 3
select m, m.getFile(), m.getLocation()

image-20220607220005886

0x11-4 getAParameter

方法的参数

Method.getAParameter 返回参数

import java

from Method m
where 
    m.getNumberOfParameters() = 3 and 
    m.hasName("updateName")
select m.getAParameter()

image-20220607220152940

0x11-5 overrides

覆盖
import java

from Method override, Method base
where
  base.hasName("save") and
  base.getDeclaringType().hasQualifiedName("com.jeecms.audit.service", "AuditStrategyService") and
  override.overrides+(base)
select override, base, override.getFile(), base.getFile()

AuditStrategyService接口定义了一个方法save

AuditStrategyServiceImpl实现类实现了save方法的具体内容

所以使用了Override注释符

所以codeql的代码里,override指的是AuditStrategyServiceImpl#save,base是AuditStrategyService#save

那么override.overrides+(base)就是说AuditStrategyServiceImpl#save覆盖了AuditStrategyService#save

image-20220608113557300

0x11-6 getAReference

方法被调用

Method.getAReference 匹配的方法被调用过

import java 

from Method m 
where 
    m.isPublic() and 
    exists(m.getAReference())
select m, m.getAReference()

例如existItem这个方法就被调用了

image-20220608114306467

0x11-7 getReturnType

方法的返回类型

getReturnType

import java

from Method m
where m.getName() = "existItem"
select m, m.getReturnType()

Method.getReturnType 返回方法的返回值类型

image-20220608151232820

0x11-8 MethodAccess.getQualifier()

返回方法调用的类名
import java

from MethodAccess ma, Method method
where
  ma.getMethod() = method and
  method.hasName("save") and
  method.getDeclaringType().hasQualifiedName("com.jeecms.audit.service", "AuditStrategyService")
select ma, method, ma.getQualifier()

image-20220608153105195

0x11-9 匹配add方法

import java

from MethodAccess ma, Method method
where
  ma.getMethod().overrides*(method) and
  method.hasName("add") and
  method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Collection")
select ma, ma.getMethod(), method,  method.getDeclaringType(), method.getDeclaringType().getSourceDeclaration(), method.getLocation()

匹配到的add方法是HashSet的内置方法。

这里要分析下为什么要这么写method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Collection")

其实看method.getDeclaringType()和method.getDeclaringType().getSourceDeclaration()的结果也能看出来

method.getDeclaringType()返回了很多Collection的类型,有set,有int,有map等等

这是因为这些都是接口,继承的是Collection类,所以method.getDeclaringType().getSourceDeclaration()的结果是Collection

image-20220608153328181

看jdk里面关于set、list等定义

set

image-20220608162816380

List

image-20220608162842012

0x11-10 匹配字符串的append方法

import java

from MethodAccess ma, Method method
where
  ma.getMethod().overrides*(method) and
  method.hasName("append") and
  method.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.lang", "StringBuilder") and
  ma.getAnArgument() instanceof StringLiteral
select ma, ma.getMethod(), method,  method.getDeclaringType(), method.getDeclaringType().getSourceDeclaration(), method.getLocation()

image-20220608163406885

0x12 ReturnStmt

0x12-1 返回语句

image-20220104154822437

import java

from ReturnStmt r
where r.getResult() instanceof StringLiteral
select r, r.getResult()

r 的值 return “bean.hasDeleted=true”;

r.getResult() 的值是返回值 “bean.hasDeleted=true”

image-20220608163529974

0x13 Switch语句

import java

from SwitchStmt switch, EnumType enum, EnumConstant missing
where
  switch.getExpr().getType() = enum and
  missing.getDeclaringType() = enum and
  not switch.getAConstCase().getValue() = missing.getAnAccess() and
  exists(switch.getDefaultCase())
select switch, enum, missing, switch.getExpr(), switch.getExpr().getType(), switch.getAConstCase(), missing.getAnAccess(), switch.getDefaultCase()

image-20220608165041111

0x13-1 SwitchStmt

switch语句

image-20220608165143529

0x13-2 EnumType

enum类型

image-20220608165208460

0x13-3 EnumConstant

enum的常量

image-20220608165908716

0x13-4 SwitchStmt.getExpr()

switch的判断语句

image-20220608165936847

0x13-5 switch.getAConstCase()

每个case

0x13-6 switch.getDefaultCase()

默认defalut

image-20220608170023753

0x14 三元运算符

0x14-1 ConditionalExpr

image-20220104164105458

import java

from ConditionalExpr e
select e, e.getTrueExpr(), e.getFalseExpr(), e.getCondition()

ConditionalExpr 是整个三元运算符语句

ConditionalExpr.getCondition 是判断语句

ConditionalExpr.getTrueExpr() 是为True时的语句

ConditionalExpr.getFalseExpr() 是为False时的语句

image-20220608170259641

0x15 抛出异常

import java

from ThrowStmt throw
select throw, throw.getThrownExceptionType()

ThrowStmt 抛出异常的语句 throw new GlobalException(RPCErrorCodeEnum.THIRD_PARTY_CALL_ERROR);

ThrowStmt.getThrownExceptionType() 异常的类型 GlobalException

image-20220608170416663

0x16 try语句

0x16-1 TryStmt

image-20220104194944375

import java

from TryStmt t
where
  exists(t.getFinally()) and
  exists(t.getACatchClause())
select t, t.getBlock(), t.getACatchClause(), t.getFinally()

TryStmt.getBlock 是 try语句的代码块

TryStmt.getACatchClause 是catch (Exception e)

TryStmt.getFinally 是 finally语句的代码块

image-20220608171136832

0x17 Parameter 参数

Parameter 参数

image-20220104202955759

0x17-1 getAnAccess

参数被调用
import java

from Parameter p
where exists(p.getAnAccess())
select p, p.getAnAccess()

image-20220608171826354

0x18 自实现匹配注解

AnnotationType 注解类型类,是继承接口类

image-20220105141409637

所以定义类SpringRequestMappingAnnotationType继承注解类型类AnnotationType

class SpringRequestMappingAnnotationType extends AnnotationType

@RequestMapping注解是在包org.springframework.web.bind.annotation里定义的接口RequestMapping

this.hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping")

image-20220608171954078

定义类SpringRequestMappingAnnotation继承注解类Annotation

private class SpringRequestMappingAnnotation extends Annotation

构造方法里,获取自身类型并且是SpringRequestMappingAnnotationType的实例

SpringRequestMappingAnnotation() { this.getType() instanceof SpringRequestMappingAnnotationType }

整体代码如下:

可以匹配到到@RequestMapping,@GetMapping,@PostMapping

import java

// 定义类SpringRequestMappingAnnotationType继承注解类AnnotationType
class SpringRequestMappingAnnotationType extends AnnotationType {
    // 构造方法
    SpringRequestMappingAnnotationType() {
      // `@RequestMapping` used directly as an annotation.
      // @RequestMapping注解是在包org.springframework.web.bind.annotation里定义的接口RequestMapping
      this.hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping")
      or
      // `@RequestMapping` can be used as a meta-annotation on other annotation types, e.g. GetMapping, PostMapping etc.
      this.getAnAnnotation().getType() instanceof SpringRequestMappingAnnotationType
    }
}

private class SpringRequestMappingAnnotation extends Annotation {
    SpringRequestMappingAnnotation() { this.getType() instanceof SpringRequestMappingAnnotationType }
}

from SpringRequestMappingAnnotation var1
select var1, var1.getFile(), var1.getLocation()

image-20220608172316143

image-20220608172408976

0x19 匹配类

0x19-1 匹配SAXParserFactory类

import java 

class SAXParserFactory extends RefType{
    SAXParserFactory(){
        this.hasQualifiedName("javax.xml.parsers", "SAXParserFactory")
    }
}

from SAXParserFactory var1
select var1, var1.getFile(), var1.getLocation()

image-20220608172456042

0x20 File文件

0x20-1 获取所有文件

import java 

from File file
select file, file.getBaseName()

image-20220116150545824

0x21 XML文件

0x21-1 XMLFile

获取xml文件

image-20220116150647280

0x22 XML元素

import java
from XMLElement var 
select var, var.getFile(), var.getLocation(), var.getAChild(), count(var.getAChild()), var.getFile().getAChild()

0x22-1 XMLElement.getAChild()

获取该元素下的子元素。

0x22-2 XMLElement.getFile().getAChild()

获取该元素所在文件最外层的元素。

image-20220116150958625

image-20220116150906335

image-20220116151057698

0x22-3 allCharactersString

获取元素里的值
import java 

/**
 * 匹配出xml文件里有mapper元素的文件。
 * 例如:<mapper namespace="org.joychou.mapper.UserMapper"> </mapper>
 */
class MyBatisMapperXMLFile extends XMLFile {
    MyBatisMapperXMLFile() {
      count(XMLElement e | e = this.getAChild()) = 1 and
      this.getAChild().getName() = "mapper"
    }
}


class MyBatisMapperXMLElement extends XMLElement{
  MyBatisMapperXMLElement(){
    this.getFile() instanceof MyBatisMapperXMLFile
  }
}

image-20220116160149908

0x23 Call和MethodAccess关联起来

0x23-1 通过getQualifier对应

call.getQualifier() = ma.getQualifier()

0x24 Node、PartialPathNode和m、ma关联起来

DataFlow::Node node
node.asParamter() = m.getAParameter()
node.asExpr() = ma.getAnArgument()


DataFlow::PartialPathNode node
node.getNode().asParamter() = m.getAParameter()
node.getNode().asExpr() = ma.getAnArgument()

   转载规则


《CodeQL系列之基础语法》 ske 采用 知识共享署名 4.0 国际许可协议 进行许可。