天天看點

CVE-2017-7529 Nginx整數溢出漏洞分析

本文講的是<b>CVE-2017-7529 Nginx整數溢出漏洞分析</b>,

1、漏洞描述

在Nginx的range filter中存在整數溢出漏洞,可以通過帶有特殊構造的range的HTTP頭的惡意請求引發這個整數溢出漏洞,并導緻資訊洩露。

2、影響程度

攻擊成本

危害程度

影響範圍

Nginx 0.5.6 – 1.13.2

3 、漏洞原理

3.1   HTTP斷點續傳:Range

HTTP的Range允許用戶端分批次請求資源的一部分,如果服務端資源較大,可以通過Range來并發下載下傳;如果通路資源時網絡中斷,可以斷點續傳。

Range設定在HTTP請求頭中,它是多個byte-range-spec(或suffix-byte-range-spec)的集合;

其中,first-bytes-pos指定了通路的第一個位元組,last-byte-pos指定了最後一個位元組,suffix-length則表示要通路資源的最後suffix-length個位元組的内容;例如:

Range:bytes=0-1024 表示通路第0到第1024位元組;

Range:bytes=500-600,601-999,-300 表示分三塊通路,分别是500到600位元組,601到600位元組,最後的300位元組;

在Response頭中設定:

Accept-Ranges:bytes 表示接受部分資源的請求;

Content-Range: bytes START-END/SIZE 表示傳回的資源位置;其中SIZE等于Content-Length;如:Content-Range: bytes 500-600/1000

3.2   Nginx Range Multipart

如果一次請求有多個range,傳回的資料需要multipart來組織;格式如下:

Nginx對Range的支援包括header處理和body處理,分别用來解析用戶端發送過來的Range header和裁剪傳回給用戶端的請求資料Body。其實作分别由ngx_http_range_header_filter_module和ngx_http_range_body_filter_module兩個過濾子產品完成。

在ngx_http_range_header_filter_module中調用了ngx_http_range_header_filter函數,而該函數進一步調用了ngx_http_range_parse函數來解析header中的Range字段;分别調用ngx_http_range_singlepart_header和ngx_http_range_multipart_header來生成single range和multi ranges的Response Header;

這次的問題就出現在多個range時,ngx_http_range_parse函數對suffix-length的處

理;

3.3   Nginx Cache

Nginx可以作為緩存伺服器,将Web應用伺服器傳回的内容緩存起來。如果用戶端請求的内容已經被緩存,那麼就可以直接将緩存内容傳回,而無需再次請求應用伺服器。由此,可降低應用伺服器的負載,并提高服務的響應性能。

下面是使用Nginx作為緩存伺服器的一個示例。假設Nginx監聽本地80端口,反向代理百度,那麼就有如下配置:

CVE-2017-7529 Nginx整數溢出漏洞分析

此時,我們通路http://127.0.0.1,即可得到百度的傳回:

CVE-2017-7529 Nginx整數溢出漏洞分析

檢查頁面資源,存在一個靜态圖檔檔案http://www.baidu.com/img/bd_logo1.png。由于這類靜态檔案一般不會發生變化,我們可以将其緩存起來。

Nginx配置緩存主要由以下指令完成:

 proxy_cache_key用于區分cache檔案。

 proxy_cache_path設定cache檔案的路徑和參數。

· cache檔案會儲存在指定的目錄下面,檔案名是cache key的MD5值

· 通過level參數将cache檔案分多層目錄儲存,以避免某個目錄下存在大量檔案造成的性能開銷

· 通過keys_zone參數指定cache key在記憶體中的區域及其大小,1M的區域大概可以儲存8000條key的資訊

proxy_cache_valid對不同傳回狀态值設定cache有效時間

例如,下面這條配置:

CVE-2017-7529 Nginx整數溢出漏洞分析

指定了以下資訊:

使用協定、請求方法、域名、URI作為cache key

cache檔案儲存在目錄/tmp/Nginx/下,采取兩層目錄,keys_zone名稱為my_zone,大小為10M

對于傳回狀态值為200的内容,cache有效時間為10分鐘

現在,我們配置好了名為my_zone的cache,接下來選擇對目錄www.baidu.com/img/下的圖檔做緩存。首先,仍然是設定反向代理:

CVE-2017-7529 Nginx整數溢出漏洞分析

接下來,我們使用下列指令對img目錄下的檔案進行緩存:

CVE-2017-7529 Nginx整數溢出漏洞分析

配置指令解釋如下:

proxy_cache指定使用的keys_zone名稱,就是之前的my_zone

add_header在Nginx傳回的HTTP頭中,增加一項X-Proxy-Cache,如果緩存命中其值為HIT,未命中則為MISS

proxy_ignore_headers由于百度對圖檔的請求也會Set-Cookie設定,而Nginx不會緩存帶有Set-Cookie的傳回,是以我們這裡設定忽略該HTTP頭

現在,對圖檔的緩存配置就完成了,完整的配置内容如下

CVE-2017-7529 Nginx整數溢出漏洞分析

我們使用curl指令進行實驗,通路http://127.0.0.1/img/bd_logo1.png。由于是第一次通路,可以看到傳回内容中X-Proxy-Cache的值為MISS:

