一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)

Editor發表於2017-12-04

看雪編輯按:作為通向網際網路世界的一扇窗,瀏覽器早已融入了我們的日常生活。然而近年來針對瀏覽器的攻擊卻不斷增長,方式也更加多元化,一旦這方面的利用技術被意圖不軌者掌握,那麼後果會非常嚴重。另一方面,企業的業務很大程度上也依託於網際網路來完成。因此,如何防止瀏覽器被攻擊者利用成了安全人員不可迴避的問題,同時也是不可推卸的責任。本次看雪安全開發者峰會,陳佳林從 Pegasus 間諜軟體披露的漏洞開始談起,由一個具體的 WebKit 中 JavaScript 核心漏洞出發,詳細闡述了漏洞的成因、核心堆風水、垃圾回收及 JIT 編譯機制,並通過綜合利用地址洩漏、物件偽造、指標劫持、堆噴和 ROP 等技巧最終實現了任意程式碼執行,為我們展示了一套完整的瀏覽器漏洞利用過程。

以下內容為陳佳林演講實錄,由看雪學院(微信公眾號:ikanxue)整理。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)

陳佳林

看雪ID:Roysue,看雪iOS版塊版主,iOS獨立安全研究員,iOS系列書籍作者。《iOS黑客養成筆記:資料探勘與提權基礎》電子工業出版社今年11月出版。《越獄!越獄!》正在撰寫中,預計明年6月上市。

從小酷愛計算機,在網路空間和單機遊戲中度過青少年生涯;大學沒能讀到計算機是最大的遺憾,卻依舊戒不掉對計算機的這份酷愛,長時間浸淫在國內外各大論壇和技術書籍之中,心之所向,身之所往!


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       首先聊一下為什麼叫做“一石多鳥”,標題主要是受長亭科技在今年的BlackHat 2017上發表的《一石多鳥——擊潰全線瀏覽器》所啟發,他們講的是挖到了一個sqlite的漏洞,這個sqlite可不簡單,所有的瀏覽器、常用的App,基本上全部使用到了這款小巧精悍的微型資料庫,這個情況就跟OpenSSL庫出問題是一樣的,所有的https都是用到了SSL協議,像去年的“心臟滴血”事件,在這種底層庫這樣的地基出現問題的情況下,所有上層協議、模組和產品的安全蕩然無存,網際網路上幾乎百分百的https網站都要受到影響。Sqlite出問題的話,攻破所有的瀏覽器和網站也是輕而易舉,我們這次所要研究的,正是另外一項移動平臺瀏覽器的“基礎建築”——JavaScript的解釋引擎:JavaScriptCore。


       首先自我介紹一下,我是看雪論壇iOS板塊的版主,我叫陳佳林。剛剛聽了很多大神的演講,心之所向,身之所往,想要更多地與大神進行交流,我們看雪就是這樣的一個平臺。演講的時間很短,但是日常的工作和學習生活很長,而看雪論壇是經歷了十七年風風雨雨為大家服務的論壇,我們都在這裡,等待你來相聚,希望大家在論壇裡多多發帖,學習知識和分享經驗。


       我的新書《iOS黑客指南:資料探勘和提權基礎》將在今年年內出版,書中具體介紹了逆向的一般方法論及iOS逆向入門的方方面面,也是我個人在學習和成長過程中的經驗和技巧的總結,希望大家可以喜歡。



