題目:?
?我仿佛見到了一位故人。。。也難怪,題目就是ZJCTF
按要求提交/?text=data://,I have a dream&file=next.php后:?
......不太行,好像得用file=php://filter/convert.base64-encode/resource=next.php?
耶?那?file=php://filter/convert.base64-encode/resource=next.php 和 file=next.php在這里有啥區別啊?
file=php://filter/convert.base64-encode/resource=next.php 和 file=next.php
示例:如果next.php為 <?php echo "hello"; ?>
1.file=next.php: 直接輸出hello(include函數會將括號內的文件內容當作php來解析)
2.file=php://filter/convert.base64-encode/resource=next.php:
php://filter
?是PHP的流包裝器,允許在讀取文件時對內容進行過濾處理。convert.base64-encode
?過濾器會將文件內容轉換為Base64編碼格式。- 當使用?
include
?包含此路徑時,PHP會先讀取?next.php
?的內容,經過Base64編碼后,再嘗試將編碼后的內容作為PHP代碼執行。
關鍵點在于base64編碼后的東西不再是有效代碼,php無法解析,在面對無效代碼這種情況下php可能會將編碼后的內容作為“原始輸出”返回
這樣就能直接返回next.php的源代碼了?
?源代碼
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;function complex($re, $str) {return preg_replace('/(' . $re . ')/ei','strtolower("\\1")',$str);
}foreach($_GET as $re => $str) {echo complex($re, $str). "\n";
}function getFlag(){@eval($_GET['cmd']);
}
代碼一些地方需要注意:
preg_replace('/(' . $re . ')/ei','strtolower("\\1")',$str);
preg_replace($要搜索的正則表達式的模式, $用于替換匹配項的字符串, $被進行搜索和替換的字符串);?所以關于這里的正則表達式模塊:'/('.$re.')/ei',
注意preg_replace在調用的時候需要將參數用 ' ' 括起來
其中$re代表用戶傳入的正則表達式模式,通過字符串拼接的方式,將$re嵌入到整體正則表達式中? / 用于表示正則表達式的開始和結束,( 和 ):是捕獲組的符號,用于將匹配到的內容捕獲起來,以便后續引用,e
?修飾符會使?preg_replace
?在替換字符串時執行其中的 PHP 代碼,i
?修飾符表示不區分大小寫進行匹配。
strtolower("\\1")
\\1
:這是對捕獲組的引用,代表第一個捕獲組匹配到的內容。strtolower
用于將字符串轉換為小寫。所以這句話意思是將捕獲到的內容轉換成小寫
捕獲組和反向引用
主要用于引用之前匹配的捕獲組的內容。它允許在同一個正則表達式中復用已匹配的文本,常用于模式重復匹配或替換操作。
捕獲組:()括起來的內容就是捕獲組,比如說(\d+)匹配一個或者多個內容,并記錄匹配的內容
反向引用:\n 表示引用第n個捕獲組的內容
foreach函數觸發漏洞
遍歷$_GET數組,鍵名作為$re,鍵值作為$str,并將這兩個值傳入complex函數中
foreach($_GET as $re => $str) {echo complex($re, $str). "\n";
}
對于php中雙引號和單引號的區別
雙引號:
<?php echo "{${phpinfo()}}"; ?>
會返回phpinfo的界面?
因為雙引號里面如果包含有變量,php解釋器會將其替換為變量解釋后的結果(允許變量解析)。任何?${...}
?內的表達式會被執行,其返回值作為變量名
具體實現:
phpinfo()
?函數會被立即執行,輸出PHP配置信息(返回值為?true
,即?1
)。- PHP嘗試解析?
${phpinfo()}
?的結果(即?1
),等價于變量?$1
。 - 由于變量名?
$1
?非法(不能以數字開頭),會觸發一個?Notice級錯誤(若未關閉錯誤提示),最終輸出空字符串。
實際效果:
輸出?phpinfo()
?的完整信息。輸出空字符串(因?$1
?未定義),并可能伴隨錯誤提示。
單引號:
<?php echo '{${phpinfo()}}'; ?>
單引號內不解析變量,所以內容按字面量輸出? {${phpinfo()}}
再返回到next.php中,其基本思路就是get個鍵名鍵值都是自己輸入的東西&cmd命令,其中get內容要觸發getflag()函數進而觸發eval()函數,cmd才有用武之地
php會將傳入的非法的參數名轉成下滑線
當非法字符為首字母時,只有點號會被替換成下劃線?
?上傳的$re=$str,傳入? .*=${getflag()}? ?代入代碼中就是:
preg_replace('/(.*)/ei','strtolower("\\1")',${getflag()});
getflag()
?是一個函數調用,意味著會先執行?getflag
?函數,然后將該函數的返回值作為目標字符串傳遞給?preg_replace
?進行處理。
也就是說會這樣
好傻啊這個函數,還會先跑去執行一下代碼然后再匹配,怪不得e被棄用了?
殘留幾個問題:
1.為什么要加上${? }? ,這個東西有什么用?
${ }用于變量替換、字符串模板、表達式嵌入
這里${getflag()}外面套上一個${ }就表示執行函數,如果不套這個${ }那么就變成
strtolower("getflag()")
此時單純把這個當成字符串,而不能把他當成函數去解析
2.為什么 \S*?能表示 .? ?
因為大寫表示取反操作
這里\s 表示匹配任意空白字符,而\S表示
匹配任意非空白字符(等價于?[^\s]
)
3.捕獲組和反向引用干什么用的?
主要是為了匹配重復的字符,比如說
\b(\w+)\b\s+\1\b
\b表示單詞邊界,\w表示任意一個單詞字符,+是個量詞,表示前面的 \w 可以出現很多次,\s+表示任意一個或多個空白符號
這里就能匹配到諸如"pig pig"這樣的字符串
也可以匹配標簽,這里的 < 和 > 都代表真實的<>
<([a-z]+)>(.*?)</\1>
反向引用還在輸出!?