codeql 学习笔记1

admin 2022年3月25日23:26:09评论255 views字数 9735阅读32分27秒阅读模式

首先话多两句 学习一门语言看文档是最烦的事情没有之一,但是不看文档又不能很全面的覆盖方方面面,说一下我的思路吧。啃文档恼火,但是需要结合文档的结构去确定学习的方向。对于一些生涩难懂的地方可以合理利用google去单个搜索。然后能看懂别人写的codeql查询语句后,结合别人的思路,进行学习。同时自己也去拓展不同的地方,结合起来,应该会轻松一点。有些地方具体的还未涉及,后期还会补发。

下载 codeql-cli:

https://github.com/github/codeql-cli-binaries/releases 

ql库:https://github.com/github/codeql/releases 

安装插件 VSCode

codeql 学习笔记1codeql 学习笔记1

使用

(1)生成审计数据库 database create test --language="java" --command="mvn clean install --file pom.xml" --source-root=../seccode (2)选择合适的工作区,这里我直接引用导入的库 

(3)编写合适的QL查询语句,位于qljavaqlexamples,文件命名为*.ql 

(4)执行简单查询并查看结果


codeql 学习笔记1codeql 学习笔记1

codeql 学习笔记1

查询多个数据库
查看AST语法树

codeql 学习笔记1

codeql 学习笔记1

(1)基本语法

查询import javafrom Class Bwhere  B.getName() in["Index","Login"]select B
from 声明变量where限制条件select 输出结果
Filed 字段Method 方法Class 类名Package 包名

(2)函数(谓词)

谓词分类1)非成员谓词 Non-member predicate(定义在class之外的谓词)2)成员谓词 定义在class之内的谓词3)特征谓词 可以理解为类构造方法
对上面的查询更改关键词 predicate 首字母必须小写1)这是没有类型的函数 还能利用 int,float,string等类型定义函数predicate getAllClass(Class b) { b.getName() in["Index","Login"]}
from Class Bwhere getAllClass(B)select B
2)有返回值的谓词保存在result中import javaClass add(Class i ){ i.getName() ="XStreamHandler" and result =i}
from Class Bselect add(B) 3)当我们需要直接执行一个谓词的时候 ,可以添加一个queryquery Class add(Class i ){ i.getName() ="XStreamHandler" and result =i}

(3)类

要求1)首字母大写2)必须继承一个类3)关键字为class4this代表父类而不是本身5)与类名相同的叫特征谓词6)重载父类方法增加override注解
import java
class Main extends Method { Main() { this.getName()="main" } }from classGet clsselect cls
类类型1)实体类 正常定义的类2)抽象类 abstract修饰的类

codeql 学习笔记1

(4)方法

Method(查找存在某个方法)

parameter 函数定义变量arguement 函数传参变量
获取调用参数的数量getNumberOfParameters()获取调用的制定位置参数类型method.getParameterType(0)获取超类包含子类的类型包括本身getAnAncestor ()获取超类型不包括本身getASupertype()判断存在方法名method.hasName("toObject")获取方法的类名类型method.getDeclaringType()查询某个类的某个方法method.hasQualifiedName(string package, string type)获取是否存在某个危险方法from Method methodwhere method.hasName("fromXML") and method.getDeclaringType().hasQualifiedName("com.thoughtworks.xstream", "XStream")select method,method.getDeclaringType()/*查询一个方法名为fromXML,并且方法的类为XStream*/查询所有包括子类的xx方法method.hasName("toObject") and method.getDeclaringType().getAnAncestor().hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")(包括他本身)method.hasName("toObject") and method.getDeclaringType().getASupertype().hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler")(不包括他本身)

MethodAccess(查找调用了某个方法)

查找调用了方法的方法call.getMethod() = method(只能显示定义)查找调用了ContentTypeHandler的toObject方法的方法(包含子类)where method.hasName("toObject") and method.getDeclaringType().getAnAncestor().hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler") and call.getMethod() = method

类型

原始数据类型  PrimitiveType boolean, byte, char, double, float, int, long, short 非原始类型  RefType 基本类型  TypeString,TypeObject、TypeCloneable、TypeRuntime、TypeSerializable类类型 Class实例  from Variable v , TypeString tswhere ts=v.getType() and ts.hasName("String")select v

适用于java的codeql

三大核心模块
DataFlow模块 数据流处理模块
Smtm模块 AST语法树模块
Expr模块 节点相关

导航调用图

