<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/papers/5669

            前言


            在4月的補丁日,微軟通過標記為“高危”的MS15-034補丁,修復了HTTP.SYS中一處遠程代碼漏洞CVE-2015-1635。據微軟公告(https://technet.microsoft.com/en-us/library/security/MS15-034)所稱,當存在該漏洞的HTTP服務器接收到精心構造的HTTP請求時,可能觸發遠程代碼在目標系統以系統權限執行。

            這是對于服務器系統影響不小的安全漏洞,任何安裝了微軟IIS 6.0以上的Windows Server 2008 R2/Server 2012/Server 2012 R2以及Windows 7/8/8.1操作系統都受到這個漏洞的影響。

            從微軟的公告致謝來看,這個漏洞是由“Citrix Security Response Team”(美國思杰公司的安全響應團隊)發現,從網上公開的信息來看,Citrix公司是一家從事云計算虛擬化、虛擬桌面和遠程接入技術領域的高科技企業。這也引發了Twitter上很多關于該漏洞是否是由針對Citrix公司的APT攻擊中發現的疑問,而就在微軟發布補丁的不到12個小時內,便有匿名用戶在Pastebin網站上貼出了針對這個漏洞可用的概念驗證攻擊代碼,似乎也印證了這一點。

            筆者和360Vulcan的小伙伴們獲得該信息后,就開始針對其進行深入的分析,并在12小時內初步分析清楚了漏洞的原理和利用相關信息,下面就將我們分析的一些結果分享給大家,以便更好地促進安全社區理解和防御這一高危安全漏洞。

            漏洞重現


            結合Pastebin網站上貼出的信息(http://pastebin.com/ypURDPc4)和微軟公告,我們知道這是一個位于HTTP.SYS中的整數溢出漏洞,根據Pastebin網站的python代碼,我們知道通過給IIS服務器發送這樣格式的HTTP請求,就可以觸發(檢測)這個漏洞:

            GET / HTTP/1.1
            Host: stuff
            Range: bytes=0-18446744073709551615
            

            我們直接使用wget或curl工具,也可以直接測試這個漏洞,例如使用如下命令行:

            #!bash
            wget 127.0.0.1 –debug –header="Range: bytes=0-18446744073709551615"
            

            此處18446744073709551615轉為十六進制即是 0xFFFFFFFFFFFFFFFF(16個F),是64位無符號整形所能表達的最大整數,那么我們很容易可以想到,這個“整數溢出”必然同這個異常的超大整數有關。

            Pastebin上POC的作者提供的檢測工具代碼認為,如上請求包,若IIS服務器返回“Requested Range Not Satisfiable”,則是存在漏洞,否則如果返回”The request has an invalid header name“,則說明漏洞已經修補。

            在實測中可能很多人也會發現并非如此,針對不同的服務器,這個測試程序很可能導致服務器直接BSOD甚至直接引發VM進程Crash(對于虛擬主機),這是為什么呢?這究竟是發生在何處的什么原因的整數一處呢?在下面的小節中我們將會進一步講到。

            漏洞原理分析


            HTTP.SYS是微軟從IIS6.0開始,為了在Windows平臺上優化IIS服務器性能而引入的一個內核模式驅動程序。它為IIS及其他需要運用HTTP協議的微軟服務器功能提供HTTP請求的接收與響應、快速緩存、提高性能、日志等功能服務。

            更多關于HTTP.SYS的信息,可以參考微軟Technet Library中”IIS 6.0 Architecture”中的“HTTP Protocol Stack”一章(https://technet.microsoft.com/en-us/library/cc739400(v=ws.10).aspx)。 HTTP.SYS提供了兩個最重要的功能是Kernel-mode caching 和Kernel mode request queuing,而本次的安全漏洞就出在Kernel mode caching(內核模式緩存)中。

            這里筆者以Windows 8.1 X86平臺上安裝的IIS 8.5為例進行分析講解,這里我們分析的存在漏洞的HTTP.SYS版本號為6.3.9600.16520,修補后的http.sys版本為6.3.9600.17712

            Pastebin上POC代碼的匿名作者提到,補丁修補了http!UlpParseRange函數,通過RtlUlonglongAdd函數實現了修補/攔截。

            從測試代碼和函數名上,我們都可以看出這個漏洞同HTTP頭中的”Range“域有直接的關系, Range請求是HTTP協議中HTTP客戶端用于只獲取服務器上文件的某一部分數據的請求域,更多關于Range請求的細節和規范,可以參考RFC 7233 “Hypertext Transfer Protocol (HTTP/1.1): Range Requests”(http://www.rfc-editor.org/rfc/rfc7233.txt)。

            這里先簡單介紹一下http.sys緩存工作的原理,IIS進程w3wp.exe接收到HTTP請求后,將數據緩存到內核中,并整合HTTP回應頭,最后由http.sys組織數據包經由網絡內核組件發送出去。請求中包括Ranges對象的指定范圍,而緩存中則包含了http文件和大小信息等。

            我們接下來先來看看這個UlpParseRange函數,看他是否是這個漏洞的根本原因。

            UlpParseRange的整個代碼比較長,這里就不全部貼出了,函數的邏輯很簡單,就是從Range bytes=lower-upper(也可以是lower-或-upper形式)中,解析出lower(即讀取范圍的開始offset)和upper(即讀取范圍的結束offset)),然后計算要讀取的長度,在正常的情況下,upper大于lower,因此長度=upper-lower +1

            這里如果是測試代碼中的例子,lower=0 ,upper=0xFFFFFFFFFFFFFFFF

            我們看看未修補前的代碼是怎么樣寫的

            #!bash
            PAGE:0009AD2C                 sub     eax, edx
            PAGE:0009AD2E                 sbb     ecx, edi
            PAGE:0009AD30                 add     eax, 1
            PAGE:0009AD33                 mov     [esi], eax
            PAGE:0009AD35                 adc     ecx, ebx
            PAGE:0009AD37                 mov     [esi+4], ecx
            

            通過匯編代碼我們可知,這里是將upper先減去lower,再加1,得到兩者之間的長度差距(例如 bytes=20-50, 則50-20+1 , 兩者之間有31個字節)

            按照例子里的寫法,就是0xFFFFFFFFFFFFFFFF – 0 + 1 , 確實發生了整數溢出,64位無符號整數上溢為0。

            我們來看修改后的版本:

            #!bash
            PAGE:0009B501                 push    ebx
            PAGE:0009B502                 sub     eax, edx
            PAGE:0009B504                 push    1
            PAGE:0009B506                 sbb     ecx, edi
            PAGE:0009B508                 push    ecx
            PAGE:0009B509                 push    eax
            PAGE:0009B50A                 mov     ecx, esi
            PAGE:0009B50C                 call    [email protected]
            

            這里的代碼是將upper 先減去 lower,然后再用RtlUlonglongAdd 將結果同1相加,這里RtlUlonglongAdd會做安全性檢查,如果相加結果溢出,則會返回STATUS_INTEGER_OVERFLOW.

            由于測試代碼中lower傳入的是0,所以這里也發生了溢出并被捕獲、阻止,但如果lower != 0,這里壓根就不會捕獲到整數溢出,這是怎么回事呢?真正出現問題的地方是這里嗎?

            實際上,這可能是POC編寫者故意隱藏了一點關鍵細節: UlpParseRange通過操縱Range參數可以引發整數溢出,也確實被進行了修補,但是并非這個Range數據真正出現問題的地方。

            我們進一步推測和分析,發現本次漏洞真正利用的地方,而是UlAdjustRangesToContentSize,這個函數用于最終修正Ranges中指定的StartingOffset和Length的合法性。

            首先UrlpParseRange解析了Range參數并獲得StartingOffset和Length后,會將其保存在http請求的對象中,而在解析到對應的緩存后,對比Offset + Length的大小,是否超過要請求的緩存文件數據長度,如果超出了,就要把length裁剪為適合的長度,防止讀取超出的數據,見如下代碼:

            #!bash
            PAGE:0007FD09                 mov     eax, [ebp+length_low]    
            PAGE:0007FD0C                 add     eax, dword ptr [ebp+offset_low]
            PAGE:0007FD0F                 mov     dword ptr [ebp+offset_low], eax
            PAGE:0007FD12                 mov     eax, [ebp+length_high]
            PAGE:0007FD15                 adc     eax, dword ptr [ebp+offset_high]  ;將Length + Offset
            PAGE:0007FD18                 cmp     eax, esi                          ;esi = content length ,緩存的實際數據長度,進行對比
            PAGE:0007FD1A                 jb      short loc_7FD30
            PAGE:0007FD1C                 ja      short loc_7FD23
            PAGE:0007FD1E                 cmp     dword ptr [ebp+offset_low], ecx  
            PAGE:0007FD21                 jb      short loc_7FD30
            PAGE:0007FD23
            PAGE:0007FD23 loc_7FD23:                             
            PAGE:0007FD23                                         
            PAGE:0007FD23                 sub     ecx, [ebp+length_low]                ;length = contentlength-offset            
            PAGE:0007FD26                 mov     eax, esi
            PAGE:0007FD28                 sbb     eax, [ebp+length_high]
            PAGE:0007FD2B                 mov     [edx+4], eax
            

            這里我們看到是一處可利用的整數溢出,Length + offset 如果發生溢出,就會小于contentsize,這里就會跳過這個”adjust”的過程,Length沒有得到任何處理和修正,我們成功控制了Length。

            以例子中的數值為例, length + offset = (0xFFFFFFFFFFFFFFFF + 1 ) + 0 (這個+ 1是前面UlpParseRange添加的) = 0 ,小于contentsize

            而假設lower不為0,則結果 = lower ,只要結果小于contentsize,也是不會被adjust的。

            也就是說,UlpParseRange處發生了整數溢出,而在此處導致了安全檢查的繞過,同時,如果lower != 0 ,UlpParseRange時不會被觸發整數溢出,而是應該在這里得以觸發。

            到這里我們就弄清楚了這個漏洞的觸發流程和原理:

            1.upper(range結束的offset) = 0xFFFFFFFFFFFFFFFF時,UlpParseRange或UlAdjustRangesToContentSize會觸發整數溢出,導致繞過UlAdjustRangesToContentSize的Length檢查

            2.Length 可控,但是Length = 0xFFFFFFFFFFFFFFFF – lower(range開始的offset) , 且lower必須要小于要獲取目標文件的數據長度contentlength。

            BSOD的重現和原理


            看到很多測試攻擊程序的研究人員都無法穩定重現BSOD,看Github上的討論,通過調整lower的數值,有些人可以打藍Server 2012 R2,有些人就不行,或者換個文件就不行。

            實際上,我們分析了這個漏洞的原理就可以很清楚的了解其中的規律了,首先一條原則是上面已經說到的lower不能大于請求的content length,例如假設請求iisstart.htm(648Bytes),lower就必須小于647。

            同時,HTTP請求的處理實際是先通過w3wp發起的進程上下文內http先解析HTTP請求包,組合成緊湊的http回應包后,通過

            UlSendData->UxTpTransmitPacket->UxpTpEnqueueTransmitPacket
            

            排入隊列,然后再由UlSendCacheEntryWorker將其發送出去,在這個過程中,如果range指定的數據開始offset小于緊湊的數據包頭部的總長度,那么就不會觸發到后面繼續命中緩存的處理。(range只允許對數據文件內存指定,不能指定響應頭內的)

            這里我使用wget添加頭部的方式測試,回應包的長度應該是(針對Windows 8.1 X86)310個字節,也就是說,lower必須大于等于310個字節,其他的發送還需要調整這個數值。

            所以,針對iisstart.htm , lower >= 310 且 < 647 就可以穩定觸發BSOD了

            進一步利用


            這個漏洞難道只能BSOD嗎?說好的遠程代碼執行呢?再深入看下漏洞觸發的細節,看上去似乎不能遠程代碼執行,但是遠程讀取服務器內核內存數據是有可能的。

            UlpSendCacheEntry->UlBuildFastRangeCacheMdlChain中,http.sys會為HTTP回應頭和緩存來源buffer/length(我們可控)創建MDL,那么,對于我們的超長length,就會創造一個巨大的mdl,接著放入UxTpTransmitPacket的數據包對象中,通過tcpip->netio,最后解析MDL,將數據最終發出去。

            此時是可以超過緩存的空間,讀取緩存內存往后的數據,如果緩存內存后面是連續的0xffffffffffffffff – lower(4GB?)左右內核內存(通常是X64),就有可能實現信息泄露。

            不過首先是很難有連續的4G內存,同時通過IIS也很難一下獲得如此多的數據,那么只能設法降低這個內存要求:length = 0xFFFFFFFFFFFFFFFF – lower ,且lower < contetnlength才行,我們可以想辦法提高content length,達到降低Length的目的,例如在服務器上尋找一個接近4GB大小的文件:)

            分析時間倉促,有任何進一步的利用和錯誤之處,歡迎討論指正。

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线