Web
Log4j使用示例
Apache Log4j是基于Java的日志记录工具。
在IDEA里使用Log4j日志组件。以log4j-1.2.17为例,创建java工程,并添加配置文件log4j.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ### 设置### log4j.rootLogger = debug,stdout,D,E
### 输出信息到控制抬 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 输出DEBUG 级别以上的日志到debug.log ### log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File = D://Works/IDEAProjects/TestDemo2/logs/debug.log log4j.appender.D.Append = true log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出ERROR 级别以上的日志到error.log ### log4j.appender.E = org.apache.log4j.DailyRollingFileAppender log4j.appender.E.File = D://Works/IDEAProjects/TestDemo2/logs/error.log log4j.appender.E.Append = true log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayout log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
|
测试demo
CVE-2017-5645
可见该CVE的影响版本是Log4j的2.x – 2.8.2版本。在使用TCP/UDP 套接字接口监听获取序列化的日志事件时,存在反序列化漏洞。
测试环境:jdk 1.7 + log4j 2.8
测试demo
运行该demo后TcpSocketServer将会在本地监听1337端口。因为Log4j本身并没有爆出可命令执行的反序列化gadget,所以这里使用commons-collections-3.1.jar的利用链,利用ysoserial生成payload再利用nc发送到1337端口上实现命令执行。
1
| java -jar ysoserial-master-30099844c6-1.jar CommonsCollections1 "calc" > test.ser
|
接下来开始漏洞分析。
跟入上面demo的main函数
上图可以看到引入了jcommander-1.48.jar,主要是main函数里的parseCommandLine需要。
来到了org.apache.logging.log4j.core.net.server. TcpSocketServer里
重点关注图中的标记处,可见创建了一个socketServer并且通过startNewThread启用了一个新进程。
根据java中线程的生命周期,接下来在TcpSocketServer.class里寻找run函数
于是接下来就是跟入这个SocketHandler里
从上图的标记处可见首先调用了wrapStream打包输入流,接着将输入流传入到logEvents函数里,直接跟入这两个函数都是接口,所以需要寻找实现类。
于是回到上面socketServer开始创建的地方,也就是createSerializedSocketServer函数里
进到ObjectInputStreamLogEventBridge类里,看到了上面两个接口的实现方法
从代码里可以看到,没有对外界可控的输入流做任何过滤直接传入readObject()进行反序列化,从而存在反序列化漏洞。
CVE-2019-17571
该CVE的影响版本是Log4j的1.2 - 1.2.17版本。
测试环境:jdk 1.7 + log4j 1.2.17
测试demo
注意这里的参数
log4j.properties文件内容从网上找了一个
1 2 3 4 5 6 7 8
| # Root logger option log4j.rootLogger=INFO, stdout
# Direct log messages to stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
|
如果提示没有读取到配置文件,查看工作目录是否配置正确
接下来开始漏洞分析
跟入main函数,可以看到这里建立了一个线程对象,并调用了SocketNode类对socket做了处理。
跟入SocketNode类
直接从socket里获取输入流并封装到ObjectInputStream对象中,然后在SocketNode.class找到run函数,直接调用了ObjectInputStream.readObject()进行反序列化。
参考链接
https://www.anquanke.com/post/id/195932
https://mochazz.github.io/2019/12/26/Log4j%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%88%86%E6%9E%90/#CVE-2017-5645
https://blog.csdn.net/Evankaka/article/details/45815047