在這里了解當(dāng)今互聯(lián)網(wǎng)的最新動(dòng)態(tài)
在這里了解當(dāng)今
在做一個(gè)Bug bounty的項(xiàng)目時(shí),我發(fā)現(xiàn)了一個(gè)網(wǎng)站,它有一個(gè)很有意思的功能:它能讓我使用一些用戶控制的表達(dá)式過濾數(shù)據(jù)。比如說,我可以輸入 book.proce > 100 表達(dá)式,使它只展示一些價(jià)格高于 $100 的書。直接輸入 true 可以列出所有的書,輸入 false 就會(huì)一本書都不顯示。所以我可以知道我使用的表達(dá)式是對(duì)是錯(cuò)。
這個(gè)功能引起了我的注意,因此我嘗試著輸入更復(fù)雜的表達(dá)式,比如 (1+1).toString()==="2" (值為 true)和 (1+1).toString()===5 (值為 false)。這顯然是 JavaScript 代碼,因此我猜測(cè)這個(gè)表達(dá)式在 NodeJS server 中被作為參數(shù)傳給了一個(gè)類似 eval 的函數(shù)。到了這里,我感覺我快要發(fā)現(xiàn)一個(gè)遠(yuǎn)程執(zhí)行漏洞了。然而,當(dāng)我想要測(cè)試一個(gè)更加復(fù)雜的表達(dá)式時(shí),它報(bào)錯(cuò)了,提示我輸入的表達(dá)式非法。我猜測(cè)這應(yīng)該不是 eval 函數(shù),而應(yīng)該是一個(gè) JavaScript 的沙箱。
沙箱都是再一個(gè)受控的環(huán)境中執(zhí)行非可信的代碼,而這一般都是很難確保不出問題的。大多數(shù)情況下,我們都能找到一些方法來繞過沙箱的保護(hù)機(jī)制。特別是對(duì)于像 JavaScript 這樣復(fù)雜,特性臃腫的語言,沙箱的漏洞可能會(huì)更多。這個(gè)問題吸引了我的注意,所以我決定花一些時(shí)間來打破這個(gè)沙箱的防護(hù)機(jī)制。我學(xué)習(xí)了 JavaScript 的一些內(nèi)部機(jī)制,用來發(fā)現(xiàn)和利用沙箱的 RCE 漏洞。
我首先要確定這個(gè)網(wǎng)站使用了什么庫來實(shí)現(xiàn)的沙箱,因?yàn)?NodeJS 中有幾十個(gè)類似的庫,在許多情況下,它們都存在一些漏洞。當(dāng)然,也有可能這是一個(gè)開發(fā)人員自己寫的庫,但是我忽略了這種可能,因?yàn)閱渭兊木W(wǎng)站開發(fā)人員不太可能花大把的時(shí)間來做這種語言底層的事情。
最后,通過分析網(wǎng)站的錯(cuò)誤日志,我推斷處它們應(yīng)該使用的是 static-eval ,這是一個(gè)不太流行的庫(由 substack寫的,這個(gè)人再 NodeJS 社區(qū)中非常有名)。盡管它的文檔中寫了,它并不是被設(shè)計(jì)來作為沙箱的,但是我仍然十分確定他在這個(gè)網(wǎng)站中被用作了沙箱。
繞過 static-eval
static-eval的基本思想是使用 esprima 庫解析 JS 表達(dá)式并將其轉(zhuǎn)換為 AST(抽象語法樹)。static-eval 通過分析這個(gè)AST 對(duì)我輸入的表達(dá)式進(jìn)行評(píng)估。如果發(fā)現(xiàn)一些奇怪的東西,函數(shù)就拋出異常,我的代碼就不會(huì)執(zhí)行。一開始,我有點(diǎn)灰心喪氣,因?yàn)槲乙庾R(shí)到沙箱對(duì)它所接受的表達(dá)式有很大的限制。我甚至不能在表達(dá)式中使用 for 或 while 語句,所以做一些需要迭代算法的事情是幾乎不可能的。無論如何,我堅(jiān)持著繼續(xù)尋找漏洞。
一開始,我并沒有發(fā)現(xiàn)任何 bug,所以我查看了 static-eval 項(xiàng)目的 commits 和 pull requests 的所有記錄。 我發(fā)現(xiàn) pull requests #18 修復(fù)了兩個(gè)沙箱逃逸的 bug,而這正是我所尋找的。 我還發(fā)現(xiàn)了這個(gè) pr 作者的博客,在這篇文章里,他深入分析了這個(gè)漏洞。同時(shí),我在立即在這個(gè)網(wǎng)站中測(cè)試這個(gè)漏洞,然而,他們使用了一個(gè)新版本的 static-eval,這個(gè)版本的 static-eval 早就把這個(gè)漏洞補(bǔ)上了。我立即嘗試在我測(cè)試的網(wǎng)站中使用這種技術(shù),但不幸的是,他們使用的是更新的靜態(tài)評(píng)估版本,已經(jīng)修補(bǔ)了這個(gè)漏洞。但是,知道有人發(fā)現(xiàn)過這種漏洞,這讓我更加自信,所以我一直在尋找繞過它的新方法。
接下來,我深入分析了這兩個(gè)漏洞,以期望能夠?yàn)槲艺业叫碌穆┒磳ふ异`感。
第一個(gè)漏洞:
第一個(gè)漏洞使用了 function constructor 來生成惡意函數(shù)。
這種技術(shù)經(jīng)常用于繞過沙箱。例如,大多數(shù)通過繞過 angular.js 沙箱來獲得 XSS 的方法都使用一些有效載荷,它們最終都會(huì)訪問和調(diào)用 function constructor。下面的表達(dá)式用來演示漏洞,能夠打印系統(tǒng)環(huán)境變量(這應(yīng)該是不被允許的,因?yàn)樯诚鋺?yīng)該阻止它):


這是一個(gè)非常簡(jiǎn)單的修復(fù),但它的效果出奇的好。當(dāng)然,function constructor 只在函數(shù)中可用。所以我無法訪問它。對(duì)象的 typeof 是不能被修改的,因此任何函數(shù)的 typeof 都將被設(shè)置為 function。
資訊列表