跳转到帖子

背景Struts2 这类历史漏洞在很多企业内网仍然有参考价值:一方面,老系统迁移周期长,仍可能存在旧版本组件;另一方面,Struts2 漏洞链条通常比较典型,适合用来梳理 Java Web 框架中的参数绑定、表达式解析、拦截器调用链和安全边界问题。这篇文章整理的是一次在本地隔离实验环境中的分析记录,重点放在漏洞成因、复现验证思路、日志侧观测和修复建议上。文中不会提供面向真实目标的攻击利用流程,也不会给出可直接用于未授权入侵的载荷。问题场景某些 Struts2 历史漏洞的共同特点是:用户可控输入在框架处理流程中被错误地带入表达式求值或对象属性绑定过程,最终导致超出预期的代码路径被触发。实际排查时,不能只看业务代码是否显式调用危险 API,还要关注框架层的隐式处理逻辑。常见风险入口包括:请求参数名或参数值被框架解析为表达式。Content-Type、文件上传字段、错误处理流程等非业务字段进入异常处理链。拦截器栈中存在不安全的参数绑定或类型转换逻辑。旧版本依赖长期未升级,安全补丁未覆盖到运行环境。实验环境与边界建议使用完全隔离的本地环境进行分析,例如 Docker 容器或本机虚拟机。不要将存在漏洞的样例应用暴露到公网,也不要对非授权系统进行验证。一个最小化的本地分析环境可以包括:JDK 8 或与目标框架兼容的 JDK 版本。Maven,用于依赖管理和源码跳转。IDEA 或 Eclipse,用于断点调试。本地代理工具,仅用于观察本机请求与响应。容器网络关闭外部访问,仅开放 localhost。# 示例:仅绑定到本机端口,避免暴露到外部网络
docker run --rm -p 127.0.0.1:8080:8080 vulnerable-lab:local这里的关键点不是“跑起来”,而是保证实验边界清晰:样例应用只服务于源码级分析和补丁验证,不用于扫描、攻击或验证第三方资产。技术分析思路分析 Struts2 类漏洞时,我通常按三层来看:请求入口、框架处理链、危险操作触发点。1. 请求入口先确认哪些 HTTP 字段会进入框架处理流程。除了常规参数,还要关注 Header、Content-Type、上传文件名、异常消息等位置。很多漏洞并不是业务 Controller 主动读取了这些字段,而是框架在参数封装或错误处理时自动处理。排查时可以记录请求进入后的关键对象:HttpServletRequest 中的 parameterMap。Struts2 ActionContext。ValueStack 中的对象变化。Interceptor 调用顺序。2. 拦截器与参数绑定Struts2 的拦截器栈是分析重点。一次请求通常会经过多个拦截器,例如参数处理、文件上传、类型转换、异常处理等。漏洞经常出现在“用户输入被当作表达式处理”或“异常信息再次进入渲染流程”这类边界模糊的位置。建议在以下位置设置断点:参数拦截器处理请求参数的位置。表达式求值相关方法。类型转换失败后的异常处理路径。结果渲染前对 ValueStack 的访问位置。通过断点观察用户输入是否被传递到表达式解析器,而不是只看最终响应结果。很多时候,响应页面没有明显异常,但危险路径已经被触发。3. 表达式解析边界Struts2 历史问题中,OGNL 表达式是绕不开的部分。分析时不建议一上来关注具体利用字符串,而是先确认几个问题:输入是否被当作普通字符串处理。是否进入表达式求值方法。是否存在安全上下文限制。框架版本中是否对特定入口做了过滤或禁用。如果某个字段从 HTTP 请求一路进入表达式求值函数,就需要继续确认安全上下文是否有效,是否存在可访问敏感对象的可能。这个过程可以通过调试变量和调用栈完成,不需要构造破坏性载荷。关键复现步骤下面是一种偏安全的复现验证流程,适合做漏洞存在性确认和补丁验证,不涉及对真实系统的攻击。步骤一:确认依赖版本先从构建文件或运行包中确认 Struts2 版本。很多系统存在“源码是新版本,部署包仍是旧版本”的情况,所以最好同时检查源码依赖和运行时 Jar。# Maven 项目查看依赖树
mvn dependency:tree | grep -i struts

# 在已部署目录中检查相关 Jar
find ./WEB-INF/lib -iname '*struts*' -o -iname '*ognl*'如果是传统部署包,还可以对 Jar 包做哈希记录,方便后续对比升级前后的变化。步骤二:打开调试日志在本地环境中临时提高框架日志级别,可以帮助观察参数处理和异常链路。生产环境不建议长期开启过细日志,避免泄露敏感信息。# log4j2 示例,仅用于本地分析
logger.struts.name = org.apache.struts2
logger.struts.level = debug

