struts2 jsp跳转action 404_Struts2 命令执行系列漏洞 S2001

01

前言

Struts2 是较早出现实现 MVC 思想的 java 框架。struts2 在 jsp 文件中使用 ognl 表达式来取出值栈 (ValueStack) 中的数据。struts 标签与 ognl 表达式的关系类似于 jstl 标签与 el 表达式的关系。[故在 jsp 文件中需引入 struts 标签库]

值栈分为 root 栈及 context 栈,其中 root 部分是一个 CompoundRoot 对象,extends  ArrayList ,用于存放对象,比如 Action 对象,也可以叫对象栈。context 部分是一个 OgnlContext 对象,implements Map,也可以叫 context/map 栈,用来存放常用对象的引用,其中也包括 root 部分的引用(com.opensymphony.xwork2.util.ValueStack.ValueStack 中存在其引用,request 域对象的 map 中也存在 value 为 struts.valueStack 的引用),以及 request、session、application 域对象。

95149f7d07f57ec180bb6673c8122914.png

图1-1|值栈- root 栈及 context 栈 

02

概述

S2-001 漏洞由于在其解析 jsp 文件的标签数据时,官方举例是 form 标签的 textfield 数据中,在验证表单出错,代码逻辑返回的页面再次回到表单页面时,若开启了 altSyntax (默认开启), struts2 就会对标签中的数据在值栈(这里是在 root 栈中)中自顶向底找到与表单 name 名同名的属性值,进行 ognl 表达式解析并显示。故在表单中输入形如 %{…} (代码逻辑中根据此形式截取字符串来获取表达式)将会对其中的表达式进行递归解析。

官方链接:

https://cwiki.apache.org/confluence/display/WW/S2-001

影响版本: 

WebWork 2.1 (with altSyntax enabled), WebWork 2.2.0 - WebWork 2.2.5, Struts 2.0.0 - Struts 2.0.8

af131848beb20fd08c2b3f7eb5832f44.png

图2-1|default.properties 中 altSyntax 参数默认开启 

03

复现

环境:

apache-tomcat-9.0.37 、 jdk1.8.0_261 、 struts 2.0.1

坑点:

进行表单验证时 500 报错:java.lang.NoClassDefFoundError: org/apache/struts2/spi/RequestContext ,加入 struts2-api 的 jar 包即可解决。

找不到 struts-default.xml ,在  Project Structure 的 Facet 中 File Set 编辑并引进来。

1、首先创建一个 struts2 的项目(Java Enterprise -- Web Application -- struts2 -- 下方 Libraries - Set up library later -- next -- 设置名称即可)… (其实网上有,就不贴图了)

2、结构如图 3-1 ,在 WEB-INF 下创建 lib ,将图中六个 jar 包复制过来,并将他们右键 Add as Library ,在 Project Structure 的 Artifacts 将其 put into output root 即可(或者打开 Project Structure 直接点击底部 fix )。

8c21bcce224fa338b8a6a6cfe29ad766.png

图3-1|项目结构 

3、在 index.jsp 中添加一个 struts 的 form 标签。

055a5c7889493a15fac120a403378b6a.png

图3-2|index.jsp 

4、修改 struts.xml 文件。验证出错重新回到 index.jsp ,成功则进入 welcome.jsp

9b85f4741801b10b58261a3d66f99f69.png

图3-3|struts.xml 

5、LoginAction.java :有 username 、 password 两个属性(和 jsp 文件中表单的 name 保持一致)同时配置其各自的 set 、get 方法( struts 都是通过反射去获得或设置属性值)以及 execute 方法(不在 struts.xml 配置文件的 action 标签中配置 method 默认会进到对应 class 的 execute 方法),如果 username 、 password 都为 admin 即登录成功返回 welcome.jsp 页面,否则登录失败返回 index.jsp 页面。

207e26cfa94fa53bb2f550eed79625f8.png

图3-4|LoginAction.java 

6、welcome.jsp

de831f92d53927bc172a0b5d61949638.png

