<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>

    Tomcat

    Apache Tomcat

    tomcat

    Apache Tomcat軟件是Java ServletJavaServer PagesJava Expression LanguageJava WebSocket 技術的開源實現。Tomcat由于其自身簡單、穩定、開源等特征,在中小型系統和并發量小的場景下被普遍使用,有極大的用戶使用量。

    Tomcat 口令爆破

    Tomcat在默認情況下提供了一些管理后臺,不同的管理后臺提供了不同的功能,這些管理后臺使用了Basic認證的方式進行權限校驗,如果暴露在互聯網上,將存在遭到暴力破解的安全風險。

    其中主要包含兩種:Manager以及Host Manager

    Manager APP

    在生產環境中,為了實現部署新的Web應用程序或取消部署現有的程序而不必重啟容器、動態更新程序代碼、列出一些JVM或操作系統的屬性值、列出應用程序或會話等等基礎功能,Tomcat默認包括了一個Web應用程序,通常位于$CATALINA_BASE/webapps/manager目錄下:

    image-20200924160215953

    并且這個應用程序默認的context path也為/manager,訪問應用的方式為/manager/html

    對于這個APP的詳細介紹,可以在官方文檔找到,以Tomcat 7.0為例,文檔的位置為:https://tomcat.apache.org/tomcat-7.0-doc/manager-howto.html

    我們查看manager應用下的web.xml,發現其中配置了如下servlet-mapping:

    image-20200924163251524

    由此我們可以知道:

    • /manager/html/:提供HTML格式的管理頁面
    • /manager/status/:提供服務器狀態(Server Status)頁面
    • /manager/jmxproxy/:提供JMX proxy 接口
    • /manager/text/:提供純文本頁面

    由于Tomcat Manager提供的這些功能是需要管理人員才能使用和查看的,因此如果默認開啟的話將會存在很高的安全問題,所以Tomcat并沒有直接提供這些功能。

    如果想要使用這些功能,則需要在$CATALINA_BASE/conf/tomcat-users.xml中配置相關的用戶信息,包括用戶名、密碼、用戶角色,來對使用這些功能的用戶進行身份鑒別和權限驗證。

    image-20200924164035213

    在 manager 項目中的web.xml中我們可以看到能夠使用的這些角色:

    image-20200924164629373

    他們對應的權限分別為:

    • manager-gui:能夠訪問/manager/html/的管理界面。
    • manager-script:能夠訪問 /manager/text/ 以及/manager/status/界面。
    • manager-jmx:能夠訪問/manager/jmxproxy/ 以及/manager/status/界面。
    • manager-status:能夠訪問 /manager/status/的Server Status界面。

    在正確的配置了用戶身份信息和角色后,就能夠訪問相應的頁面了。以本篇作者的環境為例,演示如下:

    Server Status頁面:

    image-20200924173046922

    html管理后臺:

    image-20200924173214026

    純文本頁面-使用指令list:

    image-20200924173454058

    JMX Proxy頁面:

    image-20200924173652947

    對于這幾個Servlet功能的實現如果感興趣,可以查看在org.apache.catalina.manager 目錄下的代碼,在此章節將不過多關注。

    image-20200924174502237

    Host Manager

    顧名思義,此管理后臺用于管理運行在Tomcat上的虛擬主機,文件路徑位置為$CATALINA_BASE/webapps/manager,context path為/host-manager

    詳細介紹可以在官方文檔找到,以Tomcat 7.0為例,文檔的位置為:https://tomcat.apache.org/tomcat-7.0-doc/host-manager-howto.html

    servlet-mapping以及用戶權限均有兩個:

    image-20200924175829141

    配置方式同 manager,不再重復,以下為頁面示例:

    html頁面管理后臺:

    image-20200924180115463

    純文本頁面-使用指令list:

    image-20200924180243681

    暴力破解

    剛才提到的這些應用之所以采用Basic認證進行身份驗證,原因是在應用配置、Tomcat配置等文件中進行了如下配置:

    • 認證的方式(BASIC、DIGEST、FORM、SSL等)

    image-20200924181221421

    • 通過 security-constraint 配置需要鑒權的訪問路徑

    image-20200924183406405

    • 用戶的身份信息(賬戶、密碼)、權限(角色)

    image-20200924183627781

    • 默認的域(Realm)配置,Tomcat的server.xml中默認配置 UserDatabaseRealm,它從配置的全局資源 conf/tomcat-users.xml 中提取用戶信息,在Tomcat 7.0及以上版本中,還提供了LockOutRealm 的組合域,用來阻止短時間內多次登錄失敗的情況。

    image-20200924183800370

    在經過了如上配置后,再訪問這些管理后臺將需要進行Basic認證。

    弱口令

    雖然Tomcat沒有提供默認用戶,但是在配置文件中含有注釋了的配置樣例,其中包括的用戶名密碼:

    tomcat:tomcat
    both:tomcat
    role1:tomcat

    有的管理員可能會取消注釋直接使用這些默認配置的賬戶密碼,因此可以將其當做Tomcat的默認口令

    除此之外,還有的管理員習慣于使用常用的用戶名及密碼,如:

    admin:admin
    admin:123456
    root:root
    manager:manager:
    admin:admin888
    ...

    以上僅僅是列出一小部分作為示例,實際上還有很多種類的弱口令。一旦弱口令被攻擊者猜解到,攻擊者能夠輕易的獲取一些用戶的權限,大大降低了攻擊成本。

    暴破

    Basic認證是一種較為簡單的HTTP認證方式,客戶端通過明文(Base64編碼格式)傳輸用戶名和密碼到服務端進行認證。

    客戶端攜帶數據格式為:

    Authorization: Basic dG9tY2F0OjEyMzEyMw==

    服務端會根據用戶是否登陸成功返回 200 或 401 等不同的狀態碼,攻擊者可以自行組織字典進行暴力破解攻擊。

    暴力破解攜帶的賬戶密碼可能是弱口令,也可能是撞庫攻擊,還可能是根據站點、管理員自身信息生成的字典等,具有更高的成功率。

    如下,使用 Burpsuite的 Intruder 模塊對 Tomcat 6.0 的 /manager/html 路徑的基礎認證進行爆破:

    image-20200925105747025

    限制

    剛才提到了,在Tomcat 7以上配置文件默認添加了LockOutRealm,首先我們看一下 LockOutRealm的邏輯,代碼位于org.apache.catalina.realm.LockOutRealm。類里的字段很明了,無需解釋。

    image-20200924190438021

    authenticate 方法中進行身份驗證,如果用戶登陸失敗,將調用 registerAuthFailure 方法標記用戶的登錄失敗狀態

    image-20200924190057039

    這段代碼我貼一下:

        private void registerAuthFailure(String username) {
            LockOutRealm.LockRecord lockRecord = null;
            synchronized(this) {
                if (!this.failedUsers.containsKey(username)) {
                    lockRecord = new LockOutRealm.LockRecord();
                    this.failedUsers.put(username, lockRecord);
                } else {
                    lockRecord = (LockOutRealm.LockRecord)this.failedUsers.get(username);
                    if (lockRecord.getFailures() >= this.failureCount && (System.currentTimeMillis() - lockRecord.getLastFailureTime()) / 1000L > (long)this.lockOutTime) {
                        lockRecord.setFailures(0);
                    }
                }
            }
    
            lockRecord.registerFailure();
        }

    重點的判斷邏輯是:

    如果用戶的登錄失敗次數>=5次,并且,(當前時間-上次登錄失敗的時間)>300s,將會將用戶登錄失敗的次數重新設置為0。

    函數最后一行是內部類的方法,將 failures += 1,并將 lastFailureTime置為當前時間:

    image-20200924190259639

    由此可知,在5分鐘之內同一賬戶登陸失敗5次以上,LockOutRealm 將會封鎖用戶,在未來5分鐘之內沒有新的登陸失敗的情況,會從0開始重新計數,因此這種方式是能夠一定程度緩解系統受到的暴力破解攻擊的。

    Tomcat AJP 協議

    AJP(Apache JServer Protocol) 協議最初是由 Gal Shachor 設計。對于Web服務器與Servlet容器通信來講,最主要目的是:

    • 提高性能(主要是速度)。
    • 添加對SSL的支持。

    目前Tomcat中使用的版本均為AJP1.3,簡稱為ajp13。ajp13協議是面向數據包的。出于對性能的考慮,選擇了以二進制格式傳輸,而不是更易讀的純文本。

    Web服務器通過TCP連接與servlet容器進行通信。為了減少昂貴的套接字(socket)創建過程,web服務器將嘗試保持與servlet容器的持久的TCP連接,并為多個請求/響應周期重復使用一個連接。

    官方文檔位置:https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html

    在Tomcat的server.xml 中默認配置了兩種連接器:

    image-20200925172240547

    一種是使用的HTTP Connector,監聽8080端口,還有一個AJP Connector,監聽了8009端口。在Tomcat中這個協議的監聽的一直都是默認開啟的。

    AJP Connector 的配置文檔:http://tomcat.apache.org/tomcat-6.0-doc/config/ajp.html

    AJP Connector 可以通過 AJP 協議和另一個 web 容器進行交互。例如正常情況下的訪問是由客戶端通過HTTP協議訪問到Tomcat服務器返回結果,此時將由HTTP Connector處理請求,但也可以使用通過AJP協議來進行訪問,此時將由AJP Connector來處理。

    但是通常情況下用戶的客戶端并不會支持AJP協議,因此想要使用AJP協議進行訪問,需要自己實現連接器,或由中間的代理服務器進行轉發。

    • Apache HTTP Server 2.x 上的啟用 AJP 的 mod_proxy 模塊(在 2.2 上已成為默認配置模塊)
    • 其他任何支持 JK 1.2.x 的服務器

    AJP 協議配置

    測試環境:

    • 操作系統:CentOS 7
    • Web服務器:Apache/2.4.6
    • JSP服務器:Tomcat 9.0.27
    • JDK:1.8.0_251

    mod_jk

    Mod_JK是Apache的一個模塊,其通過AJP協議實現Apache與Tomcat之間的通訊

    官網地址:http://tomcat.apache.org/download-connectors.cgi

    使用手冊:http://tomcat.apache.org/connectors-doc/webserver_howto/apache.html

    由于配置較 mod_proxy_ajp 復雜,此處不進行演示,有興趣的朋友可以按照官方文檔自行嘗試。

    mod_proxy_ajp

    首先在 /conf/httpd.conf 中添加模塊:

    LoadModule proxy_ajp_module modules/mod_proxy_ajp.so

    在虛擬主機中設置代理轉發

    <VirtualHost *:81>
        ProxyPass / ajp://localhost:8009/
        ProxyPassReverse / ajp://localhost:8009/
    </VirtualHost>

    正常啟動Tomcat,把我們命令執行的 test.jsp 放在ROOT中,使用8080端口正常以HTTP協議直接訪問項目:

    image-20200927182058312

    使用 apache 監聽的 81 端口進行 AJP 協議轉發也可以正常訪問:

    image-20200927182041920

    AJP協議數據包處理

    在看AJP協議數據包處理之前,先來了解一下Tomcat處理一個請求的過程。大致如下流程:

    image-20201013113437489

    一次請求的處理可以劃分為Connector及Container進行處理,經歷的過程大致如下:

    • 一個TCP/IP數據包發送到目標服務器,被監聽此端口的Tomcat獲取到。
    • 處理這個Socket網絡連接,使用Processor解析及包裝成request和response對象,并傳遞給下一步處理。
    • Engine來處理接下來的動作,匹配虛擬主機Host、上下文Context、Mapping Table中的servlet。
    • Servlet調用相應的方法(service/doGet/doPost…)進行處理,并將結果逐級返回。

    而對于使用HTTP協議或AJP協議進行訪問的請求來講,在解析包裝成為request和response對象之后的流程都是一樣的,主要的區別就是對socket流量的處理以及使用Processor進行解析的過程的不同。

    提供這部分功能的接口為 org.apache.coyote.Processor<S> ,主要負責請求的預處理。并通過它將請求轉發給Adapter,針對不用的協議則具有不同的實現類。

    這個接口里定義了一些重要的方法:

    image-20201013120233058

    這里主要還是針對于HTTP協議和AJP協議,抽象類AbstractProcessorLight及其子類AbstractProcessor還是對共有特性的封裝。

    image-20201013120649828

    AbstractProcessor具有三個子類,AjpProcessor 用來處理AJP協議,Http11Processor 用來處理HTTP/1.1,StreamProcessor用來處理HTTP/2,我們先來看看針對平常使用的HTTP協議的處理。

    Http11Processor 重點的process()方法,使用service()方法來處理標準HTTP請求,這里我們重點看一下:

    解析請求行和請求頭部分:

    image-20201013150323668

    在Tomcat 8.5 之后,加入了判斷是否需要HTTP協議升級:

    image-20201013145153619

    調用prepareRequest(),將相關信息放入Http11InputBuffer對象中

    image-20201013150607226

    然后調用Adapter將請求交給Container處理:

    image-20201013145055762

    然后接下來是一些收尾工作。在了解了這個過程后,我們再來看一下 AjpProcessorservice()方法,大體上是一致的流程,只是具體的細節不同,首先是一些解析數據包讀取字節的操作,這里不是重點,暫且不提,然后也是調用 prepareRequest() 方法進行預處理:

    image-20201013155203542

    處理之后同樣的調用Adapter將請求交給Container處理

    image-20201014141337514

    而AJP協議的任意文件讀取/任意文件包含漏洞,則出現在上面提到的 prepareRequest() 方法中。

    AJP漏洞

    AjpProcessorprepareRequest() 中,惡意攻擊者可通過控制請求內容,為request對象任意的設置屬性。

    switch/case 判斷中,當attributeCode=10 時,將調用 request.setAttribute 方法存入。

    image-20201013155520803

    所以在此攻擊者擁有了可控的點,這個點該如何利用呢?

    DefaultServlet

    $CATALINA_BASE/conf/web.xml 中默認配置了如下內容:

    image-20201013165533770

    可以看到這是一個默認的Servlet,這個 DefaultServlet 服務于全部應用,當客戶端請求不能匹配其他所有Servlet時,將由此Servlet處理,主要用來處理靜態資源。使用 serveResource() 方法提供資源文件內容:

    image-20201013170955488

    會調用 getRelativePath() 方法獲取請求資源路徑:

    image-20201013171255971

    這個方法存在一個判斷,如圖中紅框位置標出:如果 request.getAttribute()javax.servlet.include.request_uri 不為空,則會取 javax.servlet.include.path_infojavax.servlet.include.servlet_path 的值,并進行路徑拼接,返回路徑結果。

    這個結果 path 會被帶入到 getResource() 方法中返回結果,只要文件存在,即可讀取其中內容。

    image-20201013171943132

    由此可見,配合AJP協議中的缺陷,可以控制attribute中的內容,造成任意文件讀取漏洞。

    但是需要注意的是,在讀取資源文件的過程中,會調用org.apache.tomcat.util.http.RequestUtil.normalize() 方法來對路徑的合法性進行校驗,如果存在 ./../ 則會返回 null ,在后續流程中會拋出一個非法路徑的異常終止文件讀取操作。

    image-20201014110343428

    因此我們無法使用 ../ 跳出目錄,只能讀取Web應用目錄下的文件。

    JspServlet

    同樣的在$CATALINA_BASE/conf/web.xml 中,對訪問以 .jsp/*.jspx 后綴結尾的請求,調用 JspServlet 處理請求。

    image-20201014101016408

    看一下重點的 service(),代碼如下圖,在attribute中含有如下 javax.servlet.include.servlet_pathjavax.servlet.include.path_info 時,將會取出并拼接為文件路徑 jspUri

    image-20201014102154103

    拼接成 jspUri 后,調用 serviceJspFile() ,將此文件解析為jsp文件并執行。

    image-20201014104235407

    因此這就構成了一個文件包含漏洞。在文件內容可控的情況下,就可以延伸為任意代碼執行漏洞,所以網上有的分析文章也出現了任意代碼執行、任意命令執行漏洞的字眼。

    AJP客戶端

    在了解了AJP協議的漏洞成因之后,我們只需要構造一個客戶端就可以實現自己的攻擊行為了。

    AJP協議的請求和響應包結構在文檔可以看到:https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html

    實現過程這里不進行描述,代碼放在如下位置,請自行觀看:

    javaweb-sec/javaweb-sec-source/javasec-test/javasec-tomcat-ajp

    在這里進行漏洞兩種利用方式的演示。

    Web目錄任意文件讀取

    任意文件讀取需要滿足的條件是:

    • 訪問的地址(target)是一個沒有 Servlet 映射的地址
    • request_uri 屬性不為空
    • servlet_path 和 path_info 拼接得到我們想要讀取的文件

    如下圖配置:

    image-20201014190817620

    可以看到成功返回了文件內容:

    image-20201014191218553

    JSP文件包含

    假設我們在web目錄下具有可控的文件,比如我們上傳了一個aaa.jpg,文件里是一個執行whoami命令并返回結果的jsp惡意文件。

    image-20201014184822525

    這是我們需要控制的是訪問的地址(target)是一個.jsp結尾的文件,并且 servlet_path、path_info 拼接起來是我們可控的文件路徑。

    image-20201014185022126

    運行返回結果,可以看到我們的 jpg 文件以 jsp 解析并執行成功:

    image-20201014190642641

    問題拓展

    在實際對Tomcat AJP 漏洞的研究和利用過程中,逐漸產生了以下幾個問題,請自行思考:

    • SpringBoot項目是否受到此漏洞影響?如果能受到影響,是在什么情況下?
    • Struts2、Shiro、SpringMVC 等等具有一些全局過濾器的情況下,是否能夠觸發漏洞?
    • jsp 作為視圖模板時是否存在漏洞?

    參考答案鏈接:https://www.colabug.com/2020/0318/7137788/

    本文章首發在 網安wangan.com 網站上。

    上一篇 下一篇
    討論數量: 0
    只看當前版本


    暫無話題~
    亚洲 欧美 自拍 唯美 另类