parse_url小結

本篇文章對parse_url進行一個小結

0x01:parse_url

$url = "/baidu.com:80";
$url1 = "/baidu.com:80a";
$url2 = "//pupiles.com/about:1234";
$url3 = "//baidu.com:80a";

var_dump(parse_url($url));
var_dump(parse_url($url1));
var_dump(parse_url($url2));
var_dump(parse_url($url3));

執行以上代碼:,將得到下面的結果

/home/tr1ple/exp.php:7:
bool(false)
/home/tr1ple/exp.php:8:
array(1) {
  'path' =>
  string(14) "/baidu.com:80a"
}
/home/tr1ple/exp.php:9:
array(3) {
  'host' =>
  string(11) "pupiles.com"
  'port' =>
  int(1234)
  'path' =>
  string(11) "/about:1234"
}
/home/tr1ple/exp.php:10:
array(2) {
  'host' =>
  string(9) "baidu.com"
  'port' =>
  int(80)
}
$url4 = "//upload?/test/";
$url5 = "//upload?/1=1&id=1";
$url6 = "///upload?id=1";

var_dump(parse_url($url4));
var_dump(parse_url($url5));
var_dump(parse_url($url6));

將輸出:

/home/tr1ple/exp.php:6:
array(2) {
  'host' =>
  string(6) "upload"
  'query' =>
  string(6) "/test/"
}
/home/tr1ple/exp.php:7:
array(2) {
  'host' =>
  string(6) "upload"
  'query' =>
  string(9) "/1=1&id=1"
}
/home/tr1ple/exp.php:8:
bool(false)

1.//upload?如果是//,則被解析成host, 後面的內容如果有/,被解析出path,而不是query了

2.如果path部分爲///,則解析錯誤

感想:在實際上bypass的時候可以根據自己的目的多測試,去測試程序解析的反應

parse_url一般會用來解析$SERVER變量,其中幾個變量如下所示:

echo $_SERVER['REQUEST_URI']."<br/>";
echo $_SERVER['QUERY_STRING']."<br/>";
echo $_SERVER['HTTP_HOST']."<br/>";

#訪問http://localhost:3000/php/audit/5/parse1.php?url=baidu.com#test
>>>
/php/audit/5/parse1.php?url=/baidu.com
url=/baidu.com
localhost:3000
REQUEST_URI 是path+query部分(不包含fragment)
QUERY_STRING: 主要是key=value部分
HTTP_HOST 是 netloc+port 部分。

tricks:

1.2017swpu的一道web題

<?php
error_reporting(0);
$_POST=Add_S($_POST);
$_GET=Add_S($_GET);
$_COOKIE=Add_S($_COOKIE);
$_REQUEST=Add_S($_REQUEST);
function Add_S($array){
    foreach($array as $key=>$value){
        if(!is_array($value)){          
            $check= preg_match('/regexp|like|and|\"|%|insert|update|delete|union|into|load_file|outfile|\/\*/i', $value);
            if($check)
                {
                exit("Stop hacking by using SQL injection!");
            }
        }else{
            $array[$key]=Add_S($array[$key]); 
        }
    }
return $array;
}
function check_url()
{
    $url=parse_url($_SERVER['REQUEST_URI']);
    parse_str($url['query'],$query);
    $key_word=array("select","from","for","like");
    foreach($query as $key)
    {
        foreach($key_word as $value)
        {
            if(preg_match("/".$value."/",strtolower($key)))
            {
                die("Stop hacking by using SQL injection!");
            }
        }
    }
}
?>

我們關注這裏的check_url()函數,首先使用parse_url獲取 $_SERVER['REQUEST_URI'], 而正常的注入payload比如:

http://localhost///web/trick1/parse.php?sql=select

將會被檢測到注入,然而parse_url函數在解析url的時候存在bug,通過///x.php?key=value的方式將返回false,此時將不再進入foreach循環進行判斷,

所以可以進行注入,今年的全國大學生信息安全競賽初賽就出過這一個trick,先繞過parse,然後再反序列化==,做題時當時卡到這了,遇到卡住的點可能就是需要去繞過的點!

2.題目來自2016asisctf的一道web題