callable 被调用的函数call   调用callable实例1from Call ca ,Callable cbwhere ca.getCallee() =cb and cb.getDeclaringType().hasQualifiedName("org.springframework.expression", "ExpressionParser")and cb.hasName("parseExpression")select ca解释:寻找调用了org.springframework.expression.ExpressionParser#parseExpression的地方from Call ca ,Callable cbwhere ca.getCallee() =cb and cb.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON")and cb.hasName("parse")select ca解释:寻找调用了com.alibaba.fastjson.JSON的地方谓词ca.getArgument(0) 调用的第一个参数

数据流分析

DataFlow ExprNode :表达式节点 

ParameterNode:参数节点 

全局数据流需要继承 

DataFlow::Configuration,然后重写isSource isSink方法 

本地数据流 

DataFlow::localFlow(DataFlow::parameterNode(source), DataFlow::exprNode(sink)) 

全局污点追踪,需要继承 TaintTracking::Configuration 

本地污点跟踪 TaintTracking::localTaint(DataFlow::parameterNode(source), DataFlow::exprNode(sink)) 

可以把他理解成一个污点传播的过程,parameterNode为污点,exprNode为汇聚点 

区别

本地数据流是直接的进行带入,如果是底层方法实现则不会寻找 全局数据流可以全局带入,底层也能发现,详情可以见案例SSRF的实现 

净化方法(就是依据条件打断当前的污点追踪),详情见案例3 

isSanitizer 

强制跟踪 

isAdditionalTaintStep 

就是当断点打断时候,利用此方法进行强制污点跟踪,可以解决未在codeql库里面导致跟踪失败的问题,详情见案例3

codeql 学习笔记1

案例1 java-sec-code

实例代码来自java-sec-code本地数据流(下图1)
from Call ca ,Callable cb,SpringRequestMappingMethod route//引入需要的模块where ca.getCallee() =cb and cb.getDeclaringType().hasQualifiedName("com.alibaba.fastjson", "JSON")and cb.hasName("parse") and TaintTracking::localTaint(DataFlow::parameterNode(route.getARequestParameter()),DataFlow::exprNode(ca.getArgument(0)))//寻找当调用的函数为com.alibaba.fastjson.JSON#parse的位置,并且对Spring传入的request的参数进行污点跟踪,跟踪的参数节点为传入参数,跟踪的表达式节点为我们传入的fastjson的恶意触发方法啊select ca ,route.getARequestParameter() 查找结果,传入的参数
全局数据流查询ssrf这里没有采用openConnection()因为它不带参数,所以使用NEW URL来替代import javaimport semmle.code.java.frameworks.spring.SpringControllerimport semmle.code.java.dataflow.TaintTracking
class Config extends DataFlow::Configuration{ Config(){ this = "Config" } override predicate isSource(DataFlow::Node source){ exists(SpringRequestMappingMethod SRMM | source.asParameter() =SRMM.getARequestParameter() ) } override predicate isSink(DataFlow::Node sink) { exists( Call ca,Callable cb|sink.asExpr() =ca.getArgument(0) and ca.getCallee()=cb and cb.getDeclaringType().hasQualifiedName("java.net", "URL") and cb.hasName("URL")) }}
from DataFlow::Node sour ,DataFlow::Node sink,Config configwhere config.hasFlow(sour, sink)select sour,sink

codeql 学习笔记1

案例2 数据流分析CVE-2017-9805

https://blog.sometimenaive.com/2020/05/21/find-fastjson-jndi-gadget-by-codeql-tainttracking/https://github.com/githubsatelliteworkshops/codeql/blob/master/java.mdsource:toObject(in)sink:com.thoughtworks.xstream.fromXML根据分析,我们需要寻找一个污点为toObject的第一个参数为Reader,汇聚点为xstream.fromXML的一条污点传播路径代码如下import javaimport semmle.code.java.dataflow.DataFlow
class StrutsSafe extends DataFlow::Configuration { StrutsSafe() { this = "StrutsSafe" } override predicate isSource(DataFlow::Node source) { exists(Method me | me.hasName("toObject") and me.getDeclaringType().getAnAncestor().hasQualifiedName("org.apache.struts2.rest.handler", "ContentTypeHandler") and me.getParameter(0) = source.asParameter()) } override predicate isSink(DataFlow::Node sink) { exists(Call call, Callable method | method.hasName("fromXML") and method.getDeclaringType().hasQualifiedName("com.thoughtworks.xstream", "XStream") and call.getCallee() = method and sink.asExpr() = call.getArgument(0)) }}
from StrutsSafe stu, DataFlow::Node source, DataFlow::Node sink
where stu.hasFlow(source, sink)
select source,sink

codeql 学习笔记1

案例三

https://www.freebuf.com/articles/web/283795.html1)分析sql注入,我们发现最终是利用jdbcTemplate.query()也就是query()方法触发,所以我们可以定义query()为汇聚点,污点为我们传入的参数。import javaimport semmle.code.java.dataflow.FlowSources
class SqlConfig extends TaintTracking::Configuration { SqlConfig() { this = "SqlConfig" } override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { exists(Method me ,MethodAccess ma | me.hasName("query") and ma.getCallee() = me and sink.asExpr() = ma.getArgument(0) ) }}
from SqlConfig scg ,DataFlow::Node source, DataFlow::Node sinkwhere scg.hasFlow(source, sink)select source.toString(),source,sink

codeql 学习笔记1

为了消除上面关于数值类型的注入误报,我们加入isSanitizer来进行污点误报解决,判断当污点为数字类型就断掉import javaimport semmle.code.java.dataflow.FlowSources
class SqlConfig extends TaintTracking::Configuration { SqlConfig() { this = "SqlConfig" } override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { exists(Method me ,MethodAccess ma | me.hasName("query") and ma.getCallee() = me and sink.asExpr() = ma.getArgument(0) ) } override predicate isSanitizer(DataFlow::Node node) { node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType or node.getType() instanceof NumberType or exists(ParameterizedType pt| node.getType() = pt and pt.getTypeArgument(0) instanceof NumberType ) }}
from SqlConfig scg ,DataFlow::Node source, DataFlow::Node sinkwhere scg.hasFlow(source, sink)select source.toString(),source,sink
为了消除不可识别类型对codeql分析的影响,我们加入isAdditionalTaintStepimport javaimport semmle.code.java.dataflow.FlowSources
class SqlConfig extends TaintTracking::Configuration { SqlConfig() { this = "SqlConfig" } predicate isTaintedString(Expr expSrc, Expr expDest) { exists(Method m ,MethodAccess ma1,MethodAccess ma2 | expSrc = ma1.getArgument(0) and expDest = ma2 and ma2.getMethod() =m and m.hasName("get")) } override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { exists(Method me ,MethodAccess ma | me.hasName("query") and ma.getCallee() = me and sink.asExpr() = ma.getArgument(0) ) } override predicate isSanitizer(DataFlow::Node node) { node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType or node.getType() instanceof NumberType or exists(ParameterizedType pt| node.getType() = pt and pt.getTypeArgument(0) instanceof NumberType ) } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { isTaintedString(node1.asExpr(), node2.asExpr()) }}
from SqlConfig scg ,DataFlow::Node source, DataFlow::Node sinkwhere scg.hasFlow(source, sink)select source.toString(),source,sink
/**这个isTaintedString意思就说我们对get表达式节点进行了一个结合,但是由于没有做类型限制,所以会查出来比较多的数据,我们精确一下将参数和方法的类型做一个精确**/import javaimport semmle.code.java.dataflow.FlowSources
class SqlConfig extends TaintTracking::Configuration { SqlConfig() { this = "SqlConfig" } predicate isTaintedString(Expr expSrc, Expr expDest) { exists(Method m ,MethodAccess ma1,MethodAccess ma2 | expSrc = ma1.getArgument(0) and expDest = ma2 and ma2.getMethod() =m and m.hasName("get") and m.getDeclaringType().toString()="Optional<String>" and ma1.getAnArgument().getType().toString()="Optional<String>") } override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { exists(Method me ,MethodAccess ma | me.hasName("query") and ma.getCallee() = me and sink.asExpr() = ma.getArgument(0) ) } override predicate isSanitizer(DataFlow::Node node) { node.getType() instanceof PrimitiveType or node.getType() instanceof BoxedType or node.getType() instanceof NumberType or exists(ParameterizedType pt| node.getType() = pt and pt.getTypeArgument(0) instanceof NumberType ) } override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { isTaintedString(node1.asExpr(), node2.asExpr()) }}
from SqlConfig scg ,DataFlow::Node source, DataFlow::Node sinkwhere scg.hasFlow(source, sink)select source.toString(),source,sink

codeql 学习笔记1


原文始发于微信公众号(e0m安全屋):codeql 学习笔记1

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年3月25日23:26:09
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   codeql 学习笔记1https://cn-sec.com/archives/842139.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息