freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

Apache Log4j 反序列化
vitarain 2023-05-08 20:48:14 166492
所属地 四川省

Apache Log4j 反序列化

前言

​ 在应用程序中添加日志记录最普通的做法就是在代码中嵌入许多的打印语句,这些打印语句可以输出到控制台或文件中,比较好的做法就是构造一个日志操作类来封装此类操作,而不是让一系列的打印语句充斥了代码的主体。
​ Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
​ Log4j在工程中可以易用,方便等代替了 System.out 等打印语句,它是Java下最流行的日志输入工具,一些著名的开源项目,像spring、hibernate、struts都使用该工具作为日志输入工具,可以帮助调试(有时候debug是发挥不了作用的)和分析。

CVE-2017-5645

漏洞版本

Log4j 2.x <= 2.8.1

漏洞复现

vulhub拉起镜像

docker-compose up -d

image

利用ysoserial反序列化工具生成payload发送给目标IP地4712端口

image

查看命令执行结果

image

漏洞分析

pom配置

<!-- maven文件pom.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>log4j-2.x-rce</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.8.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.8.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.1</version>
        </dependency>
    </dependencies>

</project>

调用链

TcpSocketServer.createSerializedSocketServer() -> # 创建socketServer
startNewthread() ->	# 接受客户端传递的数据
TcpSocketServer.run() -> # 将数据交由SocketHandler处理
SocketHandler.run() -> # 数据作logEvents()处理
ObjectInputStreamLogEventBridge.logEvents() -> 
inputStream.readObject() #反序列化

image

入口点为TcpSocketServer.class#main

image

跟进createSerializedSocketServer函数,函数创建了socketServer

image

继续步过,进入到socketServer.startNewThread(),可以继续跟进到TcpSocketServer.class#run中

image

101行处,serverSocket.accept()监听接受数据;

105行处,将接受的数据交由SocketHadnler()处理;

进入SocketHandler函数中

image

在SocketHandler函数中,首先通过构造函数返回一个ObjectInputStream对象给this.inputStream。之后在自身的run函数中直接将之前赋值的this.inputStream传入到TcpSocketServer.this.logEventInput.logEvents函数中,跟进

image

在logEvents函数内,进行反序列化inputStream.readObject()

CVE-2019-17571

漏洞版本

1.2.4 <= Log4j <= 1.2.17

漏洞复现

利用代码

import org.apache.log4j.net.SimpleSocketServer;

public class Log4j {

    public static void main(String[] args) {
        Log4j log4j = new Log4j();
        log4j.CVE_2019_17571();
    }

    /**
     * CVE-2019-17571 漏洞
     */
    private void CVE_2019_17571() {
        System.out.println("Listening on port 1234");
        String[] arguments = {"1234", (new Log4j()).getClass().getClassLoader().getResource("log4j.properties").getPath()};
        SimpleSocketServer.main(arguments);
        System.out.println("Log4j output successfuly");
    }
}

log4j.properties(位于resources目录下)

log4j.rootCategory=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.threshold=DEBUG
log4j.appender.stdout.layout.ConversionPattern=[%d{yyy-MM-dd HH:mm:ss,SSS}]-[%p]-[MSG!:%m]-[%c\:%L]%n

使用ysoserial生成payload,再用nc发送到socket监听端口

image

image

漏洞分析

调用链

SimpleSocketServer#main() -> # 创建ServerSocket
Thread.start() -> #start方法底层调用Runnable对象的run方法
SocketNode.run() ->
event = (LoggingEvent)this.ois.readObject();

在漏洞代码处打下断点

image

步入SimpleSocketServer#main

image
首先步入SocketNode

image

SocketNode类有一个Runnable接口,而且在其中有一个run()函数

image

run()函数中有readObject()反序列化点

image

那么如何才能调用到该函数呢?

我们可以步出回到

image
可以看到该行代码调用了Thread类的start()方法

image

通过注释可以知道在start0()中,start()方法会底层调用Runnable对象中的run()方法。

image

英文不好,别介(^_^)

(new Thread(new SocketNode(socket, LogManager.getLoggerRepository()), "SimpleSocketServer-" + port)).start();

image

在Thread的构造方法中可以看到SocketNode对象是作为target参数传入。所以在调用Thread的start()方法时会调用到SocketNode的run()方法,最后执行readObject()

# web安全
本文为 vitarain 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
vitarain LV.2
这家伙太懒了,还未填写个人描述!
  • 4 文章数
  • 2 关注者
CVE-2016-4437(Apache Shiro)
2023-04-24
Weblogic-SSRF漏洞复现
2023-03-02
渗透测试 | FastJson漏洞原理与复现
2023-03-01
文章目录