深入考察解釋型語言背后隱藏的攻擊面,Part 1(下)
接上文《深入考察解釋型語言背后隱藏的攻擊面,Part 1(上)》
在本文中,我們將與讀者一起深入考察解釋型語言背后隱藏的攻擊面。
通常情況下,在高級語言的內存管理功能的實現代碼中,往往存在著相對脆弱的基于C/C++的攻擊面。這種問題可能存在于語言本身的核心實現中,也可能存在于將向高級語言提供基于C/C++的庫的第三方語言生態系統中。本上一篇文章中,我們為讀者介紹了與此緊密相關的C格式字符串漏洞方面的知識,在本文中,我們將為讀者介紹這些底層實現是如何影響解釋型語言的安全性的。
Perl格式化的幽靈(CVE-2005-3962)
對于解釋型語言來說,提供自己的格式設置函數的情況并不少見,特別是Perl通過其較低級別的Perl_sv_vcatpvfn函數提供格式支持。這些低級C API為高級Perl API提供了許多核心格式化支持。它的格式化支持在語法上與C語言的格式化支持有些相似,因為它也支持直接參數訪問的概念,在Perl中,該參數被稱為“精確格式索引”,以及格式標識符%n。
當我們考慮到存在基于Perl的遠程服務應用程序明顯易受格式字符串錯誤的影響時,了解Perl內置的格式化支持就變得非常有趣。然而,由于沒有辦法在Perl級別上直接利用這些錯誤,因此,安全研究社區并沒有花太多精力來嘗試利用這些錯誤,而是通常將它們視為“只不過是一個bug而已”。
大約在2005年,在CVE-2005-3962的作者(Jack Louis)確定這些漏洞的可利用性之后,我對Perl格式字符串漏洞進行了更深入的研究。在Webmin中測試Jack Louis發現的Perl格式字符串錯誤時,他在Perl解釋器中遇到了一些可觀察到的崩潰。
事實證明,攻擊者確實可以通過Perl_sv_vcatpvfn中的格式化支持的C級實現來利用基于Perl的格式字符串漏洞。
Perl格式字符串的參數存儲在參數結構指針數組(稱為svargs)中,并為格式說明符(例如%1$n)提供準確的格式索引,以使用該索引從參數數組檢索適當的參數結構指針。當從數組中檢索關聯的參數結構指針時,Perl將根據格式字符串可用的參數數量,確保所提供的索引不超過數組的上限。這里的參數計數實際上保存一個帶符號的整型變量中,即svmax。也就是說,如果將格式字符串傳遞了1個參數,則svmax的值為1,并且檢查精確格式索引值不超過1。如果攻擊者提供了格式字符串,則不存在任何參數,這時svmax的值為0。
但是,精確格式索引也是帶符號的32位整數,并且其值完全由攻擊者提供的格式字符串控制。這意味著您可以將此參數數組索引設置為負值,這樣也可以通過針對svmax的帶符號上限檢查。
了解這一點后,漏洞的利用就變得相當簡單了。人們可以直接通過svargs數組索引指向任何指向攻擊者控制的數據的指針。這種受攻擊者控制的數據將被解釋為參數結構,其中包含指向值字段的指針。與熟悉的%n格式說明符相結合,攻擊者就能夠對受控位置執行受控寫入操作。使用這樣的寫原語,就可以覆蓋任何可寫進程內存的內容,而這些內容可以通過各種方式用于完整的過程控制中。
這是一個很好的例子,它為我們展示了Perl格式化實現的bug是如何轉化為安全漏洞的。結合Webmin中的格式字符串漏洞,攻擊者就能夠對Webmin發動遠程代碼執行(RCE)攻擊。
我們得出的結論是,即使在較高級語言級別上看起來似乎“只是一個bug”的問題,也需要對錯誤輸入的較低級別處理進行深入的考察,因為它們很可能會轉化為一個高危漏洞——即使人們曾經認為這樣的問題實際上是不可利用的。
PHP解釋器的無限潛力
從攻擊者的角度來看,他們一直對PHP解釋器的許多版本趨之若鶩。這是因為,攻擊者通常可以從解釋器控制角度和遠程API輸入角度對其發動攻擊:對于前者,攻擊者能夠執行任意PHP代碼;對于后者,攻擊者可以向潛在易受攻擊的PHP API提供惡意輸入。
利用PHP解釋器的一個更有趣的例子是反序列化攻擊。因為攻擊者曾經在PHP邏輯級別和核心解釋器級別攻陷過PHP的反序列化API。
人們普遍認為,對不受信任的用戶提供的數據進行反序列化是一個壞主意。在遠程應用程序的上下文中,任意的對象反序列化可能會導致相對簡單的PHP任意執行,這取決于應用程序命名空間中哪些類是可用的和允許的。這個主題超越了語言的界限,我們在幾乎所有支持反序列化的語言和應用程序框架中都看到了同樣的概念。
在攻擊者實現任意PHP執行攻擊后,他們可能會發現自己受到受限解釋器配置的制約,這時他們通常會探索解除這些限制的方法。歷史上流行的一種方法是濫用PHP解釋器本身的bug。最近在 https://bugs.php.net/bug.php?id=76047中可以找到這類攻擊的一個示例,其中可以利用debugbacktrace()函數中的釋放后使用(UAF)漏洞來完全控制PHP解釋器本身,并廢除所有配置方面的限制。
有時,即使提供了受控的PHP反序列化原語,由于攻擊者無法獲悉哪些類是可用的,或因應用程序命名空間存在某些限制,而無法將其轉化為任意PHP執行能力。這時,從上層下潛到較低的代碼層,很可能就能找到突破口。
由于PHP的反序列化API中存在大量內存管理不善問題,因此,長久以來,它們一直都是模糊測試和解釋器漏洞的熱門研究目標。
通過利用反序列化API在解釋器實現層面的內存管理不善問題,一個堅定的攻擊者能夠將一個原本不可利用的漏洞轉變成一個完全可利用的漏洞。實際上,已經出現過許多通過該攻擊面遠程利用PHP應用程序的實際例子。
最近的一個例子出現在Ruslan Habolov撰寫的一篇優秀的文章中,其中描述了他們如何利用低級PHP解釋器錯誤和高級PHP API的交互,對一個著名的現實目標發動RCE攻擊。
PHP反序列化在較高和較低級別實現的混合攻擊面是解釋型語言垂直攻擊面的另一個很好的例子。
把C帶入Python中:CVE-2014-1912漏洞
就本文而言,我們的第三個也是最后一個例子是CVE-2014-1912漏洞。這個漏洞存在于Python的socket.recvfrom_into函數中。
在Python2.5中引入的socket.recvfrom_into的預期用途是將數據接收到指定的Python字節數組中。但是,由于該函數缺乏明確的檢查,所以無法確保接收數據的目標緩沖區的大小足以容納指定數量的傳入數據。
例如socket.recvfrom_into(bytearray(256),512)就會觸發內存損壞問題。
后來,人們通過下面的代碼對其進行了修復:
- 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);
為了利用這個漏洞,應用程序必須顯式使用一個大于目標字節數組長度的長度參數,向該字節數組中讀入比分配給該數組的空間更多的數據。如果未提供長度參數,則該函數則默認使用目標字節數組本身的長度,因此,就不會發生內存損壞問題。
如果您使用的開發語言要求程序員自己負責內存管理的話,那么對于上述問題肯定不會陌生。您甚至可能認為,任何一個心智正常的人都不會做這樣的蠢事。因為很明顯,您不應該讀取比目標緩沖區中可用的數據更多的數據,對嗎?
這個案例的有趣之處就在這里:提供內存管理功能的編程語言的開發人員,通常會傾向信任該語言的實現。但是,當語言中存在諸如CVE-2014-1912之類的問題時,則可能會出現認知失調。
Python開發人員可能完全希望能夠在Python解釋器不受內存損壞的情況下使用s.recvfrom_into(bytearray(256), 512) 。實際上,如果您嘗試這個打過補丁后的程序,它現在的表現就像您所期望的那樣:
- Traceback (most recent call last):
- File "
- ValueError: nbytes is greater than the length of the buffer
- >>>
所以,這個問題的重點在于,使用被認為是內存安全的語言實現的函數,竟然存在內存破壞漏洞。但對于一個C程序員來說,面對CVE-2014-1912漏洞,他們多半是這樣理解的:“是的,就是這樣工作的呀,難道不是嗎?”
這給了我們一個教訓,即使是在宣稱內存安全的高級語言中,也絕對不要認為其內存管理絕對是安全的。當處理顯式操作靜態長度的可變緩沖區的API時,檢查你的長度與你的緩沖區相匹配是永遠不會有壞處的,即使在由于語言本身的原因不這樣做也可能是安全的情況下也是如此。
從攻擊者的角度來看,對于通常被認為是內存安全的API進行審計,常常會有意想不到的收獲。
小結
作為關于隱藏的C/C++攻擊面的系列文章的第一篇,我們已經探討了幾個實際的例子,為大家展示了內存安全的幻覺是如何麻痹開發人員,從而讓他們放松對應用程序中接受的輸入的警惕的。
本文探討的漏洞的變體可能而且確實存在于所有解釋器API中,這些API一般可以從更高級別訪問,并且其核心是用內存不安全語言實現的。這些漏洞是否可被利用,通常取決于開發人員為攻擊者提供了多大的回旋余地。
如果開發人員對輸入類型、大小和值范圍加以嚴格要求的話,通常能夠挫敗這種漏洞——例如,當接收到整數值時,將該值的范圍顯式地限制在應用程序上下文中有意義的范圍內,而不是將其開放給變量類型本身的取值范圍,這是一種防御性的編程習慣,對您將有很大的幫助。
在本系列的下一篇文章中,我們將深入研究解釋型語言的現代C/C++攻擊面,重點介紹流行解釋型語言框架的第三方庫生態系統,以及針對它們的新型攻擊方法。
本文翻譯自:https://securitylab.github.com/research/now-you-c-me