CVE-2017-7529 Nginx整數溢出漏洞分析

再次通路時,此時緩存命中,X-Proxy-Cache的值為HIT了

CVE-2017-7529 Nginx整數溢出漏洞分析

那麼現在的Cache檔案是什麼樣的呢?我們檢查設定的緩存目錄/tmp/Nginx,發現存在以下Cache檔案:

CVE-2017-7529 Nginx整數溢出漏洞分析

可見,确實使用了2層目錄儲存了Cache檔案。Cache檔案儲存了Nginx請求得到的傳回内容:

CVE-2017-7529 Nginx整數溢出漏洞分析

可以看到,cache key的内容儲存在了Cache檔案的頭部,此外還有Nginx請求後端傳回的HTTP頭,如後端(這裡是www.baidu.com)的伺服器為Apache。正常情況下,這些資訊是不會傳回給用戶端的。而本次的的漏洞,就是由于負數偏移量,導緻Cache檔案的頭部資訊也被傳回,進而造成資訊洩漏。

4 、漏洞原理

首先,我們看這次漏洞修複的commit:

CVE-2017-7529 Nginx整數溢出漏洞分析
CVE-2017-7529 Nginx整數溢出漏洞分析

可以看到,在ngx_http_range_filter_module.c的ngx_http_range_parse函數中做了兩處修複:

· 進一步檢測了size的溢出情況,防止size溢出後造成小于content-length這條判斷的繞過

· 則直接限定了使用字尾的情況下,start不能為負的,最小隻能是0,也就是說使用&amp;ldquo;-xxx&amp;rdquo;的方式對Cache檔案的讀取隻能從0開始到結束。

根據漏洞修複commit的注釋,我們知道這次漏洞的主要成因就是bytes-range讀取的起始範圍可能為負數,進而讀取緩存檔案頭部。

首先,如果傳入完整的range參數,如start-end,則在ngx_http_range_parse()中會檢查start,確定其不會溢出為負值:

CVE-2017-7529 Nginx整數溢出漏洞分析

是以,如果需要将start解析為負數,隻能通過-end這類字尾型range參數實作:

CVE-2017-7529 Nginx整數溢出漏洞分析

此時的start等于content-length減去讀入的end值,是以如果傳入的end比實際長度還要長,就可以使start變為負數,而這就是第二處修複所處理的情形:

CVE-2017-7529 Nginx整數溢出漏洞分析

同時注意到,在這類情況下,最終end的值會被設定為content-length-1。是以這塊range的總長度就超過了content-length。而Nginx對range總長度會有檢查:

CVE-2017-7529 Nginx整數溢出漏洞分析

一般來說,range作為原始檔案的一部分,其長度應該是小于content-length的。是以一旦計算得到的size比content-length還大,那就直接将原始檔案傳回了,不再進行range處理。為了繞過這一限制,我們就需要利用到第一處修複所處理的情形。

具體而言,檢查用到的size是将multipart的全部range長度相加得到的:

CVE-2017-7529 Nginx整數溢出漏洞分析

是以,一個range是不夠的,我們至少需要兩個range,其長度之和溢出為負數,就可以繞過總長度的檢查了。

要得到一個很大長度的range,同樣可以采用-end這種字尾型,将end設定為一個非常大的數即可。此處的start, end, size均為64位有符号整形,是以隻需要最終相加得到的size為0x8000000000000000即可。

5 、漏洞利用

本次複現利用使用Nginx-1.12.0作為緩存伺服器,緩存配置同上文,通路的目标檔案仍然是http://www.baidu.com/img/bd_logo1.png。

首先,我們不指定range,得到該圖檔檔案的長度為7877:

CVE-2017-7529 Nginx整數溢出漏洞分析

設定第一段range為-8500,此時的start為7877-8500=-623,即圖檔在Cache檔案偏移之前的623 bytes也會被傳回,而這623 bytes中就包含了Cache檔案頭部。

下一步,按照上文所說,第二段range的長度需要将總長度溢出。我們的目标總和size為0x8000000000000000,第一段range長度為8500,故第二段range長度為0x8000000000000000-8500=9223372036854767308。

于是,使用curl指令,配合-r參數指定bytes range:

CVE-2017-7529 Nginx整數溢出漏洞分析

可以看到傳回内容中,第一段即為-8500的range,而這一段中我們就看到了Cache檔案頭部,例如cache key以及後端伺服器傳回的HTTP頭。

6、漏洞修複

綜合來看,這個漏洞就是整數溢出漏洞的利用,能夠從Cache檔案中擷取Cache頭的資訊。在某些配置的情況下Cache頭中會存在IP位址資訊,造成資訊洩露。

就Nginx子產品以及常用的第三方子產品本身來說,無法通過這個整數溢出來對記憶體進行操作或者遠端執行。

建議更新到1.13.3和1.12.1版本;如果不能更新,可以在Nginx配置檔案中添加max_ranges 1,進而禁用multipart range。

原文釋出時間為:2017年7月17日

本文作者:銀河實驗室

本文來自雲栖社群合作夥伴嘶吼,了解相關資訊可以關注嘶吼網站。

<a href="http://www.4hou.com/technology/6343.html" target="_blank">原文連結</a>

繼續閱讀