“炙熱”的“大”前端


       我們先從目錄開始說起,首先是“炙熱”的“大”前端,前端的火熱程度想必在座的各位都是非常感同身受的。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       “全棧”語言——JavaScript,作為大前端的基礎元件,如果沒有JavaScript,那麼所有的網頁都“動”不起來,並且無法與使用者進行互動,JavaScript一開始只是執行在客戶端的瀏覽器中,現在也可以執行在伺服器上,作為Server與資料庫、HTTP、Router等協議進行溝通,因此稱為“全棧”語言。


       我們來介紹這次分享的主角——Webkit及其元件JavaScriptCore中存在的一個漏洞,介紹如分析、除錯、和利用這個漏洞,完成我們任意程式碼執行,彈出計算器的目的。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       “大”前端有多“炙熱”,去年前年就已經出現產品經理不再僱傭安卓或者iOS程式設計師的傾向,做宣傳推廣也好、做活動也好,都以H5作為主要的傳播介質,不知道大家有沒有發現,但凡是一些“現象級”、“病毒式”的營銷,大多是由一個H5頁面作為“PoC”的概念驗證,造成瘋狂傳播的現象。


       產品經理非常重視這一點,他們會要求連結在微信裡一點就可以開啟,一點就能看,比如前年去年很火的直播,如果還要點開再點開,再下載App,這個轉化率就非常低了,大多數產品經理都會要求“點開就能看”,什麼廣告呀、條幅呀都跟著彈出來,至少可以馬上養活這個平臺,像“點開點開再下載App”這個套路使用者就會迅速損失一大批,這對於直播平臺來說是無法接受的。


       基於H5能做的事情還有很多,覆蓋了日常生活中的方方面面,包括朋友圈、各種公眾號,公眾號已經事實上成為了眾多新媒體·自媒體的主戰場,“10萬+”也已經成為一個專有名詞,在如此巨大的裝機量和傳播能級下,沒有哪一個商家、甚至是個人,能夠忽略這一塊。


       H5僅僅是一個網頁,JavaScript在其中發揮的作用與普通瀏覽器並無二致。功能更加強大的“小程式”與今年年初正式釋出,釋出初期很多人並沒有找到其正確的玩法,或者說沒能理解到其精髓,小程式在釋出之初並沒有爆發,而是循序漸進、穩紮穩打、暗中滲透。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       時至今日,我們的日常生活又被小程式給包圍了,不管走到哪裡,“紅包店”遍地都是,現在沒有紅包店的接入的店鋪已經很少了,買個戰鬥雞排都可以搶紅包,更不提肯德基、全家這些“重量級”玩家了。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       然後就是一些稍微“中度”、“重度”的應用,移動支付自不必說,成為中國的“新四大發明”。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       共享單車也是其中之一,餓了麼的客戶端我已經解除安裝了,用小程式足矣,騰訊企業郵等等,這些產品原先自己都是單獨的一個App,現在都以“寄居”的方式,生存在微信、支付寶、淘寶等App中。


       小程式雖然表面上採用的是騰訊獨立開發的wx介面和MINA框架,但是其底層呼叫的還是iOS或者安卓的Native的JavaScript解析引擎,比較直白的一個例子就是每當Chomium釋出漏洞披露的時候,騰訊應急中心也會發布相應的版本更新,因為使用者時常不更新自己的安卓系統,微信必須做到規避漏洞帶來的影響。當然這種現在在iOS上會好很多,新版系統的更新頻率遠遠大於安卓系統。


