漏洞点
生命周期
漏洞位置
触发点
S2-001
影响范围
WebWork 2.1 (with altSyntax enabled)
WebWork 2.2.0 - WebWork 2.2.5
Struts 2.0.0 - Struts 2.0.8
漏洞描述
WebWork2.1+ 和 Struts2 的 altSyntax
特性允许用户将 OGNL 表达式插入到文本中,并对
其递归解析,导致攻击者可以插入并执行任意 OGNL
在下面的示例中,用户可以在 name 参数填入 %{1+1}
而 phoneNumber 参数置空,当表单
验证失败而重新渲染页面时,会对 name 的值进行解析。正常情况是 %{name}
, 让用户可以
不用重新填写 name,但由于攻击者填入的是 OGNL 表达式,服务端会递归解析 %{%{1+1}}
导致漏洞产生,返回计算结果 2
S2-003
影响范围
Struts 2.0.0 - Struts 2.1.8.1
漏洞描述
Struts2 提供了广泛的 OGNL 表达式计算能力,甚至可以将参数名当作表达式来执行,而在
OGNL 中可以通过 # 来访问 struts 的对象,所以内置的 ParametersInterceptor 类会过
滤参数名中包含 # 在内的各种特殊符号来保证安全性
而 S2-003 就是通过 unicode 编码绕过 ParametersInterceptor 类中的正则过滤,用
\u0023
或者 \43
代替 #
poc:
S2-005
影响范围
Struts 2.0.0 - Struts 2.1.8.1
漏洞描述
该漏洞是对 S2-003 补丁的绕过,官方对 S2-003 的修复代码如下(左为 struts-2.0.8,右
为 struts-2.0.12):
可见修复代码主要引入控制静态方法调用开关 allowStaticMethodAccess 变量,以及用于
控制成员变量访问权限的 SecurityMemberAccess 类对象,然而这两个变量都可以通过
OGNL 表达式控制,所以补丁并没有实际效果
poc:
S2-007
影响范围
Struts 2.0.0 - Struts 2.2.3
漏洞描述
当配置了验证规则,且类型转换出错时,ConversionErrorInterceptor 类对产生错误的参
数值进行了不安全的字符串拼接,而造成了 OGNL 表达式注入
漏洞的入口类是
S2-007/web/WEB-INF/lib/xwork-core-2.2.3.jar!/com/opensymphony/xwork2/interceptor/ConversionErrorInterceptor.class
当发生类型转换错误时进入 ConversionErrorInterceptor#intercept 方法,在该方法中获
取导致错误的参数值,并在 getOverrideExpr 方法进行拼接
可见攻击者可以闭合单引号注入 OGNL 表达式,使其返回值的形式为: '' + (#xxxx) + ''
, 最后导致中间的表达式执行
S2-008
影响范围
Struts 2.0.0 - Struts 2.3.17
漏洞描述
为了避免攻击者通过参数执行任意方法,xwork.MethodAccessor.denyMethodExecution 参
数的值默认为 true,且 SecurityMemberAccess.allowStaticMethodAccess 参数值默认为
false。而且从 Struts 2.2.1.1 开始,ParameterInterceptor 只允许以下格式的参数:
但是在以下情况仍可以绕过上述限制执行任意 Java 代码:
Struts ⇐ 2.2.3 (ExceptionDelegator)
当 Struts 将一个参数值赋值给属性而产生异常时,该值会被作为 OGNL 表达式执行,比如
将一个字符串值赋值给一个整型属性
Struts ⇐ 2.3.1 (CookieInterceptor)
Struts 的白名单机制只应用在请求参数上,但 CookieInterceptor 中并没有使用,所以在
处理 cookie 项时可能导致 OGNL 注入,而且攻击者可以通过表达式将
allowStaticMethodAccess 设置为 true
大多 Web 容器(如 Tomcat)对 Cookie 名称都有字符限制,一些关键字符无法使用使得这
个点比较鸡肋
Arbitrary File Overwrite in Struts ⇐ 2.3.1 (ParameterInterceptor)
虽然 allowStaticMethodAccess 默认是关闭的,但攻击者仍然可以访问只有一个 String 参
数的公共构造方法和 setter 方法,所以仍然可能导致任意文件写入、覆盖
Struts ⇐ 2.3.17 (DebuggingInterceptor)
应用以开发者模式(developer mode)运行时 DebuggingInterceptor 类可能导致任意代码
执行,不过一般生产环境都不会开启 developer mode,除非是被人放的后门
poc:
以下是 DebuggingInterceptor#intercept 方法的关键部分,当 debug 参数是 command 时,
会取出 expression 参数,然后调用 OgnlValueStack#findValue
最后把参数名当作 OGNL 表达式进行解析,调用栈如下:
S2-009
影响范围
Struts 2.0.0 - Struts 2.3.1.1
漏洞描述
Struts 使用白名单解决了 S2-003 和 S2-005 从参数名注入 OGNL 表达式的情况,使用的
正则如下:
这个漏洞的成因是:类似 top['foo'](0)
这样格式的参数名可以通过校验,而 Struts 会
把它解析成 (top['foo'])(0)
, 也就是把 top['foo']
当作表达式执行,这可以从上下文获
取 foo 变量的值
因此可以通过两个参数来绕过白名单:第一个参数的值写入恶意表达式,因为
ParametersInterceptor 只会校验参数名,所以参数值可以写入任意表达式;然后第二个参
数名解析的时候获取这个恶意表达式并执行
poc:
(注意参数会根据首字母进行排序,要保证第一个参数的首字母排在前面)
S2-012
影响范围
Struts Showcase App 2.0.0 - Struts Showcase App 2.3.14.2
漏洞描述
在之前的漏洞中,我们都是在请求参数名中插入 OGNL 表达式令其执行,而 struts 也相应
地增加了白名单机制来过滤恶意参数名。但参数值还是可以写入任意内容的,只要找到条件
让 struts 把参数值当作 OGNL 表达式来解析,就可以绕过之前的防护
012 漏洞的原因即在重定向时,从内存获取我们之前输入的参数值,这时会将它当作 OGNL
表达式执行
vulhub 示例:
当 UserAction 发生重定向时,会将 name 的值作为重定向页面的参数,导致表达式执行
poc:
S2-013
影响范围
Struts 2.0.0 - Struts 2.3.14.1
漏洞描述
<s:url
和 <s:a
两个标签用于生成 anchor,而且它们有一个 includeParams 属性可以把
当前页面的参数添加到 anchor 的 href 中,includeParams 的可选值如下:
- none: 不包含原请求参数
- get: 只包含 get 请求参数
- all: 包含 get 请求和 post 请求参数
例子:
当用户访问 link.action 并携带请求参数时,会在页面中生成两个 <a> 元素,并且 href 包
含当前请求的参数。而 struts 在获取原请求参数时,把值当作 OGNL 表达式执行了,因此
导致漏洞产生
我们分析一下 struts2-core-2.2.3.jar!/org/apache/struts2/components/Anchor.class
的执行过程,关键方法如下:
首先是在 beforeRenderUrl 方法对 includeParams 属性进行判断,根据属性从
urlComponent 可以拿到需要的参数值(文本值,还没执行表达式):
然后在 renderUrl 中解析得到最终的 href, 在这个过程中执行了参数值中的表达式,调用
栈如下:
官方的修复方式是限制 %{(#exp)}
格式的 OGNL 执行,但还存在 %{exp}
格式可用,导致
补丁被绕过(S2-014)
S2-015
影响范围
Struts 2.0.0 - Struts 2.3.14.2
漏洞描述
S2-015 包含两种情况产生的漏洞
通配符
首先是 Struts2 在匹配 Action 的时候可以使用通配符,当找不到所请求的 Action 名称时,
就会访问使用通配符的 Action
示例:
{1}
是访问的 Action 名称,而且会作为 OGNL 表达式执行。假设用户访问 null.action,
就会匹配到上述配置,然后返回 /example/null.jsp 页面
另外,在 Struts 2.3.14.1 - Struts 2.3.14.2 的更新内容中,删除了
SecurityMemberAccess 类中的 setAllowStaticMethodAccess 方法,因此在 2.3.14.2 版
本以后都不能直接通过 #_memberAccess['allowStaticMethodAccess']=true
来修改其值达
到重获静态方法调用的能力,但是仍可以通过反射去修改
最终得到以下 payload:
注意,由于 payload 的位置,命令中带有斜杠可能执行失败(cat /etc/passwd)
表达式二次执行
vulhub 的示例代码如下:
在 ParamAction 的响应中,会获取请求中的 message 参数放到 fxxk 头部,首先执行
${message}
获取 Action 中的 message 属性,如果该属性的值又是 OGNL 表达式,则会进
行二次执行。所以 payload 的格式为 %{expression}
S2-016
影响范围
Struts 2.0.0 - Struts 2.3.15
漏洞描述
DefaultActionMapper 类支持以”action:”、“redirect:”、“redirectAction:“作为导航或是
重定向前缀,但是这些前缀后面同时可以跟 OGNL 表达式,由于 struts2 没有对这些前缀做过
滤,导致利用 OGNL 表达式调用 java 静态方法执行任意系统命令
poc 和 S2-015 一样,加在重定向前缀后面即可
S2-019
影响范围
Struts 2.0.0 - Struts 2.3.15.1
漏洞描述
该漏洞的原因是 Struts 默认开启动态方法调用(DMI)特性,该特性在 Struts 2.3.15.2 后
默认关闭,你也可以自己手动关闭:
poc:
S2-029
影响范围
Struts 2.0.0 - Struts 2.3.24.1 (except 2.3.20.3)
漏洞描述
Struts2 在解析某些标签时,会对标签的属性值进行二次执行求值,那如果第一次获取到的
值是一个 OGNL 表达式,就会导致表达式执行
比如 i18n 和 text 标签的 name 属性:
攻击者可以在请求的 lan 属性中注入表达式,poc:
注入的位置取决于标签的属性从什么地方获取
S2-032
影响范围
Struts 2.3.20 - Struts Struts 2.3.28 (except 2.3.20.3 and 2.3.24.3)
漏洞描述
在开启了动态方法调用(Dynamic Method Invocation)的情况下,可以使用 method:<name>
的方式来调用名字是 <name>
的方法,而这个方法名将会进行 OGNL 表达式计算,导致远程命
令执行漏洞
poc:
S2-045
影响范围
Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10
漏洞描述
Struts2 默认处理 multipart 报文的解析器是 jakarta,如果 Content-Type 的值不合法它会
将异常信息返回,并且将 Content-Type 的值作为 OGNL 表达式执行
首先是在 org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest 这个类,
它在调用 parse 方法发生异常时,调用父类 AbstractMultiPartRequest 的
buildErrorMessage 方法生成包含 payload 的错误信息,并添加到当前的 request 中
在 FileUploadInterceptor 调用 LocalizedTextUtil#findText 方法获取错误信息,在这
个过程会进入到 OgnlTextParser#evaluate 方法,执行 $ 或者 % 开头的表达式
调用栈:
在 FileItemIteratorImpl 对象的构造方法中,首先对 contentType 进行了判断,要求
contentType 字符以 multipart/
开头:
而在 Dispatcher#wrapRequest 方法中,只要 contentType 包含 multipart/form-data 就会
认为是 multipart 请求。所以可以构造以下 poc(from vulhub):
S2-046
影响范围
Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10
漏洞描述
与 s2-045 类似,但是输入点在文件上传的 filename 值位置,并需要使用 \x00
截断
poc from vulhub: