深入考察解釋型語(yǔ)言背后隱藏的攻擊面,Part 1(下)
接上文《深入考察解釋型語(yǔ)言背后隱藏的攻擊面,Part 1(上)》
在本文中,我們將與讀者一起深入考察解釋型語(yǔ)言背后隱藏的攻擊面。
通常情況下,在高級(jí)語(yǔ)言的內(nèi)存管理功能的實(shí)現(xiàn)代碼中,往往存在著相對(duì)脆弱的基于C/C++的攻擊面。這種問(wèn)題可能存在于語(yǔ)言本身的核心實(shí)現(xiàn)中,也可能存在于將向高級(jí)語(yǔ)言提供基于C/C++的庫(kù)的第三方語(yǔ)言生態(tài)系統(tǒng)中。本上一篇文章中,我們?yōu)樽x者介紹了與此緊密相關(guān)的C格式字符串漏洞方面的知識(shí),在本文中,我們將為讀者介紹這些底層實(shí)現(xiàn)是如何影響解釋型語(yǔ)言的安全性的。
Perl格式化的幽靈(CVE-2005-3962)
對(duì)于解釋型語(yǔ)言來(lái)說(shuō),提供自己的格式設(shè)置函數(shù)的情況并不少見(jiàn),特別是Perl通過(guò)其較低級(jí)別的Perl_sv_vcatpvfn函數(shù)提供格式支持。這些低級(jí)C API為高級(jí)Perl API提供了許多核心格式化支持。它的格式化支持在語(yǔ)法上與C語(yǔ)言的格式化支持有些相似,因?yàn)樗仓С种苯訁?shù)訪問(wèn)的概念,在Perl中,該參數(shù)被稱為“精確格式索引”,以及格式標(biāo)識(shí)符%n。
當(dāng)我們考慮到存在基于Perl的遠(yuǎn)程服務(wù)應(yīng)用程序明顯易受格式字符串錯(cuò)誤的影響時(shí),了解Perl內(nèi)置的格式化支持就變得非常有趣。然而,由于沒(méi)有辦法在Perl級(jí)別上直接利用這些錯(cuò)誤,因此,安全研究社區(qū)并沒(méi)有花太多精力來(lái)嘗試?yán)眠@些錯(cuò)誤,而是通常將它們視為“只不過(guò)是一個(gè)bug而已”。
大約在2005年,在CVE-2005-3962的作者(Jack Louis)確定這些漏洞的可利用性之后,我對(duì)Perl格式字符串漏洞進(jìn)行了更深入的研究。在Webmin中測(cè)試Jack Louis發(fā)現(xiàn)的Perl格式字符串錯(cuò)誤時(shí),他在Perl解釋器中遇到了一些可觀察到的崩潰。
事實(shí)證明,攻擊者確實(shí)可以通過(guò)Perl_sv_vcatpvfn中的格式化支持的C級(jí)實(shí)現(xiàn)來(lái)利用基于Perl的格式字符串漏洞。
Perl格式字符串的參數(shù)存儲(chǔ)在參數(shù)結(jié)構(gòu)指針數(shù)組(稱為svargs)中,并為格式說(shuō)明符(例如%1$n)提供準(zhǔn)確的格式索引,以使用該索引從參數(shù)數(shù)組檢索適當(dāng)?shù)膮?shù)結(jié)構(gòu)指針。當(dāng)從數(shù)組中檢索關(guān)聯(lián)的參數(shù)結(jié)構(gòu)指針時(shí),Perl將根據(jù)格式字符串可用的參數(shù)數(shù)量,確保所提供的索引不超過(guò)數(shù)組的上限。這里的參數(shù)計(jì)數(shù)實(shí)際上保存一個(gè)帶符號(hào)的整型變量中,即svmax。也就是說(shuō),如果將格式字符串傳遞了1個(gè)參數(shù),則svmax的值為1,并且檢查精確格式索引值不超過(guò)1。如果攻擊者提供了格式字符串,則不存在任何參數(shù),這時(shí)svmax的值為0。
但是,精確格式索引也是帶符號(hào)的32位整數(shù),并且其值完全由攻擊者提供的格式字符串控制。這意味著您可以將此參數(shù)數(shù)組索引設(shè)置為負(fù)值,這樣也可以通過(guò)針對(duì)svmax的帶符號(hào)上限檢查。
了解這一點(diǎn)后,漏洞的利用就變得相當(dāng)簡(jiǎn)單了。人們可以直接通過(guò)svargs數(shù)組索引指向任何指向攻擊者控制的數(shù)據(jù)的指針。這種受攻擊者控制的數(shù)據(jù)將被解釋為參數(shù)結(jié)構(gòu),其中包含指向值字段的指針。與熟悉的%n格式說(shuō)明符相結(jié)合,攻擊者就能夠?qū)κ芸匚恢脠?zhí)行受控寫入操作。使用這樣的寫原語(yǔ),就可以覆蓋任何可寫進(jìn)程內(nèi)存的內(nèi)容,而這些內(nèi)容可以通過(guò)各種方式用于完整的過(guò)程控制中。
這是一個(gè)很好的例子,它為我們展示了Perl格式化實(shí)現(xiàn)的bug是如何轉(zhuǎn)化為安全漏洞的。結(jié)合Webmin中的格式字符串漏洞,攻擊者就能夠?qū)ebmin發(fā)動(dòng)遠(yuǎn)程代碼執(zhí)行(RCE)攻擊。
我們得出的結(jié)論是,即使在較高級(jí)語(yǔ)言級(jí)別上看起來(lái)似乎“只是一個(gè)bug”的問(wèn)題,也需要對(duì)錯(cuò)誤輸入的較低級(jí)別處理進(jìn)行深入的考察,因?yàn)樗鼈兒芸赡軙?huì)轉(zhuǎn)化為一個(gè)高危漏洞——即使人們?cè)?jīng)認(rèn)為這樣的問(wèn)題實(shí)際上是不可利用的。
PHP解釋器的無(wú)限潛力
從攻擊者的角度來(lái)看,他們一直對(duì)PHP解釋器的許多版本趨之若鶩。這是因?yàn)?,攻擊者通常可以從解釋器控制角度和遠(yuǎn)程API輸入角度對(duì)其發(fā)動(dòng)攻擊:對(duì)于前者,攻擊者能夠執(zhí)行任意PHP代碼;對(duì)于后者,攻擊者可以向潛在易受攻擊的PHP API提供惡意輸入。
利用PHP解釋器的一個(gè)更有趣的例子是反序列化攻擊。因?yàn)楣粽咴?jīng)在PHP邏輯級(jí)別和核心解釋器級(jí)別攻陷過(guò)PHP的反序列化API。
人們普遍認(rèn)為,對(duì)不受信任的用戶提供的數(shù)據(jù)進(jìn)行反序列化是一個(gè)壞主意。在遠(yuǎn)程應(yīng)用程序的上下文中,任意的對(duì)象反序列化可能會(huì)導(dǎo)致相對(duì)簡(jiǎn)單的PHP任意執(zhí)行,這取決于應(yīng)用程序命名空間中哪些類是可用的和允許的。這個(gè)主題超越了語(yǔ)言的界限,我們?cè)趲缀跛兄С址葱蛄谢恼Z(yǔ)言和應(yīng)用程序框架中都看到了同樣的概念。
在攻擊者實(shí)現(xiàn)任意PHP執(zhí)行攻擊后,他們可能會(huì)發(fā)現(xiàn)自己受到受限解釋器配置的制約,這時(shí)他們通常會(huì)探索解除這些限制的方法。歷史上流行的一種方法是濫用PHP解釋器本身的bug。最近在 https://bugs.php.net/bug.php?id=76047中可以找到這類攻擊的一個(gè)示例,其中可以利用debugbacktrace()函數(shù)中的釋放后使用(UAF)漏洞來(lái)完全控制PHP解釋器本身,并廢除所有配置方面的限制。
有時(shí),即使提供了受控的PHP反序列化原語(yǔ),由于攻擊者無(wú)法獲悉哪些類是可用的,或因應(yīng)用程序命名空間存在某些限制,而無(wú)法將其轉(zhuǎn)化為任意PHP執(zhí)行能力。這時(shí),從上層下潛到較低的代碼層,很可能就能找到突破口。
由于PHP的反序列化API中存在大量?jī)?nèi)存管理不善問(wèn)題,因此,長(zhǎng)久以來(lái),它們一直都是模糊測(cè)試和解釋器漏洞的熱門研究目標(biāo)。
通過(guò)利用反序列化API在解釋器實(shí)現(xiàn)層面的內(nèi)存管理不善問(wèn)題,一個(gè)堅(jiān)定的攻擊者能夠?qū)⒁粋€(gè)原本不可利用的漏洞轉(zhuǎn)變成一個(gè)完全可利用的漏洞。實(shí)際上,已經(jīng)出現(xiàn)過(guò)許多通過(guò)該攻擊面遠(yuǎn)程利用PHP應(yīng)用程序的實(shí)際例子。
最近的一個(gè)例子出現(xiàn)在Ruslan Habolov撰寫的一篇優(yōu)秀的文章中,其中描述了他們?nèi)绾卫玫图?jí)PHP解釋器錯(cuò)誤和高級(jí)PHP API的交互,對(duì)一個(gè)著名的現(xiàn)實(shí)目標(biāo)發(fā)動(dòng)RCE攻擊。
PHP反序列化在較高和較低級(jí)別實(shí)現(xiàn)的混合攻擊面是解釋型語(yǔ)言垂直攻擊面的另一個(gè)很好的例子。
把C帶入Python中:CVE-2014-1912漏洞
就本文而言,我們的第三個(gè)也是最后一個(gè)例子是CVE-2014-1912漏洞。這個(gè)漏洞存在于Python的socket.recvfrom_into函數(shù)中。
在Python2.5中引入的socket.recvfrom_into的預(yù)期用途是將數(shù)據(jù)接收到指定的Python字節(jié)數(shù)組中。但是,由于該函數(shù)缺乏明確的檢查,所以無(wú)法確保接收數(shù)據(jù)的目標(biāo)緩沖區(qū)的大小足以容納指定數(shù)量的傳入數(shù)據(jù)。
例如socket.recvfrom_into(bytearray(256),512)就會(huì)觸發(fā)內(nèi)存損壞問(wèn)題。
后來(lái),人們通過(guò)下面的代碼對(duì)其進(jìn)行了修復(fù):
- diff -r e6358103fe4f Modules/socketmodule.c
- --- a/Modules/socketmodule.c Wed Jan 08 20:44:37 2014 -0800
- +++ b/Modules/socketmodule.c Sun Jan 12 13:21:19 2014 -0800
- @@ -2877,6 +2877,14 @@
- recvlen = buflen;
- }
- + /* Check if the buffer is large enough */
- + if (buflen < recvlen) {
- + PyBuffer_Release(&pbuf);
- + PyErr_SetString(PyExc_ValueError,
- + "buffer too small for requested bytes");
- + return NULL;
- + }
- +
- readlen = sock_recvfrom_guts(s, buf, recvlen, flags, &addr);
- if (readlen < 0) {
- PyBuffer_Release(&pbuf);
為了利用這個(gè)漏洞,應(yīng)用程序必須顯式使用一個(gè)大于目標(biāo)字節(jié)數(shù)組長(zhǎng)度的長(zhǎng)度參數(shù),向該字節(jié)數(shù)組中讀入比分配給該數(shù)組的空間更多的數(shù)據(jù)。如果未提供長(zhǎng)度參數(shù),則該函數(shù)則默認(rèn)使用目標(biāo)字節(jié)數(shù)組本身的長(zhǎng)度,因此,就不會(huì)發(fā)生內(nèi)存損壞問(wèn)題。
如果您使用的開發(fā)語(yǔ)言要求程序員自己負(fù)責(zé)內(nèi)存管理的話,那么對(duì)于上述問(wèn)題肯定不會(huì)陌生。您甚至可能認(rèn)為,任何一個(gè)心智正常的人都不會(huì)做這樣的蠢事。因?yàn)楹苊黠@,您不應(yīng)該讀取比目標(biāo)緩沖區(qū)中可用的數(shù)據(jù)更多的數(shù)據(jù),對(duì)嗎?
這個(gè)案例的有趣之處就在這里:提供內(nèi)存管理功能的編程語(yǔ)言的開發(fā)人員,通常會(huì)傾向信任該語(yǔ)言的實(shí)現(xiàn)。但是,當(dāng)語(yǔ)言中存在諸如CVE-2014-1912之類的問(wèn)題時(shí),則可能會(huì)出現(xiàn)認(rèn)知失調(diào)。
Python開發(fā)人員可能完全希望能夠在Python解釋器不受內(nèi)存損壞的情況下使用s.recvfrom_into(bytearray(256), 512) 。實(shí)際上,如果您嘗試這個(gè)打過(guò)補(bǔ)丁后的程序,它現(xiàn)在的表現(xiàn)就像您所期望的那樣:
- Traceback (most recent call last):
- File "
- ValueError: nbytes is greater than the length of the buffer
- >>>
所以,這個(gè)問(wèn)題的重點(diǎn)在于,使用被認(rèn)為是內(nèi)存安全的語(yǔ)言實(shí)現(xiàn)的函數(shù),竟然存在內(nèi)存破壞漏洞。但對(duì)于一個(gè)C程序員來(lái)說(shuō),面對(duì)CVE-2014-1912漏洞,他們多半是這樣理解的:“是的,就是這樣工作的呀,難道不是嗎?”
這給了我們一個(gè)教訓(xùn),即使是在宣稱內(nèi)存安全的高級(jí)語(yǔ)言中,也絕對(duì)不要認(rèn)為其內(nèi)存管理絕對(duì)是安全的。當(dāng)處理顯式操作靜態(tài)長(zhǎng)度的可變緩沖區(qū)的API時(shí),檢查你的長(zhǎng)度與你的緩沖區(qū)相匹配是永遠(yuǎn)不會(huì)有壞處的,即使在由于語(yǔ)言本身的原因不這樣做也可能是安全的情況下也是如此。
從攻擊者的角度來(lái)看,對(duì)于通常被認(rèn)為是內(nèi)存安全的API進(jìn)行審計(jì),常常會(huì)有意想不到的收獲。
小結(jié)
作為關(guān)于隱藏的C/C++攻擊面的系列文章的第一篇,我們已經(jīng)探討了幾個(gè)實(shí)際的例子,為大家展示了內(nèi)存安全的幻覺(jué)是如何麻痹開發(fā)人員,從而讓他們放松對(duì)應(yīng)用程序中接受的輸入的警惕的。
本文探討的漏洞的變體可能而且確實(shí)存在于所有解釋器API中,這些API一般可以從更高級(jí)別訪問(wèn),并且其核心是用內(nèi)存不安全語(yǔ)言實(shí)現(xiàn)的。這些漏洞是否可被利用,通常取決于開發(fā)人員為攻擊者提供了多大的回旋余地。
如果開發(fā)人員對(duì)輸入類型、大小和值范圍加以嚴(yán)格要求的話,通常能夠挫敗這種漏洞——例如,當(dāng)接收到整數(shù)值時(shí),將該值的范圍顯式地限制在應(yīng)用程序上下文中有意義的范圍內(nèi),而不是將其開放給變量類型本身的取值范圍,這是一種防御性的編程習(xí)慣,對(duì)您將有很大的幫助。
在本系列的下一篇文章中,我們將深入研究解釋型語(yǔ)言的現(xiàn)代C/C++攻擊面,重點(diǎn)介紹流行解釋型語(yǔ)言框架的第三方庫(kù)生態(tài)系統(tǒng),以及針對(duì)它們的新型攻擊方法。
本文翻譯自:https://securitylab.github.com/research/now-you-c-me