最近在做ctf的時候,碰見了好幾次關於php僞協議的妙用,所以通過學習整理出相關知識
文檔:http://cn2.php.net/manual/zh/wrappers.php.php#refsect2-wrappers.php-unknown-descriptioo
php僞協議,事實上是其支持的協議與封裝協議
支持的種類有這12種
*
file:// — 訪問本地文件系統
*
http:// — 訪問 HTTP(s) 網址
*
ftp:// — 訪問 FTP(s) URLs
*
php:// — 訪問各個輸入/輸出流(I/O streams)
*
zlib:// — 壓縮流
*
data:// — 數據(RFC 2397)
*
glob:// — 查找匹配的文件路徑模式
*
phar:// — PHP 歸檔
*
ssh2:// — Secure Shell 2
*
rar:// — RAR
*
ogg:// — 音頻流
*
expect:// — 處理交互式的流
先整理一下關於php://
的用法
php://
PHP 提供了一些雜項輸入/輸出(IO)流,允許訪問 PHP 的輸入輸出流、標準輸入輸出和錯誤描述符, 內存中、磁盤備份的臨時文件流以及可以操作其他讀取寫入文件資源的過濾器。
php://stdin, php://stdout 和 php://stderr
php://stdin
、php://stdout
和 php://stderr
允許直接訪問 PHP 進程相應的輸入或者輸出流。 數據流引用了複製的文件描述符,所以如果你打開php://stdin
並在之後關了它, 僅是關閉了複製品,真正被引用的 STDIN 並不受影響。 推薦簡單使用常量 STDIN、 STDOUT 和 STDERR 來代替手工打開這些封裝器。
php://stdin
是隻讀的,php://stdout
和 php://stderr
是隻寫的。
舉例:
php://stdin
<?php
while($line = fopen('php://stdin','r'))
{//open our file pointer to read from stdin
echo $line."\n";
echo fgets($line);//讀取
}
?>
可以看到打開了一個文件指針進行讀取
php://stdout
<?php
$fd = fopen('php://stdout', 'w');
if ($fd) {
echo $fd."\n";
fwrite($fd, "這是一個測試");
fwrite($fd, "\n");
fclose($fd);
}
?>
可以看到打開了一個文件指針進行寫入
php://stderr
<?php
$stderr = fopen( 'php://stderr', 'w' );
echo $stderr."\n";
fwrite($stderr, "lalala" );
fclose($stderr);
?>
可以看到打開了一個文件指針進行寫入
php://input
php://input
是個可以訪問請求的原始數據的只讀流。因爲它不依賴於特定的 php.ini
指令。
注:enctype=”multipart/form-data” 的時候 php://input
是無效的。
舉例:
就拿最近的HBCTF的一道題吧
相關源碼:
<!--
$user = $_GET["user"];
$file = $_GET["file"];
$pass = $_GET["pass"];
if(isset($user)&&(file_get_contents($user,'r')==="the user is admin")){
echo "hello admin!<br>";
include($file); //class.php
}else{
echo "you are not admin ! ";
}
-->
php://output
php://output
是一個只寫的數據流, 允許你以 print 和 echo 一樣的方式 寫入到輸出緩衝區。
舉例:
<?php
$out=fopen("php://stdout", 'w');
echo $out."\n";
fwrite($out , "this is a test");
fclose($out);
?>
php://fd
php://fd
允許直接訪問指定的文件描述符。 例如 php://fd/3
引用了文件描述符 3。
php://memory 和 php://temp
php://memory
和 php://temp
是一個類似文件 包裝器的數據流,允許讀寫臨時數據。 兩者的唯一區別是
php://memory
總是把數據儲存在內存中, 而 php://temp
會在內存量達到預定義的限制後(默認是 2MB)存入臨時文件中。 臨時文件位置的決定和
sys_get_temp_dir()
的方式一致。
php://temp
的內存限制可通過添加 /maxmemory:NN
來控制,NN
是以字節爲單位、保留在內存的最大數據量,超過則使用臨時文件。
php://filter
可以說這是最常使用的一個僞協議,一般可以利用進行任意文件讀取。 php://filter
是一種元封裝器, 設計用於數據流打開時的篩選過濾應用。 這對於一體式(all-in-one)的文件函數非常有用,類似
readfile()
、 file()
和 file_get_contents()
, 在數據流內容讀取之前沒有機會應用其他過濾器。
php://filter 參數
名稱 | 描述 |
---|---|
resource=<要過濾的數據流> | 這個參數是必須的。它指定了你要篩選過濾的數據流。 |
read=<讀鏈的篩選列表> | 該參數可選。可以設定一個或多個過濾器名稱,以管道符(_ |
write=<寫鏈的篩選列表> | 該參數可選。可以設定一個或多個過濾器名稱,以管道符(_ |
<;兩個鏈的篩選列表> | 任何沒有以 read= 或 write= 作前綴 的篩選器列表會視情況應用於讀或寫鏈。 |
封裝協議摘要(針對 php://filter,參考被篩選的封裝器。)
屬性 | 支持 |
---|---|
受限於 allow_url_include | 僅 php://input、 php://stdin、 php://memory 和 php://temp。 |
允許讀取 | 僅 php://stdin、 php://input、 php://fd、 php://memory 和 php://temp。 |
允許寫入 | 僅 php://stdout、 php://stderr、 php://output、 php://fd、 php://memory 和 php://temp。 |
允許追加 | 僅 php://stdout、 php://stderr、 php://output、 php://fd、 php://memory 和 php://temp(等於寫入) |
允許同時讀寫 | 僅 php://fd、 php://memory 和 php://temp。 |
支持 stat() | 僅 php://memory 和 php://temp。 |
僅僅支持 stream_select() | php://stdin、 php://stdout、 php://stderr、 php://fd 和 php://temp。 |
舉例:
依舊拿HBCTF舉例好啦
明顯將class.php
的代碼以base64的形式輸出,當然也可以試試字符轉成rot13形式
這就涉及過濾器的靈活使用
php://filter/read=<讀鏈需要應用的過濾器列表>
這個參數採用一個或以管道符 | 分隔的多個過濾器名稱。
過濾器
過濾器有很多種,有字符串過濾器、轉換過濾器、壓縮過濾器、加密過濾器
字符串過濾器
- string.rot13
進行rot13轉換 string.toupper
將字符全部大寫 string.tolower
將字符全部小寫 string.strip_tags
去除空字符、HTML 和 PHP 標記後的結果
着重介紹一下這個,功能類似於strip_tags()函數,若不想某些字符不被消除,後面跟上字符,可利用字符串或是數組兩種方式
舉例
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.rot13');
echo "rot13:";
fwrite($fp, "This is a test.\n");
fclose($fp);
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.toupper');
echo "Upper:";
fwrite($fp, "This is a test.\n");
fclose($fp);
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.tolower');
echo "Lower:";
fwrite($fp, "This is a test.\n");
fclose($fp);
$fp = fopen('php://output', 'w');
echo "Del1:";
stream_filter_append($fp, 'string.strip_tags', STREAM_FILTER_WRITE);
fwrite($fp, "<b>This is a test.</b>!!!!<h1>~~~~</h1>\n");
fclose($fp);
$fp = fopen('php://output', 'w');
echo "Del2:";
stream_filter_append($fp, 'string.strip_tags', STREAM_FILTER_WRITE, "<b>");
fwrite($fp, "<b>This is a test.</b>!!!!<h1>~~~~</h1>\n");
fclose($fp);
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'string.strip_tags', STREAM_FILTER_WRITE, array('b','h1'));
echo "Del3:";
fwrite($fp, "<b>This is a test.</b>!!!!<h1>~~~~</h1>\n");
fclose($fp);
?>
轉換過濾器
- convert.base64-encode & convert.base64-decode
base64 編碼解碼
convert.base64-encode和convert.base64-decode使用這兩個過濾器等同於分別用base64_encode()
和base64_decode()
函數處理所有的流數據。 convert.base64-encode支持以一個關聯數組給出的參數。如果給出了line-length,base64 輸出將被用 line-length個字符爲長度而截成塊。如果給出了* line-break-chars*,每塊將被用給出的字符隔開。這些參數的效果和用base64_encode()
再加上chunk_split()
相同。 convert.quoted-printable-encode & convert.quoted-printable-decode
quoted-printable 編碼解碼
convert.quoted-printable-encode和 convert.quoted-printable-decode等同於用quoted_printable_decode()
函數處理所有的流數據。沒有和* convert.quoted-printable-encode*相對應的函數。* convert.quoted-printable-encode*支持以一個關聯數組給出的參數。除了支持和 convert.base64-encode一樣的附加參數外,* convert.quoted-printable-encode*還支持布爾參數 binary和 force-encode-first。 convert.base64-decode只支持 line-break-chars參數作爲從編碼載荷中剝離的類型提示。
舉例:
<?php
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.base64-encode');
echo "base64-encode:";
fwrite($fp, "This is a test.\n");
fclose($fp);
$param = array('line-length' => 8, 'line-break-chars' => "\n");
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.base64-encode', STREAM_FILTER_WRITE, $param);
echo "\nbase64-encode-split:\n";
fwrite($fp, "This is a test.\n");
fclose($fp);
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.base64-decode');
echo "\nbase64-decode:";
fwrite($fp, "VGhpcyBpcyBhIHRlc3QuCg==\n");
fclose($fp);
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.quoted-printable-encode');
echo "quoted-printable-encode:";
fwrite($fp, "This is a test.\n");
fclose($fp);
$fp = fopen('php://output', 'w');
stream_filter_append($fp, 'convert.quoted-printable-decode');
echo "\nquoted-printable-decode:";
fwrite($fp, "This is a test.=0A");
fclose($fp);
?>
壓縮過濾器
- zlib.deflate和 zlib.inflate
zlib.deflate(壓縮)和 zlib.inflate(解壓)實現了定義與 » RFC 1951的壓縮算法。 deflate過濾器可以接受以一個關聯數組傳遞的最多三個參數。* level*定義了壓縮強度(1-9)。數字更高通常會產生更小的載荷,但要消耗更多的處理時間。存在兩個特殊壓縮等級:0(完全不壓縮)和 -1(zlib 內部默認值,目前是 6)。 window是壓縮回溯窗口大小,以二的次方表示。更高的值(大到 15 —— 32768 字節)產生更好的壓縮效果但消耗更多內存,低的值(低到 9 —— 512 字節)產生產生較差的壓縮效果但內存消耗低。目前默認的 window大小是 15。 memory用來指示要分配多少工作內存。合法的數值範圍是從 1(最小分配)到 9(最大分配)。內存分配僅影響速度,不會影響生成的載荷的大小。
Note: 因爲最常用的參數是壓縮等級,也可以提供一個整數值作爲此參數(而不用數組)。 bzip2.compress和 bzip2.decompress
bzip2.compress過濾器接受以一個關聯數組給出的最多兩個參數:* blocks*是從 1 到 9 的整數值,指定分配多少個 100K 字節的內存塊作爲工作區。 work是 0 到 250 的整數值,指定在退回到一個慢一些,但更可靠的算法之前做多少次常規壓縮算法的嘗試。調整此參數僅影響到速度,壓縮輸出和內存使用都不受此設置的影響。將此參數設爲 0 指示 bzip 庫使用內部默認算法。 bzip2.decompress過濾器僅接受一個參數,可以用普通的布爾值傳遞,或者用一個關聯數組中的* small*單元傳遞。當* small*設爲&true
; 值時,指示 bzip 庫用最小的內存佔用來執行解壓縮,代價是速度會慢一些。
加密過濾器
_mcrypt.*_
和 _mdecrypt.*_
使用 libmcrypt 提供了對稱的加密和解密。這兩組過濾器都支持
mcrypt 擴展庫中相同的算法,格式爲_mcrypt.ciphername_
,其中 ciphername
是密碼的名字,將被傳遞給
mcrypt_module_open()
。有以下五個過濾器參數可用:
mcrypt 過濾器參數
參數 | 是否必須 | 默認值 | 取值舉例 |
---|---|---|---|
mode | 可選 | cbc | cbc, cfb, ecb, nofb, ofb, stream |
algorithms_dir | 可選 | ini_get(‘mcrypt.algorithms_dir’) | algorithms 模塊的目錄 |
modes_dir | 可選 | ini_get(‘mcrypt.modes_dir’) | modes 模塊的目錄 |
iv | 必須 | N/A | 典型爲 8,16 或 32 字節的二進制數據。根據密碼而定 |
key | 必須 | N/A | 典型爲 8,16 或 32 字節的二進制數據。根據密碼而定 |