Не удалось прогрузить медиа
Не удалось прогрузить медиа
Не удалось прогрузить медиа
И именно будет по-факту, входной точкой, да и можно сказать конечной, при создании игры отправляется такой замечательный пост-запрос.
Не удалось прогрузить медиа
Не удалось прогрузить медиа
Меня тут заинтересовал больше всего пэйлод, а именно purchasedCaseId, понимаете чем пахнет, да? Это потенциал на иДоРчИк - уязвимость в приложениях, которая возникает, когда разработчики неправильно обращаются к данным или ресурсам в приложении. Это означает, что злоумышленник может получить доступ к конфиденциальным данным или ресурсам путем манипулирования ID объекта в URL или запросе. Окей, давайте чуть-чуть подумаем головой. Я зарегался, мне дали бесплатный кейс, нажал "играть", улетел запрос с purchasedCaseId равным 1280. А что такое этот purchasedCaseId вообще? Это же не тип кейса, это ID конкретной покупки. Когда ты покупаешь кейс, в базе создается запись: "юзер такой-то купил кейс такой-то", и этой записи присваивается порядковый номер. 1, 2, 3, 4, 5... Мой бесплатный кейс получил номер 1280. А что под номерами 1, 2, 3, 4? Правильно, чьи-то чужие покупки. Платные покупки. За которые реальные люди заплатили реальные деньги. И тут возникает вопрос, который просто обязан был возникнуть в голове у разработчика, но видимо не возник: а проверяет ли сервер, что purchasedCaseId принадлежит именно тому юзеру, который отправляет запрос?
Меняю purchasedCaseId с 1280 на 1200, отправляю. Получаю
Code:
{"success":true,"message":"Игра успешно создана","gameId":2353}
Не удалось прогрузить медиа
Платный кейс. За ноль рублей. Спасибо, чужой человек, за оплату.
Не удалось прогрузить медиа
Если purchasedCaseId это инкрементный глобальный айдишник, то все покупки всех юзеров лежат в одном ряду. Юзер купил кейс - purchasedCaseId=100. Другой юзер купил - purchasedCaseId=101. И так далее. Значит, перебрав все айдишники, я могу создать игры по ВСЕМ покупкам ВСЕХ юзеров. Накидал скрипт.
Code:
for pid in range(1, 1200):
r = s.post(
'https://api.kodgoroda.games/client/games/create',
json={'purchasedCaseId': pid},
timeout=10
)
if r.status_code == 200:
print(f"[+] VALID: purchasedCaseId={pid}")
elif "максимальное количество" in r.text:
print(f"[!] EXISTS: purchasedCaseId={pid} (limit)")
И побежало. Результат был такой: на большинство айдишников API отвечает ошибкой (покупки не существует), но на конкретные номера отвечает либо 200 (создана игра), либо 400 с текстом "Вы уже создали максимальное количество игр для этого кейса". Вторая ошибка самая показательная, она означает, что я УЖЕ использовал чужую покупку и исчерпал лимит попыток. Не мою покупку. Чужую. А сервер даже не моргнул.
Не удалось прогрузить медиа
Классический, хрестоматийный IDOR. Сервер должен был проверять одну простую вещь: "этот purchasedCaseId принадлежит юзеру из токена?". Один запрос к базе: "SELECT * FROM purchased_cases WHERE id = ? AND client_id = ?". Одна строчка кода. Но её нет. А самое ироничное знаете что? У них же есть лимит на попытки. То есть кто-то сел, написал логику "если юзер уже создал 3 игры для этого кейса, не давай создавать ещё". Потратил время, протестировал, задеплоил. Но при этом не подумал проверить, а его ли это вообще кейс.Ну, а дальше я сделал то, что должен был сделать, написал разработчикам.
Не удалось прогрузить медиа
Хотя, конечно, ирония в том, что мне предложили промокод на бесплатную игру, которую я и так только что получил бесплатно через их же баг. Но это мелочи. Вся эта история, это один запрос, одна циферка, и полный доступ ко всему платному контенту. Без каких-то сложных цепочек эксплойтов. Ну, а мне пора идти играть в детектива. Бесплатно. Не болейте!
👍19
❤️3
🤔4
LIMB