原始碼連結
(修改前的原始碼,因為feihong師傅的倉庫已經不在了,這裡使用的是Jeromeyoung師傅的原始碼進行改造)https://github.com/Jeromeyoung/JNDIExploit-1
0x00 JNDI注入工具程式碼結構分析
controllers模組:負責LDAP請求的處理
enum模組:負責儲存各種模板型別名稱,如:反序列化的Gadget、記憶體馬的型別
異常模組:負責處理可能丟擲的異常
反序列化模組:各種反序列化鏈的處理
模板模組:命令執行、回顯、DNS、記憶體馬模板
工具類模組:主要是為了方便而編寫的一些工具類
協議服務及啟動模組:負責LDAP、HTTP協議的具體實現
下面先對啟動及協議服務模組進行說明
com.feihong.ldap.Starter類為整個工具的啟動入口,從命令列接收引數傳參到com.feihong.ldap.utils.Config類
Config類中的@Parameter註解為引數的說明及代表的意思,類似於py中的argparse,命令接收到引數,分別賦值給Config類的幾個私有變數。
然後在之後的HTTPServer與LDAPServer中得到應用
關於LDAP服務端的編寫和Demo可以參考以下連結:https://www.freebuf.com/vuls/253545.html,HTTPServer則是正常的Java Demo編寫即可
第一部分註解的應用
在JNDIEXP中,作者為了可以反射一種型別的類,Controller類,這裡的類起到了尋找特定類的作用,而且每個註解類都給定了uri屬性
在LDAPServer.start()之前,會先透過new Reflections(this.getClass().getPackage().getName())的方法獲取到com.feihong.ldap包下面所有LdapMapping的類,之後將其以鍵值對(TreeMap<String, LdapController>)放入到Map中,以便於後續呼叫
比如com.feihong.ldap.controllers.BasicController類,在Map中的儲存格式就是(basic=>Object BasicController)
根據傳送來的LDAP請求去決定呼叫哪個類,具體透過com.feihong.ldap.processSearchResult
根據工具執行的實際效果,如果我們的LDAP請求為ldap://192.168.85.1:1389/Basic/123
那麼DN為Basic/123,首先根據DN中的開頭字串決定是哪個Controller來處理當前的LDAP的請求
第二部分服務端動態呼叫類
在LDAP呼叫LdapController介面實現類之後(以BasicController為例),會先呼叫process方法,以/為標誌分割,獲取到相應的模組名稱,比如ldap://0.0.0.0:1389/Basic/Command/whoami,那麼第一部分Basic用來指定是BasicController,Command指定執行BasicController下的命令執行模組
如果是命令執行模組
透過com.feihong.ldap.utils.Util的getCmdFromBase方法獲取到執行命令的內容,如果是base編碼的,進行base64解碼後返回內容
將其賦值給params,之後再呼叫BasicController的sendResult方法,如果是command模組,初始化該模組,透過asm碼的方法,這裡為了避免出現類名重複的情況,使用隨機字元命令類名
之後命令執行模組呼叫cache方法
將其儲存在map中,之後開始進行LDAP的步驟
首先LDAP設定好
javaClassName:記錄序列化物件的類名,這樣應用程式就可以確定類資訊,而不必首先反序列化
javaClassNames:關於序列化物件的附加類資訊。
javaCodebase:例項化工廠所需的類定義的位置(HTTP地址)
javaFactory:用於儲存物件工廠的完全限定類的可選屬性(即類名)
一張圖說明LDAP請求的過程
接下來看HTTPServer如何接收HTTP請求並返回
當HTTPServer接收到請求之後,將Cache類中map儲存的類寫入到響應中,這樣避免了class檔案落地的情況。
0x01 去除server console內容
在這個JNDI的注入工具中,會在注入記憶體馬的時候,使用系統輸出語句,在控制檯列印出東西,無關緊要的東西,直接去掉就好。
0x02 冰蠍3.11記憶體馬注入
改造前的filter類,獲取session是透過ServletRequest的方式獲取的
改造之後的filter類,不再使用自寫的classLoader而是直接使用URLClassLoader,同理對其他的元件也進行類似的改造,如:spring的Interceptor,weblogic和jboss的filter
所有回顯使用的是header頭的WWW-Authenticate欄位,而非cmd
0x03 JDK版本匹配及tomcat版本相容解決
利用JDK向下相容的特性,這裡使用JDK6編譯filter class。
tomcat版本相容,根據先知不久之前提出的,tomcat6-9的通用StandardContext獲取方法,獲取到StandardContext,接下來的問題就是解決tomcat版本識別與不同版本間filter注入的問題。
tomcat8以下版本FilterDef、FilterMap都在org.apache.catalina.deploy包裡,tomcat8以上包括8都在org.apache.tomcat.util.descriptor.web裡。
2.識別版本之後,進行不同版本間的filter注入
實現邏輯大概如下:
對各個版本的filter記憶體馬全部轉為位元組碼Base64的編碼格式,透過loadClass的辦法,根據版本注入到記憶體中。(class一律使用JDK1.6編譯)
為了避免相關類載入不到的問題,這裡統一使用反射寫所有的filter注入步驟。
tomcat6789的StandardContext先知上已經給出瞭解決方案:
https://xz.aliyun.com/t/9914
對tomcat特定類進行篩選,來決定FilterDef和FilterMap類載入的使用,最後形成的Demo如下:
getStand的原始碼如下:
經測試,可用於常規的tomcat6、7(某凌)、8、9版本。
當所有的tomcat版本都測試完畢之後,發現這個通用方法對某遠並不適用,原因是致遠這裡的ServerPort會永遠等於-1。
解決方案是使用原版的feihong-jndi將TomcatMemShell2中的filter類替換為自定義的filter類即可。
0x04 spring memShell的改進
在一些版本的測試中,發現原版的JNDIExploit的spring並不適用於所有的spring框架場景,對此做出補充(借鑑自lz2y與microworld師傅的成果)
以ruoyi cms後臺的snake yaml為例,lz2y師傅這裡已經說了具體的原因(https://xz.aliyun.com/t/10651),直接使用師傅的Demo就好,我這裡使用的是Windows的環境,使用比較在Linux和Windows都通用的Demo獲取的WebApplicationContext
使用com.ruoyi.common.utils.spring.SpringUtils在windows ruoyi4.6.0的環境中沒有成功(POC使用的是com.sun.rowset.JdbcRowSetImpl)
使用com.ruoyi.common.utils.spring.SpringUtils在windows ruoyi4.6.0的環境中成功(POC使用的是javax.script.ScriptEngineManager)
剩下的步驟直接注入記憶體馬即可,實現Demo如下:
將其新增到類的引用處,對於spring的框架新增新增類的引用,需要修改的是四個類:
com.feihong.ldap.enumtypes.PayloadType
com.feihong.ldap.utils.Cache
com.feihong.ldap.controllers.BasicController
com.feihong.ldap.controllers.TomcatBypassController
修改Cache類是為了能夠讓惡意類能夠在記憶體中載入,具體的原因已經在0.1中分析完畢,修改BasicController和TomcatBypassController是為了能在LDAP在JDK的高低版本中找到引用。
0x05 jetty memShell的改進
對於jetty記憶體馬的改進,主要是log4j對solr的影響,針對於實戰的場景,參考Qu3een師傅的文章(https://tttang.com/archive/1386/)
實現的Demo如下,直接整合到JNDI注入工具中
修改com.feihong.ldap.enumtypes.PayloadType
修改com.feihong.ldap.utils.Cache
修改com.feihong.ldap.controllers.BasicController
0x06 webSphere memShell的改進
暫缺
0x07 增加resin中介軟體下的記憶體馬注入
基於pen4uin師傅的通用研究成果直接整合到JNDI注入工具中,原理不再贅述,參考pen4uin師傅的公眾號即可,直接給出Demo
同樣的,修改com.feihong.ldap.enumtypes.PayloadType
修改com.feihong.ldap.utils.Cache
修改com.feihong.ldap.controllers.BasicController
0x08 增加tomcatValue記憶體馬
在tomcat容器中,有些場景filter記憶體馬並不如valve記憶體馬,所以為了增加相容性,這裡增加valve記憶體馬,實現的Demo大致如下:
同樣的修改配置檔案,增加到BasicController、TomcatBypassController、Cache、Config中。
參考連結
https://landgrey.me/blog/19/
https://github.com/Mr-xn/JNDIExploit-1
https://tttang.com/archive/1386/
https://mp.weixin.qq.com/s/5s9eyRpaP7WhVd2estwtKg
https://github.com/lz2y/yaml-payload-for-ruoyi
https://xz.aliyun.com/t/10651