“全棧”語言:JavaScript


        JavaScript其誕生之初,僅僅是Netscape(網景),也就是火狐的前身,為了讓單調的網頁活躍一點,動起來,而在其瀏覽器上加入的一種動態網頁技術。

        當這項技術越來越流行越來越廣泛的時候,為了保護JavaScript不被微軟搶走,因為微軟是有名的把別人的idea抄過來,自己做一個來代替他的copycat。事實上他們也這麼做了,同樣這麼做了,微軟抄襲之後推出了Jscript,Macromedia公司(後被Adobe收購)推出了ActionScript,Netscape為了保護JavaScript,將其提交給歐洲計算機制造商協會ECMA。


        ECMA在博取眾長的基礎上,推出了ECMAScript標準,這只是一項標準,大家可以採用也可以不採用,這就是為什麼IE會成為很多前端程式設計師的噩夢的原因,IE總是“特立獨行”,或者乾脆不相容,有效地“狙擊”了JavaScript的發展。


       當時間進入到2010年往後,ECMA 5、6、7甚至8的釋出,大家還是很給面子的,基本上做到了JavaScript這門語言的統一,尤其是谷歌在推出開源的v8解析引擎之後,在nodejs的幫助下,JavaScript這門語言完成了在客戶端上執行程式碼,到在伺服器上執行程式碼的蛻變,JavaScript成為了“全棧”語言。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       目前開源世界主流的三大網頁瀏覽器是Chrome、Mozilla、和Safari。Mozilla自然其本身就是JavaScript的發明者,其網頁渲染引擎叫做Gecko,JavaScript解析引擎叫做SpinderMonkey。Safari的網頁渲染引擎是Webkit,網頁解析引擎叫做JavaScriptCore,這也是我們今天的主角。


       Chrome的Blink渲染引擎最初脫胎於Webkit,後來Chrome為了更加激進地提高JavaScript的效能,另起爐灶,牟足了勁把所有黑科技都給安上,推出了完全效能優先的v8引擎,這也是為什麼剛剛使用Chrome時,能夠體驗到無與倫比的速度的原因,但是其缺陷也很明顯,Chrome帶來的開銷和佔用太大了,老電腦根本不敢開,幾個頁面就可以把效能全部吃光。


       今天的主角是聊JavaScriptCore,也就是Webkit的JavaScript解析引擎,Webkit是老牌的網頁渲染引擎了,距今已經有十多年的歷史,而且只要是與蘋果相關的平臺,都是強制使用Webkit渲染引擎的,比如說哪怕即使是Chrome,iOS版本的Chrome和Firefox其核心都是Webkit,因為蘋果不允許其使用其自己的內容,比較iOS是“我的地盤我做主”嘛。當然安卓和PC平臺蘋果就管不到了。


主角:Webkit


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       Webkit由於其老牌,並且脫胎於KHTML、GNOME等linux社群的特性,收到了移動裝置廠商的廣泛歡迎,比如我們的遊戲機PlayStation、任天堂Nintendo,都是採用的Webkit核心加個自己的皮膚就拿出來當做預設瀏覽器了,安卓在4.3之前其WebView也是採用的Webkit核心,因為大家知道Android這個專案是谷歌買過來的,要知道魯賓領導的安卓團隊可是誕生於2003年,那時候並沒有什麼更好的選擇。


       後來谷歌逐漸收緊對安卓的控制,一切以Chromium開源專案為基礎,以v8引擎為核心,打造超高速度的WebView排版引擎。最後,Webkit在PC平臺上當然也是全平臺的,macOS自不必說,Linux和Windows上都有其相對應的實現和產品。


       關鍵是在移動平臺上,Webkit佔據了全部瀏覽器和App市場的“半壁江山”,而移動平臺在今年雙十一中,佔據了92%的市場份額,這就很能說明問題,Webkit漏洞的影響能力,究竟可以大到什麼程度。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       iOS/macOS裝置越獄的典型流程,利用Webkit漏洞構造任意程式碼執行,再利用其XNU核心漏洞打穿核心,做好持久化之後越獄成功!


PlayStation、Nintendo裝置破解的典型流程,利用Webkit漏洞構造任意程式碼執行,再利用FreeBSD漏洞打穿核心,持久化之後破解成功!


       安卓也是一樣的道理,安卓由於其基於規則的許可權管理機制,跟Windows非常像,如果只要獲取資料的話,在網頁掛馬就可以了,都不需要給裝置進行root,就可以獲得全套的敏感資料。


       現在的很多Pwn型別的比賽中,獲取Webkit漏洞都是第一步,以Webkit漏洞為跳板,只要訪問一個他們提供的網站,先打穿Webkit,然後打穿核心,獲取整個系統最高許可權,最後彈計算,越獄是一模一樣的流程。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       去年有一個三叉戟漏洞就是非常典型的一個案例,其首先利用CVE-2016-4657的Webkit漏洞達到任意程式碼執行,然後綜合利用XNU的兩個漏洞在後臺“幫”機主靜默越獄,全部過程機主完全不知情,越獄完成之後再“為”使用者裝上全套監聽軟體;


       今年的Pwn2Own2017上爆出的CVE-2017-2533也是從Webkit的一個特殊的漏洞構造一個符號連結開始引爆攻擊鏈,最終完成的全套核心提權操作。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       其作者Saelo去年曾經在Phrack上放出了一整套Webkit的JavaScriptCore引擎漏洞利用思路,介紹的非常詳細,在這裡我們對這個經典案例進行學習,來掌握一整套漏洞利用的經典技巧,並最終給它“安裝”上Shellcode,在macOS平臺上彈出計算器。


