看著文章不錯,順手翻譯了下,如有不足,敬請指正,同時希望能和大家一起交流。 From :http://googleprojectzero.blogspot.com/2015/07/one-perfect-bug-exploiting-type_20.html
對于一些攻擊者而言,exp能穩定的利用是很重要的。換言之,exp在運行特定flash版本的特定平臺的系統上,exp要每次都能導致代碼執行。編寫這種高質量的exp的途徑之一就是利用高質量的bug(也就是主動因素都利用編寫穩定利用的exp).此文討論的就是利用一個這種類型的bug,并討論它適合編寫穩定利用的exp的因素。
Cve-2015-3077
是一種發生在Adobe Flash Button
和MovieClip
的fliters
(是一個包含filter對象的索引數組)屬性的setters方法的類型混淆問題,使得任意類型的filter都可以用其它類型的filter來混淆。該缺陷在五月份上報給了Adobe,并在五月得到了修復。該bug的根源在于,攻擊者可以覆蓋初始化filter對象的構造函數。下面的代碼能說明這個問題:
var filter = new flash.filters.BlurFilter();
object.filters = [filter];
var e = flash.filters.ConvolutionFilter;
flash["filters"] = [];
flash["filters"]["BlurFilter"] = e;
var f = object.filters;
var d = f[0];
這段代碼可能因使用操作符‘[]’
而讓人疑惑,而它在Flash CS編譯的時候是需要的。邏輯上等價的代碼如下(不保證能編譯):
var filter = new flash.filters.BlurFilter();
object.filters = [filter];
flash.filters.BlurFilter = flash.filters.ConvolutionFilter;
var f = object.filters;
var d = f[0];
上面這段代碼將button
對象或者一個MovieClip
對象的filters
屬性設置為了BlurFilters
(這當然也代碼中object本身是什么類型有關),然后由Flash進行原生存儲。BlurFilter
的構造函數接著被ConvolutionFilter
的構造函數覆蓋了。然后filters
的getter
方法被調用,生成了ActionScript
對象存儲起初的BlurFilter
,然而,構造函數已經被覆蓋了,因此調用的是ConvolutionFilter
的構造函數。這就導致了原始是ConvolutionFilter
對象,卻以BlurFilter
對象返回。
最終的效果是ConvolutionFilter
的域可以被當做BlurFilter
來訪問(進行讀寫操作),其它類型的filter也與此類似。這就為進行利用提供了很多修改的機會。
圖1展示了,利用該漏洞能夠在64位linux上進行類型混淆的原始對象的內存布局。
在這兩種情形下,指針的占用的空間與多個證書或者浮點占用的空間對齊(如圖1中ConvolutionFilter
的float* matrix
剛好與float blurX
和float blurY
占用的空間對齊),這也就意味著,指針能直接進行讀寫。同樣,由于對象的域的順序和大小都是由類的定義決定的,其位置通常都是可預期的,因此對其進行讀寫不會失敗。這些特性在編寫穩定利用的exp時是很關鍵的。
由于利用該缺陷可能需要多次觸發類型混淆,因此編寫了用于類型混淆的功能函數FilterConfuse.confuse
.該函數也進行了一些清理操作,如將ActionScript filter
的構造函數重新修改為正常的構造函數,這樣缺陷函數能夠調用多次而不影響ActionScript在該函數外的行為(如果不這樣做,很可能就直接崩潰了)。
首先要做的就是確定vtable(虛表)的地址來繞過ASLR技術。理想的方式是對含有虛表的對象使用一個特殊的對象,該對象的一個成員和虛表有交叉,并能被修改,但是所有filter對象的虛表都在同一偏移處。相反,我使用了DisplacementMapFilter
的BitmapData
對象來確定虛表的地址。
為了確定BitmapData對象在內存中的位置,我用BevelFilter
對DisplacementMapFilter
進行了類型混淆。結果就是,存放在DisplacementMapFilter
中的 BitmapData
指針與BevelFilter
的顏色屬性(shadwoColor
,shadowAlpha
,highlightColor
,hightlightAlpha
,同樣是從內存分布上而言)是對齊的(占用的空間大小事一樣的)。這些屬性信息,存放在2個32位整數中(如圖2的scolor
,hcolor
所示),顏色屬性能夠訪問到每個整數的低24 bit,而alpha屬性能夠訪問到整數的高8 bit.通過讀取這些屬性并結合位運算,提取BitmapData
對象的真實地址是可能的。
接著,我們需要讀出BitmapData對象頂部的虛表。因此,我使用了ConvolutionFilter
對象的matrix屬性來。該屬性的值是一個指向浮點數組的指針,其指向的數組在該屬性值設定的時候進行分配。當獲取該屬性的值的時候,就會返回包含這些浮點數的ActionScript數組。通過將matrix指針指向BitmapData對象,以浮點數組的形式讀出對象在內存中的值是可行的(其實就有點類似于C中,char* buf=(char*)malloc(size)
,int* buf2=(int*)buf
,這樣緩沖區中的數據,可以以char為單位讀寫,也可以以int為單位進行讀寫)。
為了設置該指針,我用一個DisplacementMapFilter
對象(與上面用的DisplacementMapFilter
對象,不是同一個對象實例)混淆了ConvolutionFilter
對象,并將mapPoint屬性設置為以上BitmapData
對象的指針的位置。mapPoint屬性是一個含有x,y坐標的點(坐標值為整數,也就是圖3的p_x,p_y),并與ConvolutionFilter
的matrix是對齊的,這使得設置其值很容易。接著,通過讀取ConvolutionFilter
對象的matrix 數組(該對象得用DisplacementBitmapFilter
進行類型混淆,接著又混淆回ConvolutionFilte
r對象),從BitmapData對象讀取出虛表地址是可行的。
從這一點上看,由于使用了浮點,代碼要能穩定利用就更難了。vtbale_low和vtable_high的值是以浮點的形式從ConvolutionFilter
的matrix讀出來的(matrix對應的是一個數組類型)。但是不幸的是,不是所有有效的指針都是有效的單精度浮點數(如需要遵守ieee 754標準等等),這也就意味著讀取該值可能會導致NaN(not a number)錯誤,或者更糟情況下,讀取的數值有誤。
該問題理想的解決方式是通過以整數形式獲取的gettter方法來獲取vtable_high
和vtable_low
的值,但是沒有這樣的getter,因為filter成員由于其功能特性其類型往往是浮點數。
幸運的是,AS2虛擬機在解析浮點數的時候較寬松—它僅僅在ActionScript對其進行操作的時候,才將其內存中的值轉換為浮點。原生操作通常并不會導致將其解析為浮點,除非進行了特殊的操作,如使用了它的算數運算。這就意味著,當matrxi數組的浮點數據拷貝到vtable_low和vtable_high時,它會在內存中保持其值,直到被拷貝到ActionScript中并被實際使用,或者以原生代碼的形式對其進行了算數運算,即使它不是有效的浮點數。因此,如果變量的值立馬類型混淆為不同的類型,并且其值的范圍涵蓋了32bit能表示的數值,如int,那么可以確定的是在matrix數組對應的內存中其值保持不變。因此為了避免給利用代碼帶來不穩定性,在ActionScript中操作任何浮點數之前都有必要進行這種類型的類型混淆。
為了達到這一目的,我寫了一個轉換類—FloatConverter(如圖4),它在filters中使用類型混淆來實現整數到浮點和浮點到整數。它使用GlowFilterd
的color,alpha屬性來混淆ColorMatrixFilter
的 matrix(內部是一些浮點數組)屬性,它能訪問到int的不同字節。
雖然這實現了穩定的float到int的轉換,但沒能實現int 到float的穩定的轉換。在ActionScript中ColorMatrix filter
的color 數組被訪問的時候,整個數組都會被拷貝,即使是只訪問了其中的第一個元素。當數組被拷貝時,每個元素都被轉換為了一個Number(有時包含訪問指針,如調用對象的valueof方法)。由于color數組比整個GlowFilter對象占用的空間大,當使用GlowFilter
混淆的時候它會擴展到堆上(也就是會把堆上的一部分數據也當做GlowFilter的數據)。這也就意味著,轉換可以發生在堆的未知的值上,當被轉換為Number時,如果他們引用了無效的指針就可能導致崩潰。因此,對于int到float的轉換,我實現了一個浮點轉換(如圖5所示),它在ConvolutionFilter
和DisplacementMapFilter
使用了一種不同的混淆—使用的直接的強制類型 轉換,不會導致堆上任何未知的值被訪問到。
這解決了因訪問未知堆值而出現的崩潰,但不幸的是,還有一個涉及浮點的問題關系到利用代碼的穩定性。這是由ConvolutionFilter matrix
的 getter的實現導致的。在ActionScript2中,所有的數字值都是以Number類型存儲的,而Number是一個int和一個double指針的聯合。原始的ConvolutionFilter matrix
被存儲為一組浮點數組,但是它被復制到了一個ActionScript數組中,因此在ActionScript中能通過matrix 的getter方法來訪問它,它的值在這個過程中被轉換為了double.接著,當float轉換作用在該值上時,他們又被強制轉換回了float.
將float強制轉換為double,然后又將得到的double轉換為float,通常值都能保持不變,除非在一種特殊的情況,單精度浮點值是一個SNaN值。根據單精度浮點標準,有兩種類型的NaN(not a number,其實也就是浮點值異常),QNaN和SNaN。出現QNaN的時候,不會做什么處理,但出現SNaN的時候,在某些情況下會拋出浮點異常。在x86中,將double強制轉換為float通常出現的NaN是QNaN(即使double導致的是SNaN),來避免非預期的異常。
因此一個指針的低bit位恰好是SNaN值,它將會被轉換為QNaN值。這意味著其中一個bit位(mantissa的第一個bit,bit 22)將會被置位,而此時本不應該被置位。這個問題在vtable被讀的時候可以避免---||||指針的第三個字節(含有能被反轉的bit)可以以非對齊的方式讀取,來確定其真正的值。因此代碼會進行非對齊方式的讀取(用Bitmap指針加1后進行第二次讀取vtable), 如果浮點恰好是SNaN,則對int值進行校正。.
使用上面實現的float轉換,vtable的地址可以被轉換為一個整數。現在我們需要利用該地址來獲取代碼執行。一種簡單的移動EIP的方式是覆蓋一個對象的vtable(或者指針指向的對象的vtable)。這可以通過用DisplacementFilter BitmapData
指針類型混淆ConvolutionFilter matrix
數組來實現。
BitmapData對象包含一系列原生對象。ActionScript對象包含一個指向BitmapData native object
的指針,而后者又包含指向其他原生對象的指針。其中的一個對象是bits 對象,它包含實際的bitmap bit數據。這個bits 對象包含許多虛方法,這些虛方法通常是任何操作作用于BitmapData對象時首先調用的方法。為了對該特性進行利用,Exp偽造了一個含有指向偽造的bits對象的指針的BitmapData對象,然后調用了一個方法,該方法會導致對偽造的bits對象調用虛方法。
通過使用ConvolutionFilter的屬性matrix的setter方法,可以為浮點數據分配任意大小的緩沖區。而緩沖區的位置,可以通過用DisplacementMapFilter
類型混淆ConvolutionFilter
,并結合使用DisplaementMapFilter
的mapPoint屬性來確定,操作有點類似于讀取vtable的位置。由于分配的數組是不變的,首先就得偽造一個vtable對象,和一個指向vtable的bits對象,最后生成一個偽造的bitmap指向它。
第一步是生成一個偽造的vtable,并使用ConvolutionFilter/DisplacementMapFilter
混淆來確定其位置。
接著,以同樣的方式偽造一個bitmap bits。
接著,以同樣的方式偽造一個bitmap bits。
通過設置DisplacementMapFilter
對象的BitmapData對象為指向偽造的bitmap的指針,并使用BevelFilter進行混淆,設置color屬性為指針的值(讀取原始BitmapData對象的位置逆過程),可以在ActionScript中獲取到對偽造的bitmap的引用。這個對象接著又利用類型混淆混淆回DisplacementMapFilter
,BitmapData
對象可以通過調用mapImage 的getter方法進行訪問。那么無論何時對象包含虛方法調用的方法被調用(如setPixel32),該方法都會調用由偽造的vtale確定的函數地址。
從這一點上看,值得細究偽造的vtable中具體的內容。前面的float轉換忽略了SNaN:寫float。ConvolutionFilter.matrix
的setter也轉換float到double,然后在寫之前又將double轉換回float,因此如果一個指針值碰巧是SNaN值,那么寫回matrix數組時,bit 23會被置位,即使在原始值中沒有置位。這可以利用受限的非對齊寫來避免。
在內存中,一個SNaN指針布局如下:
00: XX XX YY QQ XX ZZ 00 00
其中: xx:可以為從00到0xFF的任意值 YY:bit 5被置位,其它無限制 QQ:除了第7bit,其它所有bit位為1 ZZ:第7bit被置位1,其它bit位無限制
它可以作為32bit的單精度浮點數對其進行寫,如下:
00: 00 XX XX YY
04: QQ XX ZZ 00
08: 00 00 00 00
這就保證了,如果原始的指針值是一個SNaN值,所有非對齊值都不會是SNaN(因為如果原始浮點是一個SNaN值,YY的第5bit會恒為0)。必須使用兩個常量指針才能達到這一目的(除非這兩個事先都知道是SNaN值),布局將是下面這樣的:
00: 00 XX XX YY
04: QQ XX ZZ 00
08: 00 XX XX XX
0C: QQ XX ZZ 00
10: 00 00 00 00
位于0x08處的浮點,bit22到bit31沒有限制,因此最終可能會是一個SNaN值,并被不正確的寫。
因此寫任何指針到浮點數組是可能的,不論它是否是SNaN值,但僅僅能被做一次。在原始指針被寫后,如果原始指針是SNaN值,那么其它的指針都應該是SNaN值,如果原始值不是SNaN值,那么所有其它的指針都不能是SNaN值。該exp處理這一限制的方式是僅僅寫單個指針到Convolution matrix
數組中。
對于偽造的Bitmap和偽造的Bitmap bits,這是很容易實現的,因為他們僅僅需要包含一個指針。難點在于偽造的vtable只能包含一個指針,這很難用于為函數調用建立參數,并調用函數。
好的解決辦法是在首次調用了vtable后移動到內容完全可控的緩沖區中去。好消息是,BitmapData(偽造的bitmap模擬的)類的paletteMap方法創建了一個這樣的緩沖區。 該方法有四個參數(redArray
,greenArray
,buleArray
,alphaArray
),這四個參數都是ActionScript Numbers
型的數組。當該方法被調用的時候,他們被轉換為整數,并被拷貝到了原生的的int型數組中去。接著另一個含有四個指向這個數組(指向輸入數組的特定的偏移處)的指針的原生函數被調用。該方法調用虛方法,跳轉到偽造的vtable.
作為初始調用的一部分,數組指針存儲在x64的r12,r13,r14,r15寄存器中。指針指向了四個可以控制的緩沖區,這點很有用。偽造的vtable中的單個指針實質是如下內容:
mov rdi, r13
call [r12]
r13指向的緩沖區的內容為”gedit”,r12對應的緩沖區的內容為一段Flash庫中調用系統方法的gadget(不關心SNaN)。最終的結果就是,當虛函數調用到了偽造的vtable ,gedit被調用。
exp到此為止,雖然沒有優雅的退出(當gedit退出的時候,Flash player會崩潰)。優雅的退出的關鍵在于多次在這四個緩沖區上調用函數。即使這里也處理好了,也不能幸免于Flash Player的銷毀(如tab關閉了,或者swf刷新了)。因為調用了filter對象的析構函數會導致崩潰,因為指向ConvolutionFilter matrix
數組的指針被類型混淆為指向BitmapData
的對象的指針。這些對象分配在不同的堆上,因此當對一個對象調用刪除而其本身實質是另一個對象時,會導致崩潰。這個問題并不能通過通過類型混淆到一個好的狀態來完成,因為類型混淆在這個bug中創建了對象的一個副本,因此原始的有問題的對象任然存在,任然需要被釋放。同樣也不能通過在原始對象上設置參數來完成,因為BitmapData對象,matrix對象的setter會在進行創建之前試圖刪除已經存在的對象。當exp在運行中并避免這個問題是可能的,只要player保持打開,通過獲取到這些對象的引用,這樣對象就不會被釋放掉。但是當player銷毀的時候,這個崩潰還是會出現。當然,exp執行的代碼是可以避免崩潰的,可以通過在內存中校正類型混淆對象,把他們置于正確的狀態,亦或將其析構函數的指針指向為空。該段代碼中并沒有實現這個。
雖然類型混淆通常是可以利用的,還有其他的因素使得cve-2015-3077
能夠被很穩定的利用。
當類型混淆被觸發,通常包括兩種類型,實例化時的有缺陷的對象,漏洞觸發后混淆的類型。原始類型的對象成員如何對其混淆的對象成員,對exp的可利用性和利用的穩定性有很大的影響。在這種情形下,原始缺陷類型成員(如指針)以混淆類型成員進行排列,攻擊者可以直接進行操作,反之亦然。這是導致exp能可靠執行的最理想的情形。另一個常見情形是原始對象成員的大小超過了混淆對象成員的大小,他們的值由內存越界區域的值決定。這種情形通常也是可以利用的,盡管利用不穩定,因為很難保證對象的堆以預期的方式排列。另一種可能是,對象操作的方式受限,例如,原始對象成員僅僅能設置為一些受限的值,而混淆的對象又是只讀的,或者只有一個特定的方法調用使用它。這種情形下,是否能利用,取決于bug本身的特性,也能導致如需要結合其他bug綜合利用的信息泄露型bug。這種類型的bug也可能能穩定的利用,但需要那種運氣好的bug,碰巧有對的成員又有對的數值。
這個bug的另一方面是,類型混淆發生在導致類型混淆的缺陷函數調用的最后。這很關鍵,因為這意味著,一個對象可以被混淆,并且不會以攻擊者不期望的方式被修改。一些類型混淆的bug不能利用或者不能穩定利用,因為類型混淆后調用的方法是用了混淆的對象,而該值應該是有效的,而后面使用的時候此處又不是有效的。值得注意的是,在以上的利用代碼中,其成員發生類型混淆的MovieClip對象,被設置為了0*0緯。這能避免那些對flter對象的特定調用導致不可穩定利用,因為filter沒必要作用于沒有像素數據的對象。
同樣,在這個bug中,攻擊者沒有進行訪問的對象成員也沒有被軟件使用。這是另一個影響類型混淆型bug能否利用以及穩定利用的因素。有時,對攻擊者無用的成員(從攻擊者角度看)能導致崩潰,如果攻擊者不能修正它的值的話。同樣,MovieClip設置為了0*0緯避免了該問題,因為Flash 不能對沒有像素的圖像使用filter方法。
最后,保持對原始對象和混淆對象的引用是很重要的,因為它阻止了垃圾回收,當垃圾回收作用于類型混淆對象時通常都會導致崩潰。垃圾回收以一定的方式認為對象成員是有效的,如包含有效的指針,如果指針無效將會導致崩潰,攻擊者通常也很難對這種情形進行校正,因為垃圾回收可以發生在任意時間,因此任何無效的窗體也是一個問題。完全可靠的解決方式是,當對象有效的時候阻止垃圾回收。
很多因素,包括原始對象的布局,混淆對象的成員,對象時何時以何種方式使用,以及該對象時候容易遭受垃圾回收都能影響到類型混淆漏洞利用的穩定性。Cve-2015-3077由于結合了以上因素是一個高質量的bug,,能夠被穩定的利用。利用該代碼需要觸發該bug 31次:其中8次用戶設置和獲取利用需要的對象成員,19次到第23次用于float轉換,這依賴于發生了多少SNaN值。雖然次數看著有點多,但利用是穩定的,因為每一步都是確定的,并不依賴于那些不一定會發生的行為。
參考鏈接:
【1】:Issue 254: Adobe Flash: Type Confusion in Button.filters https://code.google.com/p/google-security-research/issues/detail?id=254
【2】MovieClip.filters property http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00001293.html