AspectJWeaver反序列化分析
上周末参加了TSCTF
,有两道Java
题目,一道是RMI
的,lookup
的时候用hook
传Interface
自带的恶意类参数,然后使用方法之前hook
住Server
端的ip
,将0.0.0.0
改成正确ip
,即可使用。其中由于传的shell
是固定格式,而且使用的是dash
,构造比较恶心,就不在这讨论了。
另一道是java
反序列化的题目。代码很简单,解读起来就是post
一个String
,然后使用base64
解密,在通过serialkiller
过滤,最后反序列化。
那么思路也很清晰,就是构造可以通过过滤的payload
,然后讲序列化的字节数组进行base64
加密,得到payload
。但是serialkiller
过滤了CC1-CC6
常见的方法。
赛后看到参加MRCTF
师傅的wp
说可以使用AspectJWeaver
,然后用org.apache.commons.collections.functors.MapTransformer
替代ConstantTransformer
就可以了。比赛时候其实我也找了AspectJWeaver
反序列化利用的poc
,但是看里面有ConstantTransformer
就想着不可行,比赛的时候一直想找一个可以直接用的poc
,还是太年轻、太菜了。
所以今天先来学习一下AspectJWeaver
什么是AspectJWeaver
AspectJ
是 Eclipse
基金组织的开源项目,它是 Java
语言的一个 AOP
实现,是最早、功能比较强大的 AOP
实现之一,对整套 AOP
机制都有较好的实现,很多其他语言的 AOP
实现也借鉴或者采纳了 AspectJ
中的很多设计。在 Java
领域,AspectJ
中的很多语法结构基本上已经成为 AOP
领域的标准。本条链是一条任意文件写入的利用链,可以将内存马写入进去,这也避免了不出网的环境下的窘境。
前置知识
SimpleCache$StoreableCachingMap
在 org.aspectj.weaver.tools.cache.SimpleCache
类中定义了一个内部类 StoreableCachingMap
,这个类继承了 HashMap
,提供了将 Map
中值写入文件中的功能。
在调用 put
方法向 StoreableCachingMap
中放入值时,会调用 writeToPath
方法将 value
中的值写入到文件中。
可以看到,路径由 folder
起始,拼接 File.separator
以及 key
,values
是 byte
数组类型的数据。
由此,可构建:实例化一个StoreableCachingMap
,传入参数folder
和storingTimer
,folder
作为路径,然后某方法调用StoreableCachingMap
的put
,传入两个Object
,其中key
是文件名,value
是文件内容。这就构成了一个文件写出的 sink
点。
接下来就看谁调用了put
方法。
LazyMap
在 CC
链中的 org.apache.commons.collections.map.LazyMap
类,我们通过触发其 get
方法来触发 Transformer
的 transform
方法来触发 ChainedTransformer
/InvokerTransformer
等后续调用链。
实际上 LazyMap
在 transform
之后会调用封装的内部 map
的 put
方法将结果保存,这就触发了 put
方法。
POC构造
ConstantTransformer
ysoserial
此链中使用了 HashSet
反序列化来触发 TiedMapEntry
的 hashCode
进一步触发 LazyMap
的 get
方法,在 CC6
/CC7
中使用 HashSet
/HashMap
/Hashtable
触发本质上都是一样的。这里使用 HashSet
来触发。
1 | public class AspectJWeaver { |
注意:以get
形式传参时要Base64
后的字符串进行url
编码
1 | java.net.URLEncoder.encode(str, "UTF-8") |
Gadget
如下:
1 | HashSet#readObject() |
依赖版本
aspectjweaver : 1.9.2
commons-collections : 3.2.2
题解poc文件
使用MapTransformer替代ConstantTransformer
使用CC链,引入FactoryTransformer,使用InstantiateFactory、ConstantFactory代替InstantiateTransformer、ConstantTransformer
AspectJWeaver利用思路
直接写入jsp
如果目标Web应用可以写入jsp
,并且能够解析,那直接写jsp Webshell
即可,比较直接
写入class文件
可以在poc
中添加如下方法
1 | protected static byte[] getBytescode() throws Exception { |
并修改代码
1 | map1.put(fileName,getBytescode()); |
此时可以向target
中写入class
文件,但是无法执行,只有在不清理target
的情况下重启项目,才能执行。
SpringBoot采用jar包部署的情况
现在很多应用都采用了SpringBoot
打包成一个jar
或者war
包放到服务器上部署,就算我们能够写文件,也不会被内嵌的中间件解析,这个时候应该怎么办呢?LandGrey
大佬给出了解决办法:Spring Boot Fat Jar 写文件漏洞到稳定 RCE 的探索
向服务器的jdk
目录下写入jar
包,由于jvm
的类加载机制,并不会一次性把所有jdk
中的jar
包都进行加载,所以可以先写入/jre/lib/charsets.jar
进行覆盖,然后给request header
中加入特殊头部,此时由于给定了字符编码,会让jvm
去加载charset.jar
,从而触发恶意代码。恶意头部可以如下:
1 | Accept: text/plain, */*; q=0.01 |
具体细节请见大佬的博客和github
仓库。
打内存马
也有说直接打内存马,但还没尝试