codeql常用类型及函数积累(进阶)| 相关自写案例

目录

  • 前言
  • 一、函数
    • method.hasName("query")
    • method.getDeclaringType()
    • methodAccess.getQualifier
    • class.hasQualifiedName("com.mytest", "User")
  • 二、类型
    • ParamTag
    • MethodAccess
    • PrimitiveType
    • RefType
    • TopLevelType
    • NestedType
    • Call
  • 例子1
  • 例子2(spring提取注解参数名)
  • 例子3 (某堡垒机练手)

前言

写着写着,感觉这篇文章意义不是很大,更加推荐去看官方文档,例子和习题:
https://codeql.github.com/docs/codeql-language-guides/analyzing-data-flow-in-java/#examples
类型学习:
https://codeql.github.com/docs/codeql-language-guides/codeql-library-for-java/#types
各种类型漏洞使用codeql查询官方案例:
https://codeql.github.com/codeql-query-help/java/


本篇继续积累一些自己觉得重点的codeql用法和例子

一、函数

method.hasName(“query”)

用于判断一个方法的名称

method.hasName("query")

method.getDeclaringType()

表示获取一个方法所在的类或接口

import java
 
predicate isStudent(Method method) {
exists(|method.hasName("getStudent"))
}
 
from Method method
where isStudent(method)
select method.getName(), method.getDeclaringType()

methodAccess.getQualifier

获取被调用方法,例如a.b(),那么methodAccess.getQualifier()获取的是a对象

methodAccess.getQualifier().getType() instanceof TypeString

class.hasQualifiedName(“com.mytest”, “User”)

检查是否是一个具有完全限定名称 com.mytest.User 的类。

二、类型

部分类型可翻阅如下文档:
https://codeql.github.com/docs/codeql-language-guides/codeql-library-for-java/#types

ParamTag

获取注释中的标签,例如param

import java

from Callable c, ParamTag pt
where c.getDoc().getJavadoc() = pt.getParent()
select c, pt

MethodAccess

表示被调用的方法,例如:a.b(),那么这个b()就是被调用的方法

import java

from Method method,MethodAccess call
where
call.getMethod() = method and
//.getCompilationUnit().fromSource()该条件指定了查询结果中的函数或方法必须来自源代码文件,而非库文件等其他来源。
method.getCompilationUnit().fromSource()
select method,call

PrimitiveType

PrimitiveType表示原始类型,PrimitiveType represents a primitive type, that is, one of boolean, byte, char, double, float, int, long, short; QL also classifies void and (the type of the null literal) as primitive types.

RefType

表示引用(其他对象),即非原始类型,例如:

Student stu //非原始类型
String str //原始类型

TopLevelType

TopLevelType表示在编译单元顶层声明的引用类型。

NestedType

NestedType是在另一个类型中声明的类型。

Call

指代被传递进入的参数,例如a(test),a即为call

例子1

查找new URL(param)这种形式的代码

import java
import semmle.code.java.dataflow.DataFlow

from Constructor fileReader, Call call, Expr src
where
  
  call.getCallee() = fileReader and
  DataFlow::localFlow(DataFlow::exprNode(src), DataFlow::exprNode(call.getArgument(0))) and
  fileReader.getDeclaringType().hasQualifiedName("java.net","URL")
select call.getCallee(),call,src,fileReader

查找new URL(param)中的param参数

import java
import semmle.code.java.dataflow.DataFlow

from Constructor fileReader, Call call, Parameter p
where
  fileReader.getDeclaringType().hasQualifiedName("java.net", "URL") and
  call.getCallee() = fileReader and
  DataFlow::localFlow(DataFlow::parameterNode(p), DataFlow::exprNode(call.getArgument(0)))
select p,call.getArgument(0)

例子2(spring提取注解参数名)

参考连接:https://tttang.com/archive/1512/

有如下例子:

(@RequestParam(required=true,value="id") String id)

1、getAnAnnotation获取注解
2、Annotation注解
3、hasQualifiedName
4、Expr表达式.getValue(“value”)例如:,则getValue(“value”)的值就是id

string getRequestParam(Method m){
    exists(Parameter p, Expr e, Annotation a | 
        p = m.getAParameter()
        and a = p.getAnAnnotation()
        and a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "RequestParam")
        and e = a.getValue("value")
        and e.getParent().toString() = "RequestParam"
        and e.toString() != "\"\""
        and result = e.toString()
    )
}

例子3 (某堡垒机练手)

这是自己首次尝试构造出的第一个查询,构造过程中学到了很多:
在我参考:https://tttang.com/archive/1512/文章尝试定位注解的名称为RequestParam时,发现返回值居然为空(这可能是我构建数据库代码不全导致)

Annotation a
a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "RequestParam")

解决办法:
通过getAQlClass来获取注解的类,判断其类名为:MetricElement
Annotation:注解

import java


from Parameter p, Annotation a
where
  a = p.getAnAnnotation() and
  a.getAQlClass().toString() = "MetricElement" 
select a

我主要想实现的功能是查找调用了sendMessageWithResponse的上级方法,全局数据流如下,但由于我分析的是闭源代码,效果不是很理想:

/**
 * @name Unsafe shiro deserialization
 * @kind problem
 * @id java/unsafe-deserialization
 */
import java
import semmle.code.java.dataflow.DataFlow

// TODO add previous class and predicate definitions here

class ShiroUnsafeDeserializationConfig extends DataFlow::Configuration {
  ShiroUnsafeDeserializationConfig() { this = "ShiroUnsafeDeserializationConfig" }
  override predicate isSource(DataFlow::Node source) {
    exists(Annotation a,Parameter p |
        p.getAQlClass().toString().matches("MetricElement") and
        a = p.getAnAnnotation() and
        p.getCallable().hasModifier("public")//这里的p.getCallable()就是method
    )
  }
  


  
  predicate isDes(Expr arg){
    exists(MethodAccess des |
    des.getMethod().hasName("sendMessageWithResponse") 
    and
    arg = des.getArgument(0)
    )
}
  override predicate isSink(DataFlow::Node sink) {
    exists(Expr arg|isDes(arg))
  }
}

from ShiroUnsafeDeserializationConfig config, DataFlow::Node source, DataFlow::Node sink
where config.hasFlow(source, sink)
select source,sink

本地数据流查询实现如下(不完善):

/**
 * @name Find public callers of sendMessageWithResponse and trace their callers
 * @description Recursively finds public methods that call sendMessageWithResponse and traces their callers.
 * @language java
 */

 import java

 class SendMessageWithResponse extends MethodAccess {
   SendMessageWithResponse() {
     this.getMethod().hasName("sendMessageWithResponse")
   }
 }
 
 class PublicCallerOfSendMessage extends Method {
   PublicCallerOfSendMessage() {
     this.getDeclaringType().isPublic() and
     this.fromSource() and
     exists(SendMessageWithResponse m |
       this.calls(m.getMethod())
     )
   }
 }
 
 predicate methodRecursivelyCalls(Method caller, Method callee) {
   caller = callee or
   exists(Method intermediate |
     caller.calls(intermediate) and
     methodRecursivelyCalls(intermediate, callee)
   )
 }
 
 class TopLevelCaller extends Method {
   TopLevelCaller() {
     this.getDeclaringType().isPublic() and
     this.fromSource() and
     exists(Method m |
       methodRecursivelyCalls(this, m) and
       m instanceof PublicCallerOfSendMessage
     )
   }
 }
 
 from TopLevelCaller topLevelCaller
 select topLevelCaller.getDeclaringType(), topLevelCaller
 

你可能感兴趣的:(codeql)