CVE-2016-4622


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       接下來我們來看一下什麼是JavaScript引擎?JavaScript引擎是專門處理JavaScript指令碼並執行的虛擬機器,一般會附帶在網頁瀏覽器之中。第一個JavaScript引擎由布蘭登·艾克在網景公司開發,用於Netscape Navigator網頁瀏覽器中。JavaScriptCore就是一個JavaScript引擎。


       一個JavaScript引擎不外乎包括以下幾個部分:

1. 編譯器:將原始碼編譯成為抽象語法樹及位元組碼;

2. 直譯器:主要是接受位元組碼並且解釋執行;

3. JIT工具:可以將位元組碼或者語法樹轉化為本地機器碼的工具;

4. 垃圾回收器和其餘配件:符合記憶體和堆中的垃圾回收,幫助改進引擎的效能和功效。


       接下來我們來看看這個漏洞,由於JavaScriptCore的原始碼是開源的,我們可以git clone到本地,然後逐行閱讀和確認漏洞到底問題出在哪裡。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       該漏洞於2016年初被`yours truly`團隊發現,並且上報給ZDI團隊(番號ZDI-16-485),利用該漏洞可以導致JavaScript物件地址洩漏以及偽造。結合一些其他的提權手段的話,攻擊者可以做到瀏覽器中的任意程式碼執行。


       這個bug的`650552a`的git提交版本中被修復,下文中引用的程式碼來自於320b1fc版本,也就是最後一個受影響的版本,也是前文我們一直在討論和實驗的版本。這個漏洞是在2fa4973版本中引入的,歷時一年之久才被發現和修復,分配的CVE編碼為CVE-2016-4622。


       該漏洞主要存在於切片函式slice()之中。查詢該函式引用關係可知該函式位於Array.prototype.slice()的實現過程中,然後我們來到原始碼的相關位置,也就是./Source/JavaScriptCore/runtime/ArrayPrototype.cpp:848行的位置。


       在執行切片的時候,有兩種方式。


       如果陣列是密集儲存(FastPath)的本地陣列,則使用快速切片——`fastSlice`,使用給定的索引和長度將記憶體值直接寫入新的陣列。


       如果不是,則取不到快速路徑,這時候只有使用簡單的迴圈來獲取每個元素並將其新增到新的陣列。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       然後,在第二種較慢的方法中,屬性檢查會檢查陣列的邊界,而快速切片中卻沒有進行邊界檢查...這就有意思了,我們暫且不深入,先往下看,`result = constructEmptyArray(exec, nullptr, end - begin);`這行程式碼在構造空的result陣列的時候,用`end - begin`來的大小來構建陣列的長度麼?


       那我們用腳趾頭都能猜到程式猿的“臆測”絕對是這個差值肯定是小於原陣列的長度的咯?對呀,切片之後的陣列當然比切片之前的短的咯?然而,我們可以在JavaScript的型別轉化過程中耍一些手段,來毀掉程式猿的美夢,我們繼續往下看。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       上文程式碼中,在執行具體切片的前方,有一個argumentClampedIndexFromStartOrEnd 函式來決定end的大小,接受的引數是陣列原先的長度length。大家也看到了,所有地方都不會對新的`length`進行檢查。


       綜上所述,在PoC程式碼中的這句話:`var b = a.slice(0, {valueOf: function() { a.length = 0; return 10; }});`,如果我們在valueOf方法的內部修改陣列的長度,slice()函式卻會依舊使用之前的長度,導致指標訪問到了本不該它能訪問的內容,導致記憶體越界讀。


       在完美利用這一點之前,我們還必須保證陣列確實是可以被縮小的。怎樣來縮小陣列呢?使用陣列的.length屬性就好了,我們來看下`.length`方法的實現,在`JSArray::setLength`中(`./Source/JavaScriptCore/Runtime/JSArray.cpp`檔案的第431行):


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       為了節省開銷,陣列縮小的元素個數小於64,系統就不執行縮小了,折騰一下帶來的效能損耗還不如不節約呢。也就是說我們得“顯著”地縮小一下,系統才願意為我們折騰一下。


       這裡我們進行一次從100個元素縮小到10個元素的實驗,用Array.prototype.slice來切割陣列,同時在內部修改其長度。再來看一眼PoC.js,PoC先使用setLength來縮小陣列的長度,再使用ValueOf來返回固定的10,而在執行slice()方法的過程中不會再檢查a陣列的長度,因此取回的結果b,已經不再是a陣列裡的數字了,因為a陣列早就被置0了。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


        取回的結果應該是10個**undefined**資料才對,但是最終結果卻是一些浮點數,貌似我們的指標訪問到了一些,本不該我們可以訪問到的東西。這個東西是什麼呢?其實就是記憶體中緊靠著它的下一個物件的地址。接下來我們只要在這個位置進行構造一個物件,就可以通過記憶體越界讀直接獲取到它的地址,如果構造的非常完美,通過這個物件來獲取任意地址讀寫的能力。


