使用codeql自动挖掘Java反序列化gadget

白白白 2022-04-02 09:59:00

0x00 背景

前两天看到一篇老外写的用codeql挖掘Java反序列化gadget的文章 https://www.synacktiv.com/en/publications/finding-gadgets-like-its-2022.html,虽然显示的效果还可以,但用了种比较土味的半自动化半人工的方式。直觉上感觉codeql用法应该不会这么呆,于是研究了下,有了这篇文章。

0x01 分析

翻了翻网上的帖子,发现好像除了上面那篇也没有什么挖掘Java原生反序列化的利用链的文章。那么先分析下挖掘原生反序列化利用链的条件是什么:
1. source,入口点,一般就是一个readObject方法
2. sink,执行点,一般是动态方法执行、Jndi注入、写文件之类的
3. gadget,连接source和sink的多个类。有几个条件:
1. 类之间的方法调用是链式的。
2. 类实例之间的关系是嵌套的,调用链上后一个类实例是前一个类实例的属性。
3. 调用链上的类都需要是可以序列化的。

所以发现原生反序列化的污点不好定义,每个类的每个属性都可能是污点。自己水平也不够,索性就放弃了污点分析的方式,直接从函数调用关系入手,误报其实没有想象的那么多。

0x02 实现

首先找source,这里只定义了readObject。对codeql不太熟,没找到系统内置的readObject,自己定义了个

class ROMethod extends Method{
    ROMethod(){
        this.hasName("readObject")
        and this.isPrivate()
        and this.getReturnType() instanceof VoidType
    }
}

class Source extends Callable {
    Source(){
        getDeclaringType().getASupertype*() instanceof TypeSerializable and (
            this instanceof ROMethod
        )
    }
}

然后是sink,就定义了一个Method.invoke,可以自行加别的执行点。这里定义了一个CallsDangerousMethod,是因为后面分析调用关系时候的限制条件是存在调用关系的两个类都需要是可序列化的,但sink点其实是不需要可序列化的。

class InvokeMethod extends Method {
    InvokeMethod(){
        this.hasName("invoke") and
        this.getDeclaringType().hasQualifiedName("java.lang.reflect","Method")
    }
}

class DangerousMethod extends Callable {
    DangerousMethod(){
        this instanceof InvokeMethod 
      }
  }

class CallsDangerousMethod extends Callable {

    CallsDangerousMethod() {
        exists(Callable a| this.polyCalls(a) and 
        a instanceof DangerousMethod )
    }  
}  

最后是函数调用,在codeql里就是a.polyCall(b),代表a方法里调用了b方法。原生反序列化的话,两个类都需要是可以序列化的,或者调用的是静态方法(其实还有可能是链式调用的静态方法,这么写就会漏报)。查询时用edges解决了土味自动化的问题。

query predicate edges(Method a, Method b) { 
    a.polyCalls(b) and
    (a.getDeclaringType().getASupertype*() instanceof TypeSerializable or a.isStatic()) and
    (b.getDeclaringType().getASupertype*() instanceof TypeSerializable or b.isStatic()) 
}

最终一个简易的完整实现是这样:

/**
@kind path-problem
*/

import java
import semmle.code.java.dataflow.FlowSources

class ROMethod extends Method{
    ROMethod(){
        this.hasName("readObject")
        and this.isPrivate()
        and this.getReturnType() instanceof VoidType
    }
}

class Source extends Callable {
    Source(){
        getDeclaringType().getASupertype*() instanceof TypeSerializable and (
            this instanceof ROMethod
        )
    }
}

class InvokeMethod extends Method {
    InvokeMethod(){
        this.hasName("invoke") and
        this.getDeclaringType().hasQualifiedName("java.lang.reflect","Method")
    }
}

class DangerousMethod extends Callable {
    DangerousMethod(){
        this instanceof InvokeMethod 
      }
  }

class CallsDangerousMethod extends Callable {

    CallsDangerousMethod() {
        exists(Callable a| this.polyCalls(a) and 
        a instanceof DangerousMethod )
    }  
}  


query predicate edges(Method a, Method b) { 
    a.polyCalls(b) and
    (a.getDeclaringType().getASupertype*() instanceof TypeSerializable or a.isStatic()) and
    (b.getDeclaringType().getASupertype*() instanceof TypeSerializable or b.isStatic()) 
}

from Source source, CallsDangerousMethod sink
where edges+(source, sink)
select source, source, sink, "$@ $@ to $@ $@" ,
source.getDeclaringType(),source.getDeclaringType().getName(),
source,source.getName(),
sink.getDeclaringType(),sink.getDeclaringType().getName(),
sink,sink.getName() 

0x03 结果

导入的数据库是cc3,尝试找一条不依赖JDK的完整反序列化利用链,没啥意义,就是试试好不好使。
codeql.png
大概分析下发现Flat3Map#readObject下面的几条链是可用的,误报也还算可以接受,可能因为cc代码量比较小。
其实大部分已知的链都是从JDK内部类开始的,但codeql没有办法同时跑两个数据库,折中的解决方法就是找半条链,比如把hashCode、equals之类的当作source,自己补充就好了,能跑出一大堆。
在yso里面已知的库上跑了跑,发现误报主要集中在entry这种transient变量,漏报主要是一些不可序列化类的静态方法。

0x04 总结

p34066865.jpg

评论

Y

yuan 2022-04-13 19:18:46

请问这个edges+()递归是怎么理解的呢

白白白 2022-04-14 19:34:49

就是从source一直链式调用到sink

T

t33t 2022-04-15 10:59:59

请问InvokeMethod要怎么改才能查出org.jboss.as.connector.subsystems.datasources.WildFlyDataSource这条链?改成this.hasName("lookup") and this.getDeclaringType().hasQualifiedName("javax.naming","InitialContext")好像不行

白白白 2022-04-18 14:11:45

用edges查不到是因为WildFlyDataSource是因为那个链太短了,直接quick evaluation下CallsDangerousMethod就找到了。

T

t33t 2022-05-02 17:06:51

师傅你好,图中Flat3Map#readObject的第一条链的利用能提供一下吗?后半部分调不出来。
HashMap<Object, Object> map = new HashMap<>();
Map<Object,Object> defaultedMap = DefaultedMap.decorate(map,chainedTransformer);
FastHashMap fastHashMap = new FastHashMap(defaultedMap);
fastHashMap.put("key","111");
fastHashMap.put(defaultedMap,"222");
Flat3Map flat3Map = new Flat3Map(fastHashMap);
// flat3Map.put("key","333");
flat3Map.put(fastHashMap,"444");

白白白

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

事件分析 文章:223 篇
前端安全 文章:29 篇
无线安全 文章:27 篇
逻辑漏洞 文章:15 篇
Python安全 文章:13 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Yukong

🐮皮

H

HHHeey

好的,谢谢师傅的解答

Article_kelp

a类中的变量secret_class_var = "secret"是在merge

H

HHHeey

secret_var = 1 def test(): pass

H

hgsmonkey

tql!!!

目录