图3-5|welcome.jsp 

7、运行

a1d5c587ab1836ac7b6107e8e5f844d2.png

图3-6|payload 

855bafa50243e8808b08e8cb80d3b29a.png

图3-7|submit 提交完成,ognl 表达式解析 

04

分析

首先在进入到 LoginAction 之前,会由 FilterDispatcher 创建一个 ActionProxy 根据 struts.xml 中的配置找到处理该请求的 Action ,接着会执行拦截器栈,在拦截器中初始化值栈,将当前 Action 压入 root 栈,并将参数值压入同名属性的 root 栈中。进入 Action 后根据逻辑返回对应的视图。【这一段真是很不严谨】

这里进入 action 的 execute 方法后,判断用户名、密码是否正确后,设置不正确的用户名密码,重新返回到 index.jsp 页面,接下来会对 index.jsp 文件进行解析。而此时的 username 、 password 已压入栈中,在对表单标签进行解析时,会将其数据取出,若开启了 altSyntax 则进行 ognl 表达式的递归解析。

所以重点看对表单数据进行 ognl 表达式解析的过程。解析 jsp 标签时会调用 ComponentTagSupport 的 doStartTag 及 doEndTag 方法,看到 index.jsp 的 17 行,也就是解析 username 时,可以看到图中的调用链,doEndTag - end - evaluateParams - findValue - translateVariables - findValue - … 最终通过 getValue 获取到 username 在 root 栈中的值:%{1+2} 。

57b07cbc3f1b07f9847e852286ea9ce9.png

图4-1|解析 username 时的调用栈 

获取到值后会返回给 com.opensymphony.xwork2.util.TextParseUtil#translateVariables 中的 o ,而此时的 o 将再次作为表达式传入 translateVariables 方法中进行解析(他的循环条件为 true )。

c12a574f99790cbe22ac2b46c9ca1865.png

图4-2|translateVariables 中 o 的值 

由于形如 %{…} ,故其通过截取字符串再次进入 findValue 方法,进行解析,此时的 var 即为 1+2 。

4690a9fcd1773acb5b29fcc341c6e446.png

图4-3|translateVariables 中截取出表达式 1+2 

进入 findValue 后,最终在 ognl.Ognl#getValue 中得到结果值 3 。然后返回给 translateVariables 中的 o ,最终返回页面。

4ddda250c365d214a168c74cf20d05de.png

图4-4|getValue 方法 

8ebeb18d4bb03a7bcee59f9efe1a547c.png

图4-5|调用栈 

05

修复

官方建议:

升级到 Struts 2.0.9 / XWork 2.0.4

关键代码:

设置最大循环解析次数为 1 次,通过最开始的 %{…} 找到表单的属性值后,不再往下循环解析。不再往下循环解析是通过 pos 变量来控制下次从哪个索引开始检索表达式,当前属性值已经解析一次,pos 值则设置为当前属性值的长度,故解析了一次的表达式不再解析第二次。

9cf2922f03896eb406e6e0a4a2e9ec82.png 6e9063f7521a8d09e86522d02ec5a72c.png

图5-1|translateVariables 中修复代码 

06

疑问

除了 form 标签可以解析 ognl 表达式之外,其他标签是不是也可以解析 ognl 表达式。在业务功能上体现的话就是可由用户输入,且将其用户输入再次呈现的地方。

为什么漏洞修复不能直接在 拦截器 的步骤中将用户输入的 形如 %{} 的表达式直接剔除
  • 这里直接转发到 index.jsp 页面不由 struts2 接收请求,故不能在拦截器层面将其拦截。

  • 提出这个问题是因为觉得这种修复应该在没进入正式业务前做,比如这里的拦截器层面。但是后来细想还是有很多不妥。上面回答的点也有些不妥,因为最开始进入到 LoginAction 是可以进行拦截的。而后面对 index.jsp 中的标签进行解析时出现问题。ojz

a27e3e9f15618821197de83e8dc847cf.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值