AspectJWeaver
0x01 What is AspectJWeaver
AspectJWeaver运用在面向切面编程(AOP: Aspect Oriented Programming)中
AOP是一种编程范式,旨在提高模块化、降低代码耦合度。它可以向现有代码添加其他行为而不修改代码本身。Spring就运用到了AOP
AOP的一些概念:
切面(Aspect): 公共功能的实现。如日志切面、权限切面、验签切面。给Java类使用
@Aspect注释修饰,就能被AOP容器识别为切面通知(Advice): 切面的具体实现,即切面类中的一个方法,根据放置的地方不同,可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)与环绕通知(Around)
连接点(JoinPoint): 程序在运行过程中能够插入切面的地方。Spring只支持方法级的连接点。比如一个目标对象有5个方法,就有5个连接点
切入点(PointCut): 用于定义通知应该切入到哪些连接点
目标对象(Target): 即将切入切面的对象,被通知的对象
代理对象(Proxy): 将通知应用到目标对象之后被动态创建的对象,可以简单地理解为,代理对象的功能等于目标对象本身业务逻辑加上共有功能。代理对象对于使用者而言是透明的,是程序运行过程中的产物。目标对象被织入公共功能后产生的对象。
织入(Weaving): 将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译时、类加载时、运行时。Spring是在运行时完成织入,运行时织入通过Java语言的反射机制与动态代理机制来动态实现。
大概了解一下,跟下面讲的利用链没啥关系
0x02 Any File Write
这个利用链用到了CC依赖。回忆一下,Commons Collections 3.2.2中 增加了⼀个⽅法FunctorUtils#checkUnsafeSerialization ⽤于检测反序列化是否安全,其会检查常⻅的危险Transformer类,当我们反序列化包含这些对象时就会抛出异常。
AspectJWeaver这里只用到了CC里的LazyMap、TiedMapEntry、ConstantTransformer,高版本CC仍具有实用性。
HashSet#readObject
-> HashMap#put(tiedMapEntry, new Object())
-> HashMap#hash(tiedMapEntry)
-> TiedMapEntry#hashCode
-> TiedMapEntry#getValue
-> LazyMap#get
-> SimpleCache$StorableCachingMap#put
-> SimpleCache$StorableCachingMap#writeToPath
-> FileOutputStream#write()
StoreableCachingMap是HashMap的子类,重写了put方法
writeToPath实现写文件,folder和key拼接组成文件全路径。传入StoreableCachingMap#put的key为文件名,value为写入的内容。
但单纯的写文件危害不大,还得配合其他漏洞打。
如何将写文件升级为RCE呢
🌔Jsp WebShell
若目标应用支持解析JSP,直接写个Jsp WebShell
🌓class file in WEB-INF/classes
既然有反序列化入口,在WEB-INF/classes下写入一个恶意的字节码文件,在readObject或静态代码块中编写命令执行,然后再反序列化这个类。若有往JAVA_HOME写的权限,可以往jre/classes写入编译好的class
🌒FatJar under SpringBoot
现很多应用都采用了SpringBoot打包成一个jar或者war包放到服务器上部署,我们无法往classpath写jsp或字节码文件了,那就考虑覆盖jdk的系统类。
由于jvm的类加载机制,并不会一次性把所有jdk中的jar包都进行加载。往目标环境写入/jre/lib/charsets.jar进行覆盖,然后在request header中加入特殊头部,此时由于给定了字符编码,会让jvm去加载charset.jar,从而触发恶意代码。
这种方法的缺点是目标$JAVA_HOME未知,需一个个尝试。
可以参考这篇文章👉Click Me
0x03 Bypass SerialKiller
利用链中的ConstantTransformer在SerialKiller中被ban了
https://github.com/ikkisoft/SerialKiller
需要找一个和ConstantTransformer效果等同的Transformer
✖️
StringValueTransformer
transform返回输入对象的字符串表示,会调用toString()

本以为这个能成,但后面写文件时会把value强转为byte[],而String强转不了byte[]。
✔️
FactoryTransformer+ConstantFactory


0x04 Forward Deser
利用AspectJWeaver任意文件写后,发现同目录下出现了一个cache.idx文件
StorableCachingMap#put中调用完writeToPath后紧接着调用了storeMap
获取当前系统时间,若和上次存储时间的时间差大于storingTimer,会创建一个文件cache.idx,并将this序列化写入。
有序列化的地方必然有反序列化,StorableCachingMap#init
读取了cache.idx并进行反序列化。接着看哪里调用了StoreableCachingMap#init
在SimpleCache的构造方法中调用StoreableCachingMap#init也很好理解。顾名思义这个类是一个缓存类,cacheMap成员即其内部类StoreableCachingMap,充当了一个内存层面的键值对缓存,当然它支持持久化存储,也就是每次写入缓存(StoreableCachingMap#put)时,判断和上次存储时间的时间差是否超过storingTimer存储计时器,超过则进行持久化操作,存储格式是序列化数据,存储文件为cache.idx。下次需要恢复到内存的时候,只需重新构造一个SimpleCache对象即可,它会调用StoreableCachingMap#init对持久化文件进行反序列化,得到原来的cacheMap
思路到这里就很明显了,先用AspectJWeaver往cache.idx写入恶意序列化数据,再通过CC链触发构造函数。
为了防止写入文件后,storeMap又马上重写了我们的cache.idx,设置storingTimer为稍微大一点的值。
很可惜,不管是InstantiateTransformer还是InstantiateFactory,都要求目标类的构造方法需要是public
应该能配合其他漏洞打,比如SnakeYaml
贴一个写CC6序列化数据的payload,接下来就是调用SimpleCache构造器的问题了。
Last updated
Was this helpful?