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的类,造成误报。

然后是定义Sourcesource 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的查询结果:

image-20240125103830296

踩坑记录

这个坑踩在元数据的使用中

当元数据为 @kind path**-**problem 时:查询模板为 select element, source, sink, string

开始我只是

select  source, sink

然后查询报错,有结果,但是不能像上图一样查看每个节点的位置

第二个坑在import DataFlow2::PathGraph,这个也是为了查看每个节点的位置,

开始使用的是import DataFlow::PathGraph,没有报错,但是不能查看每个节点的位置。这个可能和全局污点分析中的TaintTracking2版本有关