本仓库是 SC3010 计算机安全课程项目 的一部分。
它复现了 CVE-2017-5638,这是 Apache Struts 2(版本 2.3.5–2.3.31 和 2.5–2.5.10)中的一个严重远程代码执行漏洞,该漏洞在 2017 年 Equifax 数据泄露事件 中被广为人知地利用,导致约 1.47 亿人的个人记录被窃取。
该漏洞源于一个单一的设计缺陷:当 Apache Struts 无法解析 multipart/form-data 上传请求时,它会通过将原始的 Content-Type 头——未经任何清理——嵌入到一个字符串中来构建错误消息,而该字符串随后会被传入 OGNL 表达式求值器。因此,攻击者只要在 Content-Type 头中放入一个 OGNL 表达式,就能以 Tomcat 进程用户的身份在服务端执行该表达式。
本仓库包含:
SC3010-Computer-Security/
├── simulation/
│ ├── backend/ # Vulnerable Apache Struts2 2.3.28 server (Java/Maven)
│ └── attack-script/ # Exploit script for CVE-2017-5638
│ └── exploit_cve_2017_5638.ps1 # PowerShell (cross-platform)
├── struts-src-code/ # Apache Struts2 reference source + legal notices
│ ├── licenses/ # LICENSE, NOTICE, and component licenses
│ └── src/
│ ├── struts2-core/ # Request pipeline classes + vulnerable JakartaMultiPartRequest
│ ├── xwork2/ # ActionContext.java, OgnlUtil.java
│ └── ognl/ # OgnlContext.java
├── diagrams/ # State-machine + sequence diagrams, annotated OGNL payload
└── _notes/ # Background reading
下图展示了 Struts2 请求管道中的每个决策分支。红色路径是攻击者强制执行的路径;灰色的 → ... 分支是在漏洞利用期间不适用的安全路径。
flowchart TD
Start(["Attacker sends HTTP POST /upload.action<br/>Content-Type: %{OGNL_PAYLOAD}.multipart/form-data"])
Start --> PIPE
PIPE["<b>Struts2 filter → wrap → parse pipeline</b><br/>doFilter() → PrepareOperations.wrapRequest()<br/>→ Dispatcher.wrapRequest() → new MultiPartRequestWrapper()"]
PIPE --> D_CT{"Content-Type contains<br/>multipart/form-data?"}
D_CT -- "No → normal request ..." --> OUT_NORM([non-upload path ...])
D_CT -- "Yes (attacker appends<br/>.multipart/form-data to payload)" --> PARSE
PARSE[/"<b>JakartaMultiPartRequest.parse()</b><br/>→ Commons FileUpload.parseRequest()"/]
PARSE --> D_VALID{"FileUpload can parse<br/>Content-Type?"}
D_VALID -- "Yes → normal file upload ..." --> OUT_OK([files extracted, action executes ...])
D_VALID -- "No — malformed Content-Type<br/>throws InvalidContentTypeException<br/>(message = raw Content-Type string)" --> CATCH
CATCH["parse() catch block<br/>calls buildErrorMessage(e, {})"]
CATCH --> FIND
FIND[/"<b>buildErrorMessage()</b><br/>→ LocalizedTextUtil.findText(class, errorKey, locale, e.getMessage(), args)"/]
FIND --> D_KEY{"Resource bundle has key<br/>struts.messages.upload.error.*?"}
D_KEY -- "Yes → safe localised message ..." --> OUT_SAFE([error displayed safely ...])
D_KEY -- "No — falls back to<br/>e.getMessage() as the default<br/>(attacker's raw Content-Type)" --> TRANSLATE
TRANSLATE["TextParseUtil.translateVariables()<br/>scans string for %{...} expressions"]
TRANSLATE --> D_OGNL{"String contains<br/>%{...} OGNL expression?"}
D_OGNL -- "No → plain error string ..." --> OUT_PLAIN([safe error message ...])
D_OGNL -- "Yes — evaluates OGNL<br/>inside attacker content" --> SANDBOX
SANDBOX[/"<b>OGNL sandbox bypass</b><br/>#ognlUtil.getExcludedClasses().clear()<br/>#ognlUtil.getExcludedPackageNames().clear()"/]
SANDBOX --> ACCESS
ACCESS[/"<b>Unrestricted reflection</b><br/>#context.setMemberAccess(DEFAULT_MEMBER_ACCESS)"/]
ACCESS --> RCE
RCE[/"<b>RCE</b><br/>Runtime.getRuntime().exec(cmd)"/]
RCE --> Done([Command executed as Tomcat process user])
style Start fill:#d32f2f,color:#fff
style Done fill:#d32f2f,color:#fff
style OUT_NORM fill:#9e9e9e,color:#fff
style OUT_OK fill:#9e9e9e,color:#fff
style OUT_SAFE fill:#9e9e9e,color:#fff
style OUT_PLAIN fill:#9e9e9e,color:#fff
style SANDBOX fill:#b71c1c,color:#fff
style ACCESS fill:#b71c1c,color:#fff
style RCE fill:#b71c1c,color:#fff
style CATCH fill:#e65100,color:#fff
style TRANSLATE fill:#e65100,color:#fff
关于带分支说明的完整状态机,请参见 diagrams/cve-2017-5638-state-machine.md。
关于时序图视图和带注释的 OGNL 载荷解析,请参见 diagrams/cve-2017-5638-attack-chain.md。
本仓库出于学术安全研究目的,复现了 Apache Struts 2.3.28 源代码的部分内容。
Apache Struts 版权所有 © 2000–2016 The Apache Software Foundation。
依据 Apache License, Version 2.0 授权。
许可证副本可在struts-src-code/licenses/LICENSE.txt获取。
Apache License 要求的完整归属声明见struts-src-code/licenses/NOTICE.txt。
Apache Struts 2 捆绑了额外的第三方组件,每个组件均受其各自许可证约束:
| 组件 | 许可证文件 |
|---|---|
| OGNL(Object-Graph Navigation Library) | struts-src-code/licenses/OGNL-LICENSE.txt |
| XWork | struts-src-code/licenses/XWORK-LICENSE.txt |
| FreeMarker | struts-src-code/licenses/FREEMARKER-LICENSE.txt |