<?php 
function waf(){
    $INFO = parse_url($_SERVER['REQUEST_URI']);
    var_dump($INFO);
    var_dump($_GET);
    parse_str($INFO['query'], $query);
    $filter = ["union", "select", "information_schema", "from"];
    foreach($query as $q){
        foreach($filter as $f){
            if (preg_match("/".$f."/i", $q)){ 
                die("attack detected!");
            }
        }
    }

    $sql = "select * from ctf where id='".$_GET['id']."'";
    var_dump($sql);
}
waf();

關注點在$_SERVER['REQUEST_URI'],在parse_解析後,要檢測查詢的參數裏面是否包含sql查詢關鍵字,那麼我們是不是可以構造惡意url使parse按照非預期的進行解析,那麼的確bypass的過程就是如此神奇。。

payload1:

http://localhost//exp.php?/1=1&id=1' union select 1,2,3#

此時parse_url解析後的REQUEST_URI爲:

php7.2測試:

php5.3測試:

解析的不同還和php的版本有關係,那麼在5.3的版本中,此時query將爲空,那麼將繞過過濾,並且此時$_GET方式傳遞過來的id參數的值正是我們想要的payload,7.2版本是先識別查詢符號?,然後把後面的當作查詢字符串,而5.3版本是先把url分段,//到/爲host,/後爲path。參數纔是前後端交互的橋樑,用戶的不可信數據也正是通過參數進行傳遞,tricks也正是用來保護參數不被過濾嗎,7.2將參數首先提取出來,更注重了參數路徑的安全性,越不可信的數據先處理。

payload2:

http://localhost///exp.php?id=1' union select 1,2,3#

php7.2:

 

 從上面兩幅圖中可以看出,這個payload對5.3和7.2都是適用的,返回false來bypass,id參數中包含的payload依然存在!

3.網鼎杯第三場comein

 

<?php
ini_set("display_errors",0);
$uri = $_SERVER['REQUEST_URI']; // 請求的uri
var_dump($uri);

if(stripos($uri,".")){   // uri中要麼不出現“.” 要麼以“.”開頭
    die("Unkonw URI.");
}

if(!parse_url($uri,PHP_URL_HOST)){  //嘗試解析uri
    $uri = "http://".$_SERVER['REMOTE_ADDR'].$_SERVER['REQUEST_URI'];
    var_dump($uri);
}

$host = parse_url($uri,PHP_URL_HOST);  //再次解析uri
var_dump($host);
if($host === "c7f.zhuque.com"){
   echo "flag  sasa";
}

 

首先要繞過stripos,開頭爲.即可繞過,第二處使用parse_url來處理uri,即path+query部分,正常的應該是如下圖所示,然後第二個if條件將調用parse_url函數對$uri變量進行處理,提取出其中的host信息,但是其中

明顯不再包含host頭了,所以會拼接上http://,然後再用parse_url進行host的頭的提取,其中$_SERVER['REQUEST_URI']是可控的,並且第一個字符應該爲點.(在BURP中操作),然後第三個if條件中提取host頭部時,

不能用第二個if條件的拼接的host頭,因爲後面的部分是我們可以控制的,所以注入一個@符號那麼parse_url再次解析的時候將把127.0.0.1.解析成用戶名,@符號後面的將解析成要訪問的網址,然後@後面改爲[email protected]/,但是此時會bad url,因爲apache在解析url時出現了問題,因爲我們並沒有[email protected]/這個目錄,所以還需要調整paylaod同時滿足後端PHP判斷和apache的解析,要滿足apache的解析,只需要跳到一個存在的文件即可,比如index.php,即此時繼續拼接payload,爲[email protected]/..//index.php,/後面的將被解析爲path,其中..//,先跳到和[email protected]同級目錄,然後此目錄下的/index.php,此時才能滿足apache對文件路徑的解析,其中../index.php和.././index.php都不行,因爲apache拼接出來都找不到index.php這個文件

 

該漏洞出現的原因是,parse_url函數和apache對地址的解析方式不同。

PHP認爲127.0.0.1.是個user,c7f.zhuque.com是真實host

apache認爲127.0.0.1是host,[email protected]/是一個路徑,後邊..//index.php退回根目錄,再訪問index.php

參考:

https://skysec.top/2017/12/15/parse-url%E5%87%BD%E6%95%B0%E5%B0%8F%E8%AE%B0/

https://blog.csdn.net/publicStr/article/details/83004265 

https://github.com/jiangsir404/Audit-Learning/blob/master/filter_var%E5%87%BD%E6%95%B0%E7%BC%BA%E9%99%B7.md

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章