Fake Object Injection


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       在構造物件的過程中有幾個難點,最為困難的是偽造SturctureID這個部分,我們就需要知道結構表中Float64Array的正確的結構ID。


       所以首先是預測結構ID,而結構ID是執行時動態產生和分配的,隨著各個引擎、版本的不同,結構ID也會改變。而且更要命的是,我們也不能隨便填個ID上去,因為ID有可能是字串的、符號連結的、各種物件的,甚至結構ID自己的。


       在MethodTable裡呼叫方法的時候,ID不對,JavaScriptCore就Crash了。這些ID是在引擎啟動的時候動態確定的,也就是說他們都會有ID,但是你就是不知道哪個是哪個的,總不能把受害人的電腦搶過來看一下吧。


       為了解決這個問題,我們想了個辦法,結合堆噴和instanceof()函式,來猜測一下結構ID。我們在堆裡噴幾千個Float64Array物件,帶著不同的結構ID。然後挨個檢查下這個物件是否被引擎識別為Float64Array物件,如果是,那麼它的結構ID肯定就是對的了。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       用instanceof()函式來判斷物件是否是Float64Array,如果不是,下一個再上。Instanceof()是一個非常安全的函式,它只檢查從結構ID裡抽出屬性來和預設的對比一下,其他沒有任何操作。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       找到正確的結構ID之後,終於可以放心大膽的開始偽造物件以劫持指標(弄虛作假)了。然後要過垃圾回收也有點小小的問題,我們又採用了一點小小的手段,由於時間的關係,這裡沒辦法做過多的深入,歡迎大家到bbs來參與學習和討論。


JIT Function Overwrite


       在得到任意地址讀寫的能力之後,我們使用覆蓋JIT編譯之後的函式的方式,來觸發我們的shellcode執行。由於現代瀏覽器全都採用JIT進行即時編譯,這就意味著把指令寫到記憶體、然後去執行這塊指令,也就是說這塊記憶體是可讀也可寫的,這就給我們執行shellcode幫了大忙。我們使用剛剛講完的任意地址記憶體讀寫功能來找到一塊經過JIT引擎編譯之後的JavaScript函式的地址,然後使用shellcode來覆蓋這塊地址,最終呼叫這個函式的時候,執行的就是我們的shellcode。請看下面的這段程式碼。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


Arbitrary Code Execution


        shellcode是從hacking team洩露的網路軍火庫中扒下來的。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       給Expolit裝上之後執行,立刻就彈出計算器了。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


       當然,這才是通向核心的第一步,接下來我們只要手中擁有核心的漏洞,就可以進行核心的破解了,還有一個好訊息就是,macOS/iOS的核心XNU也都是開源的。


一石多鳥——擊潰全線移動平臺瀏覽器(看雪2017安全開發者峰會演講回顧12)


這次的分享就到這裡,謝謝大家!


本演講PPT:https://bbs.pediy.com/post-update-1507290.htm

到此 《看雪安全開發者峰會的全部12個議題 》,內容就回顧完畢了~

我們明年再見!

相關文章