logger.ognl.name = ognl
logger.ognl.level = debug日志中重点看两类信息:一类是参数名、类型转换、拦截器执行顺序;另一类是异常堆栈中是否出现表达式解析相关调用。步骤三:使用无害探针验证数据流不要直接使用高危载荷。可以先用普通字符串、边界字符、类型转换错误等方式观察数据流。例如提交一个明显不会造成破坏的标记值,然后在断点和日志中追踪它是否进入表达式处理路径。# 示例:只用于本地代理工具中观察请求结构
GET /demo/action?name=probe_struts_analysis HTTP/1.1
Host: 127.0.0.1:8080如果标记值仅进入业务 Action 的普通属性绑定,风险相对可控;如果它进入表达式求值、异常消息渲染或 ValueStack 动态访问,就需要结合版本公告进一步确认。步骤四:对照补丁差异Struts2 漏洞分析很适合做补丁 diff。对比修复前后的代码,通常能看出官方是在哪个入口加了限制,例如:禁止某些表达式语法进入参数名。加强 Content-Type 或上传字段校验。调整异常处理流程,避免将用户输入再次解释。增加安全常量或默认拦截策略。补丁 diff 的价值在于能帮助定位真正风险点,而不是只依赖漏洞编号和扫描器结论。代码审计关注点在企业项目中排查 Struts2 风险时,可以重点看以下配置和代码模式。1. Struts 配置检查 struts.xml 中是否启用了不必要的动态方法调用、通配符映射或过宽的拦截器栈。老项目中为了开发方便打开的配置,迁移多年后很容易被遗忘。<!-- 示例:审计时关注类似配置,是否存在过宽暴露面 -->
<constant name=\"struts.devMode\" value=\"false\" />
<constant name=\"struts.enable.DynamicMethodInvocation\" value=\"false\" />devMode 必须关闭。动态方法调用如果没有明确业务需要,建议禁用。通配符 Action 需要结合访问控制一起审查,避免把内部方法意外暴露。2. Action 属性绑定检查 Action 中是否存在复杂对象直接绑定、集合对象自动填充、嵌套属性过深等情况。参数绑定越复杂,越需要限制输入字段白名单。// 示例:建议对输入字段做显式校验,而不是完全依赖框架绑定
public class UserAction {
private String username;

public String execute() {
if (username == null || !username.matches(\"[a-zA-Z0-9_]{3,32}\")) {
return \"error\";
}
return \"success\";
}
}这里不是说简单正则能解决所有问题,而是强调输入边界应由业务明确约束,不能把所有控制权交给框架的自动绑定。3. 文件上传链路文件上传相关漏洞经常和 Content-Type、文件名、临时文件处理有关。审计时需要确认:上传目录是否在 Web 可访问路径外。文件扩展名和 MIME 类型是否双重校验。服务端是否重新生成文件名。异常信息是否会原样返回前端。上传组件版本是否包含历史安全问题。日志与检测建议防护和排查不能只依赖 WAF。对 Struts2 这类框架漏洞,应用日志、访问日志和异常日志结合起来更有效。可以关注以下异常迹象:请求参数名中出现异常的表达式符号或长字段名。Content-Type 非常规且包含大量特殊字符。短时间内大量 400、500 响应集中在少数 Action。异常堆栈中频繁出现 ognl、ValueStack、ParametersInterceptor 等关键词。访问路径集中命中旧 Action 或测试接口。一个简单的本地日志筛选命令如下:# 从应用日志中筛选可疑框架调用痕迹
grep -Ei 'ognl|ValueStack|ParametersInterceptor|StrutsException|ConversionError' app.log如果发现异常请求,建议先保全日志,再在隔离环境复现调用链,不要直接在生产系统上反复测试。修复与加固建议实际修复时,优先级建议如下:升级 Struts2 到官方仍维护且包含相关补丁的版本。同步升级 OGNL、Commons FileUpload 等相关依赖,避免只升主框架。关闭 devMode、动态方法调用等非必要功能。收敛 Action 暴露面,删除测试接口和遗留入口。对外部输入做白名单校验,尤其是文件上传、复杂对象绑定和枚举字段。生产环境统一错误页,避免将异常堆栈和用户输入回显。在 CI 中加入依赖漏洞检查和制品版本校验。依赖检查可以放到构建流程中,例如使用 Maven 插件或企业内部制品治理平台。关键是让“版本风险”变成持续可见,而不是等到应急时再人工翻 Jar 包。常见误区误区问题建议只看业务代码忽略框架自动绑定和拦截器链结合调用栈和框架源码分析只升级 struts2-core相关依赖仍可能存在风险同时检查 OGNL、上传组件等依赖用扫描器结果直接定性可能存在误报或漏报在隔离环境做版本和数据流确认生产环境开启详细日志可能泄露敏感信息本地分析开启,生产按需临时调整补丁后不回归升级可能影响拦截器和类型转换行为补充 Action 级回归测试事件复盘角度如果是在真实事件中发现 Struts2 风险,复盘时建议不要只停留在“某组件未升级”。更有价值的是把问题拆成几个层面:资产层:是否准确知道哪些系统使用了 Struts2。依赖层:是否能快速定位运行时真实版本。发布层:安全升级是否有灰度和回滚机制。监控层:异常请求和异常堆栈是否能被及时发现。治理层:历史系统是否有下线计划或隔离策略。很多老框架漏洞真正难处理的不是漏洞本身,而是资产不清、依赖不可控、系统没人敢改。复盘时把这些问题写清楚,比单纯补一个版本更有长期价值。总结Struts2 漏洞分析的核心,不是记住某个漏洞编号对应的利用字符串,而是理解用户输入如何穿过框架边界、进入表达式解析或异常处理流程。做本地复现时,应尽量使用无害探针、断点和日志来确认数据流,避免使用破坏性载荷。从防守角度看,最有效的措施仍然是:准确资产、及时升级、关闭危险配置、收敛输入边界、建立日志检测。老系统无法立即替换时,也至少应该做到网络隔离、访问控制和运行时版本可见。这样在下一次类似漏洞出现时,处理成本会低很多。

代码审计与漏洞定位示意图
代码审计、调用链与关键函数定位示意

0篇意见

推荐意见

没有意见。

游客
抱歉,你的帖子内容包括我们不允许的字词。请编辑你的帖子,删除下面高亮的屏蔽字。
添加意见…