此仓库是 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 表达式计算器进行处理。攻击者若将 OGNL 表达式放入 Content-Type 标头中,则可在服务器端以 Tomcat 进程用户的身份执行该表达式。
此仓库包含:
SC3010-Computer-Security/
├── simulation/
│ ├── backend/ # 存在漏洞的 Apache Struts2 2.3.28 服务器(Java/Maven)
│ └── attack-script/ # CVE-2017-5638 漏洞攻击脚本
│ └── exploit_cve_2017_5638.ps1 # PowerShell(跨平台)
├── struts-src-code/ # Apache Struts2 参考源码 + 法律声明
│ ├── licenses/ # LICENSE、NOTICE 以及组件许可证
│ └── src/
│ ├── struts2-core/ # 请求处理管线类 + 存在漏洞的 JakartaMultiPartRequest
│ ├── xwork2/ # ActionContext.java、OgnlUtil.java
│ └── ognl/ # OgnlContext.java
├── diagrams/ # 状态机 + 顺序图,带注释的 OGNL 攻击载荷
└── _notes/ # 背景阅读材料
下图显示了 Struts2 请求管线中的每个决策分支。红色路径是攻击者强制采取的路径;灰色 → ... 分支是安全路径,在攻击中不会触发。
flowchart TD
Start([攻击者发送 HTTP POST /upload.action<br/>Content-Type: %{OGNL_PAYLOAD}.multipart/form-data])
Start --> PIPE
PIPE["<b>Struts2 过滤器 → 包装 → 解析管线</b><br/>doFilter() → PrepareOperations.wrapRequest()<br/>→ Dispatcher.wrapRequest() → new MultiPartRequestWrapper()"]
PIPE --> D_CT{"Content-Type 是否包含<br/>"multipart/form-data"?"}
D_CT -- "否 → 普通请求 ..." --> OUT_NORM([非上传路径 ...])
D_CT -- "是(攻击者在载荷后附加<br/>.multipart/form-data)" --> PARSE
PARSE[/"<b>JakartaMultiPartRequest.parse()</b><br/>→ Commons FileUpload.parseRequest()"/]
PARSE --> D_VALID{"FileUpload 能否解析<br/>Content-Type?"}
D_VALID -- "能 → 正常文件上传 ..." --> OUT_OK([文件被提取、动作执行 ...])
D_VALID -- "不能 — Content-Type 格式错误<br/>抛出 InvalidContentTypeException<br/>(消息 = 原始 Content-Type 字符串)" --> CATCH
CATCH["parse() 的 catch 块<br/>调用 buildErrorMessage(e, {})"]
CATCH --> FIND
FIND[/"<b>buildErrorMessage()</b><br/>→ LocalizedTextUtil.findText(class, errorKey, locale, e.getMessage(), args)"/]
FIND --> D_KEY{"资源包是否有键<br/>struts.messages.upload.error.*?"}
D_KEY -- "有 → 安全的本地化消息 ..." --> OUT_SAFE([安全地显示错误 ...])
D_KEY -- "无 — 回退到<br/>e.getMessage() 作为默认值<br/>(攻击者的原始 Content-Type)" --> TRANSLATE
TRANSLATE["TextParseUtil.translateVariables()<br/>扫描字符串中的 %{...} 表达式"]
TRANSLATE --> D_OGNL{"字符串是否包含<br/>%{...} OGNL 表达式?"}
D_OGNL -- "否 → 普通错误字符串 ..." --> OUT_PLAIN([安全错误消息 ...])
D_OGNL -- "是 — 计算 OGNL<br/>在攻击者内容内部" --> SANDBOX
SANDBOX[/"<b>OGNL 沙箱绕过</b><br/>#ognlUtil.getExcludedClasses().clear()<br/>#ognlUtil.getExcludedPackageNames().clear()"/]
SANDBOX --> ACCESS
ACCESS[/"<b>无限制反射</b><br/>#context.setMemberAccess(DEFAULT_MEMBER_ACCESS)"/]
ACCESS --> RCE
RCE[/"<b>RCE</b><br/>Runtime.getRuntime().exec(cmd)"/]
RCE --> Done([命令以 Tomcat 进程用户身份执行])
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 许可证 2.0 版 授权。
许可证副本请参见struts-src-code/licenses/LICENSE.txt。
Apache 许可证要求的完整署名声明请参见struts-src-code/licenses/NOTICE.txt。
Apache Struts 2 捆绑了其他第三方组件,每个组件受其各自许可证约束:
| 组件 | 许可证文件 |
|---|---|
| OGNL(对象图导航库) |
| XWork | struts-src-code/licenses/XWORK-LICENSE.txt |
| FreeMarker | struts-src-code/licenses/FREEMARKER-LICENSE.txt |