1. 程式人生 > >Java安全之Weblogic 2016-0638分析

Java安全之Weblogic 2016-0638分析

# Java安全之Weblogic 2016-0638分析 文章首發先知:[Java安全之Weblogic 2016-0638分析](https://xz.aliyun.com/t/8701) ## 0x00 前言 續上篇文的初探weblogic的T3協議漏洞,再談CVE-2016-0638, CVE-2016-0638是基於 CVE-2015-4852漏洞的一個繞過。 [Java安全之初探weblogic T3協議漏洞](https://www.cnblogs.com/nice0e3/p/14141925.html) ## 0x01 環境搭建 ### 補丁環境搭建 這裡採用上次的weblogic環境,但是在這裡還需要打一個補丁包,來修復 CVE-2015-4852漏洞後,對該漏洞進行一個繞過。 CVE-2015-4852的修復補丁為`p21984589_1036_Generic`,由於在網際網路上並沒有找到該補丁包,只能通過官網下載,官網下載需要購買對應的服務,所以在這裡找了`p20780171_1036_Generic`和`p22248372_1036012_Generic`這兩個補丁包,`p21984589_1036_Generic`是前面這兩個補丁包的整合。 因為前面搭建是docker的環境,需要將這兩個補丁包上傳到docker映象裡面去,然後進行安裝。 命令整理: ```bash docker cp ../p20780171_1036_Generic weblogic1036jdk7u21:/p20780171_1036_Generic docker cp ../p22248372_1036012_Generic weblogic1036jdk7u21:/p22248372_1036012_Generic docker exec -it weblogic1036jdk7u21 /bin/bash cd /u01/app/oracle/middleware/utils/bsu mkdir cache_dir vi bsu.sh 編輯MEM_ARGS引數為1024 cp /p20780171_1036_Generic/* cache_dir/ ./bsu.sh -install -patch_download_dir=/u01/app/oracle/middleware/utils/bsu/cache_dir/ -patchlist=EJUW -prod_dir=/u01/app/oracle/middleware/wlserver/ cp /p22248372_1036012_Generic/* cache_dir/ ./bsu.sh -install -patch_download_dir=/u01/app/oracle/middleware/utils/bsu/cache_dir/ -patchlist=ZLNA -prod_dir=/u01/app/oracle/middleware/wlserver/ –verbose ``` ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150402098-624695630.png) 重啟weblogic服務。 ```bash /u01/app/oracle/Domains/ExampleSilentWTDomain/bin/startWebLogic.sh ``` ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150415528-1376660003.png) 這裡看到weblogic 2015-4852的payload打過去,並沒有像以往一樣,建立一個檔案。那麼就說明補丁已經打上了,已經能夠修復該漏洞了。這裡是切換了JDK7u21和cc1的利用鏈依舊沒打成功。 ### 遠端除錯 接下來還是需要將裡面的依賴包給拷一下。 ```bash mkdir wlserver1036 mkdir coherence_3.7 docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/modules ./wlserver1036 docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/wlserver/server/lib ./wlserver1036 docker cp weblogic1036jdk7u21:/u01/app/oracle/middleware/coherence_3.7/lib ./coherence_3.7/lib ``` 下面來對該補丁進行一個繞過。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150425150-830002124.png) ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150432819-473435038.png) ## 0x02 補丁分析 補丁作用位置: ```java weblogic.rjvm.InboundMsgAbbrev.class :: ServerChannelInputStream weblogic.rjvm.MsgAbbrevInputStream.class weblogic.iiop.Utils.class ``` 在分析漏洞前,先來看到一下,上一個漏洞點的補丁是怎麼進行修復的。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150444619-1821093805.png) 在這其實看到該`resolveClass`方法的位置,前面加多一個判斷。 前面判斷className是否為空,ClassName的長度是否為零,但是重點是`ClassFilter.isBlackListed`方法。 這裡先打一個 CVE-2015-4852 exp過來,在該位置打個斷點,跟蹤進該方法,檢視怎麼進行防護。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150454064-1669720310.png) 跟進進來後,先別急著看後面的,因為下面還有一個靜態程式碼塊,靜態程式碼塊中程式碼優先執行,需要先來檢視靜態程式碼塊內容。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150501746-1980597488.png) 這裡前面有兩個判斷,判斷中都呼叫了兩個方法,來看看這兩個方法的實現。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150511583-288146640.png) 一個是判斷是否為`weblogic.rmi.disableblacklist`,一個是判斷是否為`weblogic.rmi.disabledefaultblacklist`,寫法有點奇怪,可能是因為是class檔案的緣故。 這兩個判斷為true的話,就會執行來到下一步呼叫`updateBlackList`將後面的一系列黑名單的類傳入到裡面去。 `updateBlackList`該方法從名字得知,就是一個黑名單列表新增的一個方法,將黑名單內容新增到一個`HashSet`裡面去。檢視具體實現。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150522579-1884857781.png) StringTokenizer 構造方法:為指定的字串構造一個字串tokenizer。 hasMoreTokens方法:返回與 `hasMoreTokens`方法相同的值。 nextToken 方法:返回此字串tokenizer字串中的下一個令牌。 總體的來理解就是構造一個字串,然後遍歷裡面的值,然後呼叫`processToken`方法將該值傳遞進去。 再來看到`processToken`方法。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150530508-559722570.png) 裡面判斷如果開頭是`+`號,則擷取第一位後面的值新增到黑名單的這個HashSet裡面去。如果是`-`號則移除,如果開頭不是前面的`+` `-`號則直接新增到黑名單裡面去。 到這裡靜態程式碼塊就已經分析完成了,總的來說其實就是將一些危險的類,新增到了黑名單裡的一個步驟。 黑名單列表為: ```java +org.apache.commons.collections.functors, +com.sun.org.apache.xalan.internal.xsltc.trax, +javassist,+org.codehaus.groovy.runtime.ConvertedClosure, +org.codehaus.groovy.runtime.ConversionHandler, +org.codehaus.groovy.runtime.MethodClosure ``` 返回剛剛的`ClassFilter.isBlackListed`方法進行跟蹤 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150541191-2011934440.png) ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150548379-2037477586.png) 最後這裡呼叫了`contains`方法判斷 這個pkgName存不存在黑名單中,存在的話這裡返回true。 返回到`resolveClass`方法可以看到這裡為true,就會直接拋異常。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150555955-1097295450.png) 如果不存在於黑名單中,會來到else這個分支的程式碼塊中呼叫父類的`resolveClass`方法。 而這一個點,只是過濾的一個點,下面來看看過濾的點都有哪些。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150604531-754542370.png) 再來看下一個點`MsgAbbrevInputStream`的位置 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150616970-649446263.png) 這裡也是呼叫`ClassFilter.isBlackListed`方法進行過濾,和前面的是一樣的。以此類推。 ## 0x03 工具分析 在CVE-2016-0638裡面用到了weblogic_cmd工具,[github地址](https://github.com/5up3rc/weblogic_cmd)。 下面來看看該工具的實現,再談漏洞的繞過方式。 下載該原始碼後,匯入IDEA中,配置命令引數。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150624980-1133813739.png) 這裡如果報錯找不到sun.tools.asm包的話,需要將Tools.jar包手動新增一下。在這我是使用jdk1.6進行執行的,使用1.8版本會找不到`sun.org.mozilla.javascript.internal.DefiningClassLoader`類 在Main的類中打個斷點進行執行。 前面都是程式碼都是進行一個配置,這裡斷點選擇落在該方法中。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150633390-645366341.png) 選擇跟蹤 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150641749-509912272.png) 繼續跟蹤`WebLogicOperation.blindExecute`方法。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150651304-332661302.png) 前面判斷了伺服器型別,重點在`SerialDataGenerator.serialBlindDatas`方法中,payload由該方法進行生成。跟進檢視一下該方法如何生成payload。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150706939-1441276210.png) 在這先選擇跟蹤`blindExecutePayloadTransformerChain`方法。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150714491-136345528.png) 在這裡又看到了熟悉的面孔,CC鏈的部分程式碼。 回到剛剛的地方,跟蹤`serialData`方法 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150731748-421622329.png) ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150740069-467633337.png) 在這裡就看到了CC鏈後面的一段程式碼,這組合成了一條CC1利用鏈。但是在後面呼叫了`BypassPayloadSelector.selectBypass`方法來處理在原生的利用鏈中本該直接進行序列化的物件。 跟進該方法進行檢視。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150748375-2087745190.png) 這裡面還會去呼叫`Serializables.serialize`,依舊先跟蹤最裡層的方法。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150757429-694594215.png) 這傳入一個obj物件和out物件,進行了序列化操作。然後將序列化後的資料寫到out物件中。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150806018-423008002.png) 執行完成後,返回上一個點,剛才分析得知返回的是序列化後的資料。所以在處呼叫`streamMessageImpl`方法傳遞的引數也是序列化的資料。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150813822-679718290.png) 跟蹤檢視。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150822026-1241803551.png) 內部是new了一個`weblogic.jms.common.StreamMessageImpl`的例項,然後呼叫`setDataBuffer`方法將序列化後的物件和序列化後的長度傳遞進去。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150835499-461419300.png) 執行完這步後,回到這個地方 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150845499-2056453635.png) 後面的這個方法是進行序列化操作的,這裡又對 `streamMessageImpl`的例項物件進行了一次序列化。該方法在前面檢視過了,這裡就不跟進去看了。 而最後來到了這裡。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150854540-567583629.png) 而後面這個方法就是構造特定的資料包,使用T3協議傳送payload。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150903828-1572330843.png) ## 0x04 漏洞分析 那麼如果需要繞過的話,我們需要找一個類,他的類在內部的`readObject`方法建立了自己的`InputStream`的物件,但是又不能為黑名單裡面過濾掉的`ServerChannelInputStream`和`MsgAbbrevInputStream`裡面的`readObject`方法。然後呼叫該`readObject` 方法進行反序列化,這時候就可以達成一個繞過的效果。 在師傅們的挖掘中尋找到了`weblogic.jms.common.StreamMessageImpl#readExternal()`,`StreamMessageImpl`類中的`readExternal`方法可以接收序列化資料作為引數,而當`StreamMessageImpl`類的`readExternal`執行時,會反序列化傳入的引數並呼叫該引數反序列化後對應類的這個`readObject`方法。 繞過原理如下: ```java 將反序列化的物件封裝進了 StreamMessageImpl,然後再對 StreamMessageImpl 進行序列化,生成 payload 位元組碼。反序列化時 StreamMessageImpl 不在 WebLogic 黑名單裡,可正常反序列化,在反序列化時 StreamMessageImpl 物件呼叫 readObject 時對 StreamMessageImpl 封裝的序列化物件再次反序列化,這樣就逃過了黑名單的檢查。 ``` 在此先再來思考一個問題,`weblogic.jms.common.StreamMessageImpl#readExternal()`該方法是怎麼被呼叫的呢?在前面分析原生`readObject`方法的時候發現,其實`readObject`方法的底層還會去呼叫很多其他方法。 在Weblogic從流量中的序列化類位元組段通過readClassDesc-readNonProxyDesc-resolveClass獲取到普通類序列化資料的類物件後,程式依次嘗試呼叫類物件中的readObject、readResolve、readExternal等方法。而在這裡`readExternal`就會被呼叫。 那麼下面來除錯分析一下該漏洞。 還是先在`weblogic.rjvm.InboundMsgAbbrev#ServerChannelInputStream.resolveClass`地方打個斷點,然後使用weblogic_cmd工具打一個payload過去,先來檢視一下傳輸過來的資料。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150914089-302309563.png) 這裡可以看到獲取到的ClassName是`weblogic.jms.common.StreamMessageImpl`的物件,而不在再是`AnnotationInvocationHandler`物件。`StreamMessageImpl`不在黑名單中,這裡的判斷不會進行拋異常。 下個斷點直接落在`StreamMessageImpl.readExternal`中跟蹤一下。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150923428-1396297320.png) 看到呼叫棧這裡就應驗了我們前面所提到的關於`StreamMessageImpl.readExternal`呼叫問題。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150933539-105314804.png) 這裡的var4為正常反序列化後的資料,而後面會new一個`ObjectInputStream`類傳遞var4引數進去。然後再呼叫`readObject`方法 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150944348-608462008.png) 執行完這一步後,命令就已經執行成功。後面的是對CC鏈執行命令的一個基本認知,在此不做贅述。 ![](https://img2020.cnblogs.com/blog/1993669/202012/1993669-20201223150954667-1372989662.png) ### 參考文章 ``` https://xz.aliyun.com/t/8443#toc-6 https://www.anquanke.com/post/id/224343#h3-6 ``` ## 0x05 結尾 其實摸清楚補丁的一個套路過後,再去基於補丁分析後面的漏洞會比較清晰。因為補丁無非就是再從ClassFilter裡面新增黑名單列表,這也是為什麼weblogic修修補補又爆洞的原因,並沒有從根本原因去進行