CodeQL挖掘fastjson漏洞
fastjson反序列化
这个漏洞原理这里就不过多BB了,简单解释:
fastjson反序列化的触发点在 parse 或者 parseObject 方法,该方法一个参数会接受一个json字符串,然后将其反序列化为一个java对象并调用其 getXXX 或 setXXX 方法。攻击者可以精心构造一个json字符串,触发危险的 getXXX 或 setXXX 方法,导致产生危害严重的漏洞
参考:https://tttang.com/archive/1579/
Source
按照Spring框架,这个Source一般定义为各种Controller的参数或者request.getParameter
等
可能还有其他,具体问题具体分析
代码如下:
class AllControllerMethod extends Method{
AllControllerMethod(){
exists(RefType rt |
rt.getName().indexOf("Controller")>0 and
this = rt.getACallable() )
}
}
override predicate isSource(DataFlow::Node source) {
source instanceof RemoteFlowSource or
exists(Method method |
method instanceof AllControllerMethod and
source.asParameter() = method.getAParameter()
)
}
AllControllerMethod
谓词是获取所有Controller中的方法,通过简单的判断类名是否存在Controller
来确定。这个方式有点不妥,可能会找到些非Controller的类,造成误报。
然后是定义Source
,source instanceof RemoteFlowSource
这里RemoteFlowSource
类已经定义了很多常见的source点,可以满足我们做一般性代码审计的需要,例如上面提到的request.getParameter
。除了这个还有Controller下方法的参数。
Sink
这里设置的是漏洞触发点,即parse
或者parseObject
方法,最好加上父类判断,减少误报
写个谓词ParseOrParseObjectMethod
来获取这两个方法,使用递归查找父类,防止遗漏
class ParseOrParseObjectMethod extends Method{
ParseOrParseObjectMethod(){
(this.hasName("parse") or this.hasName("parseObject")) and
(this.getDeclaringType().getASupertype*
().hasQualifiedName("com.alibaba.fastjson","JSONObject") or
this.getDeclaringType().getASupertype*
().hasQualifiedName("com.alibaba.fastjson","JSON"))
}
}
设置sink,将parse
或者parseObject
方法的第一个参数作为sink
override predicate isSink(DataFlow::Node sink) {
exists(MethodCall call |
call.getMethod() instanceof ParseOrParseObjectMethod and
sink.asExpr() = call.getArgument(0)
)
}
完整代码
/**
* @kind path-problem
*/
import java
import DataFlow2::PathGraph
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking2
class AllControllerMethod extends Method{
AllControllerMethod(){
exists(RefType rt |
rt.getName().indexOf("Controller")>0 and
this = rt.getACallable() )
}
}
class ParseOrParseObjectMethod extends Method{
ParseOrParseObjectMethod(){
(this.hasName("parse") or this.hasName("parseObject")) and
(this.getDeclaringType().getASupertype*
().hasQualifiedName("com.alibaba.fastjson","JSONObject") or
this.getDeclaringType().getASupertype*
().hasQualifiedName("com.alibaba.fastjson","JSON"))
}
}
class MyTaintTrackingConfiguration extends TaintTracking2::Configuration {
MyTaintTrackingConfiguration() {
this = "MyTaintTrackingConfiguration"
}
//source为Controller中方法的任意参数
override predicate isSource(DataFlow::Node source) {
source instanceof RemoteFlowSource or
exists(Method method |
method instanceof AllControllerMethod and
source.asParameter() = method.getAParameter()
)
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodCall call |
call.getMethod() instanceof ParseOrParseObjectMethod 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, "fastJsonInject!"
在华夏ERP3.1的查询结果:
踩坑记录
这个坑踩在元数据的使用中
当元数据为 @kind path**-**problem 时:查询模板为 select element, source, sink, string
开始我只是
select source, sink
然后查询报错,有结果,但是不能像上图一样查看每个节点的位置
第二个坑在import DataFlow2::PathGraph
,这个也是为了查看每个节点的位置,
开始使用的是import DataFlow::PathGraph
,没有报错,但是不能查看每个节点的位置。这个可能和全局污点分析中的TaintTracking2
版本有关