Loading console...
Preparing execution environment... OK
Loading Bash interpreter...
user@HackerWars:~/h4rdf0rk $ ls -a
.
..
agitation.txt
H4rdF0rk.log
H4rdF0rk_media.html
SkyBridge.log
VIP_Zone.log
user@HackerWars:~/h4rdf0rk $ cat SkyBridge.log
█████████ > В этом логе я расскажу о применении 0day уязвимости 🌁SkyBridge. Содержание:
[README.txt] Общая информация
[01_01_2020-14_00.conf] get_data
[02_01_2020-14_00.conf] login_HTTP
[03_01_2020-14_00.conf] server_info
[04_01_2020-14_00.conf] CRC_check
[05_01_2020-14_00.conf] send_message
[06_01_2020-14_00.conf] keygen_DH
[07_01_2020-14_00.conf] A_token
[H4rdF0rk_S3rv3r.conf] Атака на сервер H4rdF0rk
█████████ > Все API 💠Aegis расположены по адресу https://aegis.hackerwars.ru/. При переходе на него получаем ответ:
{
"encoding": "utf_8",
"error": "GET params 'api' and 'key' required for processing.",
"method": "GET",
"response": 400
}
Нам нужны GET параметры 'api' и 'key' для подключения. Где их взять - непонятно.
█████████ > Я создал эксплойт 0day уязвимости 🌁SkyBridge.
🌁SkyBridge > Script activated.
______ ___ _ __
/ __/ /____ __/ _ )____(_)__/ /__ ____
_\ \/ '_/ // / _ / __/ / _ / _ `/ -_)
/___/_/\_\\_, /____/_/ /_/\_,_/\_, /\__/
/___/ /___/
🌁SkyBridge > Target: https://aegis.hackerwars.ru/... OK
🌁SkyBridge > Credentials found: 01_01_2020-14_00.conf
█████████ > В первый день мы получили данные для входа:
api = get_data
key = 746E533C
█████████ > Для подключения к API переходим по ссылке: https://aegis.hackerwars.ru/?api=get_data&key=746E533C. А вот там получаем уже что-то страшное. Первым получил информацию AL|⬛🚧❄010 (25).
AL|⬛🚧❄010 (25) > Заметив сбой в кодировке через браузер, я решил воспользоваться питоном в надежде, что он подхватит нужную кодировку. Так и случилось.
█████████ > Действительно, в заголовках ответа присутствует header:
Content-Type: application/json; charset=cp037
█████████ > Похоже, браузеры не умеют работать с этой кодировкой. Если взять данные от сервера (не в том виде, в котором их отображает браузер) и перевести из указанной кодировки в привычную (например, utf-8), получим:
{"api": "get_data", "encoding": "cp037", "information": {"H4rdF0rk_01.txt": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 Aegis \u043e\u0431\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0447\u0435\u043d\u044c \u0431\u044b\u0441\u0442\u0440\u043e, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0432\u0441\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0441 \u043c\u043e\u0435\u0433\u043e \u043a\u043e\u043c\u043f\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043e\u043d\u0430 \u0441\u0442\u0430\u043d\u0435\u0442 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u043c H4rdF0rk.", "Public code": "3guv7vuln9", "Secret code": "uu88udq2dn"}, "key": "746E533C", "method": "GET", "response": 203}
На языке программирования Python cp037 является одной из встроенных кодировок, поэтому при запросе через него текст сразу будет иметь такой вид.
После расшифровки:
{
"api": "get_data",
"encoding": "cp037",
"information": {
"H4rdF0rk_01.txt": "Система Aegis обучается очень быстро, используя все данные с моего компа. Возможно, она станет основным инструментом H4rdF0rk.",
"Public code": "3guv7vuln9",
"Secret code": "uu88udq2dn"
},
"key": "746E533C",
"method": "GET",
"response": 203
}
█████████ > До введения unicode (и его способов отображения) как основного стандарта кодировки информации, многие локальные кодировки пересекались, что создавало огромное количество проблем с отображением информации.
Но это не первый такой случай. Задолго до этого, не существовало единой кодировки даже для латинского алфавита. Сейчас эту функцию выполняет ASCII, оставаясь в начале всех современных кодировок, чтобы избежать проблем с совместимостью.
🌁SkyBridge > Credentials found: 02_01_2020-14_00.conf
█████████ > Данные для входа на этот раз такие:
api = login_HTTP
key = 5F36C7F3
login = aegis_server
password = YWVnaXNfc2VydmVy
█████████ > Для подключения к API используем ссылку: https://aegis.hackerwars.ru/?api=login_HTTP&key=5F36C7F3. API запрашивает авторизацию:
{
"api": "login_HTTP",
"encoding": "utf_8",
"error": "HTTP basic access authentication required.",
"key": "5F36C7F3",
"method": "GET",
"response": 401
}
█████████ > Первым подключился пользователь RJ|🔳🇺🇸🔹0xC0FF33__KP3B3T04KA (32).
RJ|🔳🇺🇸🔹0xC0FF33__KP3B3T04KA (32) > Введя api и key на сайт я столкнулся с базовой авторизацией, то есть нужно было передать логин и пароль. Я не стал изобретать велосипед, и попросту использовал онлайн сервисы, получив сразу же читаемый результат.
█████████ > Да, изобретать велосипед не нужно. Но я на всякий случай расскажу, как работает этот тип авторизации.
Сначала нужно взять логин и пароль и записать их через двоеточие:
aegis_server:YWVnaXNfc2VydmVy
После этого, кодируем получившуюся строку в base64:
YWVnaXNfc2VydmVyOllXVm5hWE5mYzJWeWRtVnk=
Дописываем в начало строки "Authorization: Basic ":
Authorization: Basic YWVnaXNfc2VydmVyOllXVm5hWE5mYzJWeWRtVnk=
█████████ > Мы получили header авторизации, передающий логин и пароль. Если отправить запрос к API с этим хэдером, мы получим информацию:
{"api": "login_HTTP", "auth": {"method": "Basic", "password": "YWVnaXNfc2VydmVy", "username": "aegis_server"}, "encoding": "utf_8", "information": {"H4rdF0rk_02.txt": "Aegis \u0443\u0436\u0435 \u0443\u043c\u0435\u0435\u0442 \u0441\u043e\u0432\u0435\u0440\u0448\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u0430\u0442\u0430\u043a\u0438, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b H4rdF0rk. \u041d\u0443\u0436\u043d\u043e \u0437\u0430\u0449\u0438\u0442\u0438\u0442\u044c \u0441\u0432\u043e\u044e \u0441\u0438\u0441\u0442\u0435\u043c\u0443, \u0447\u0442\u043e\u0431\u044b \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u043d\u0430\u0434 \u0442\u0430\u043a\u043e\u0439 \u043c\u043e\u0449\u043d\u043e\u0439 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044c\u044e \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0445\u0430\u043a\u0435\u0440\u044b.", "Public code": "4nxg4encryption6", "Secret code": "mdbwi99csv"}, "key": "5F36C7F3", "method": "GET", "response": 200}
После расшифровки:
{
"api": "login_HTTP",
"auth": {
"method": "Basic",
"password": "YWVnaXNfc2VydmVy",
"username": "aegis_server"
},
"encoding": "utf_8",
"information": {
"H4rdF0rk_02.txt": "Aegis уже умеет совершать простые атаки, используя инструменты H4rdF0rk. Нужно защитить свою систему, чтобы контроль над такой мощной нейросетью не получили другие хакеры.",
"Public code": "4nxg4encryption6",
"Secret code": "mdbwi99csv"
},
"key": "5F36C7F3",
"method": "GET",
"response": 200
}
█████████ > Протокол HTTP предлагает несколько типов авторизации, и Basic - самый ненадёжный из них. Кодировка - не шифрование, а значит логин и пароль передаются в открытом виде. Тем не менее, этот способ может использоваться совместно с протоколом HTTPS для осуществления простейшей авторизации для сервисов без критической информации.
Кстати, API Aegis не передает на сторону клиента header, указывающий, какие данные нужны для авторизации:
WWW-Authenticate: Basic realm="Some text for user"
В таком случае, браузер сам отобразит окно для ввода логина и пароля.
Я поднял тестовую версию API, которая передает нужный хэдер: https://aegis.hackerwars.ru/login_HTTP
🌁SkyBridge > Credentials found: 03_01_2020-14_00.conf
█████████ > Новые данные для входа:
api = server_info
key = 3C765F87
█████████ > Подключаемся к API по ссылке: https://aegis.hackerwars.ru/?api=server_info&key=3C765F87. В ответе сообщается, что по соображениям безопасности API отключили:
{
"api": "server_info",
"encoding": "utf_8",
"error": "API disabled for safety reasons.",
"key": "3C765F87",
"method": "GET",
"response": 410,
"version": "2.0"
}
█████████ > Пользователь AL|⬛🚧❄010 (25) смог первым обнаружить способ добыть информацию.
AL|⬛🚧❄010 (25) > Первым делом я увидел перенаправление на другую страницу (https://aegis.hackerwars.ru/server_info?key=3C765F87).
Чтобы увидеть оригинальную страницу (https://aegis.hackerwars.ru/?api=server_info&key=3C765F87) я решил воспользоваться питоном и посмотреть историю. Там то я и нашел ответ.
█████████ > Да, когда мы запрашиваем изначальный ресурс, сервер отвечает с кодом 302 и хэдером:
Location: https://aegis.hackerwars.ru/server_info?key=3C765F87
При таком ответе, браузеры игнорируют тело ответа и сразу отправляют запрос по новому адресу.
Нужно найти что-то, что будет загружать тела ответов для всех запросов (как python) или игнорирует редиректы (как программа командной строки curl).
Во втором варианте для получения ответа воспользуемся командой:
curl -G -d api=server_info -d key=3C765F87 https://aegis.hackerwars.ru/
Это выдаст нам тело запроса:
{"api": "server_info", "encoding": "utf_8", "error": "API moved to another address, this info is outdated.", "information": {"H4rdF0rk_03.txt": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 Aegis \u0443\u0436\u0435 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u043a\u0430\u0447\u0430\u043b\u0430 \u0441\u0432\u043e\u0438 \u043d\u0430\u0432\u044b\u043a\u0438 \u0438 \u0443\u0436\u0435 \u043d\u0430\u0443\u0447\u0438\u043b\u0430\u0441\u044c \u0437\u0430\u0449\u0438\u0449\u0430\u0442\u044c\u0441\u044f. \u0421 \u0442\u0430\u043a\u043e\u0439 \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u044c\u044e H4rdF0rk \u0441\u0442\u0430\u043d\u0435\u0442 \u0441\u0430\u043c\u043e\u0439 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e\u0439 \u0445\u0430\u043a\u0435\u0440\u0441\u043a\u043e\u0439 \u0433\u0440\u0443\u043f\u043f\u0438\u0440\u043e\u0432\u043a\u043e\u0439 \u0432 \u0438\u0441\u0442\u043e\u0440\u0438\u0438!", "Public code": "6tqf2coolhacker3", "Secret code": "cxti2nm9kx"}, "key": "3C765F87", "method": "GET", "response": 302, "version": "1.0"}
После расшифровки:
{
"api": "server_info",
"encoding": "utf_8",
"error": "API moved to another address, this info is outdated.",
"information": {
"H4rdF0rk_03.txt": "Система Aegis уже достаточно прокачала свои навыки и уже научилась защищаться. С такой нейросетью H4rdF0rk станет самой известной хакерской группировкой в истории!",
"Public code": "6tqf2coolhacker3",
"Secret code": "cxti2nm9kx"
},
"key": "3C765F87",
"method": "GET",
"response": 302,
"version": "1.0"
}
█████████ > Как показывает этот случай, наличие редиректа ещё не означает недоступность изначального ресурса.
🌁SkyBridge > Credentials found: 04_01_2020-14_00.conf
█████████ > Полученные данные:
api = CRC_check
key = 2ECC1BCD
█████████ > Ссылка для подключения: https://aegis.hackerwars.ru/?api=CRC_check&key=2ECC1BCD. На этот раз нас просят передать данные методом POST:
{
"api": "CRC_check",
"encoding": "utf_8",
"error": "Provide POST data.",
"key": "2ECC1BCD",
"method": "GET",
"response": 422
}
█████████ > Если передать данные - ответ меняется (в примере я передал строку somePOSTdata):
{
"CRC-32": {
"actual": "0xf59b75c5",
"expected": "0x189c5897",
"valid": false
},
"api": "CRC_check",
"data": "somePOSTdata",
"encoding": "utf_8",
"error": "Invalid password in POST data.",
"key": "2ECC1BCD",
"method": "POST",
"response": 403
}
█████████ > Подходящий пароль первым нашел AL|⬛🚧❄010 (25).
AL|⬛🚧❄010 (25) > Зная, что CRC-32 не самая надежная хеш функция + то, что нам дали сам хеш, я запустил hashcat и за 9 секунд сбрутил нужную строку.
█████████ > Действительно, брутить хэши удобнее всего через специальное ПО, вроде hashcat.
Команда для запуска под Windows, которую использовал AL|⬛🚧❄010 (25):
./hashcat64.exe -m 11500 -a 3 D:/file.txt -i
Подробнее об аргументах:
-m - тип хэша, 11500 - номер CRC32 в списке hashcat
-a - режим атаки, третий режим - брутфорс
D:/file.txt - файл с хэшами и солью. Соли нет, поэтому вместо нее нули
-i - режим приращения маски, нужен для брутфорса
Содержимое файла D:/file.txt:
0x189c5897:00000000
189c5897:00000000
412899479:00000000
Такая команда выдаёт подходящий пароль:
Dconide
Отправим его и получим:
{"CRC-32": {"actual": "0x189c5897", "expected": "0x189c5897", "valid": true}, "api": "CRC_check", "data": "Dconide", "encoding": "utf_8", "information": {"H4rdF0rk_04.txt": "\u041a\u0430\u0436\u0435\u0442\u0441\u044f, \u0441\u0438\u0441\u0442\u0435\u043c\u0430 Aegis \u0440\u0435\u0448\u0438\u043b\u0430 \u0434\u0435\u0439\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e, \u0438\u0433\u043d\u043e\u0440\u0438\u0440\u0443\u044f \u0437\u0430\u0434\u0430\u043d\u0438\u044f \u043e\u0442 H4rdF0rk. \u041d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0435\u0451 \u0432\u044b\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438 \u043f\u043e\u043d\u044f\u0442\u044c, \u0447\u0442\u043e \u043d\u0435 \u0442\u0430\u043a.", "Public code": "6prt8wifi3", "Secret code": "55fi6r3tzv"}, "key": "2ECC1BCD", "method": "POST", "response": 200}
После расшифровки:
{
"CRC-32": {
"actual": "0x189c5897",
"expected": "0x189c5897",
"valid": true
},
"api": "CRC_check",
"data": "Dconide",
"encoding": "utf_8",
"information": {
"H4rdF0rk_04.txt": "Кажется, система Aegis решила действовать самостоятельно, игнорируя задания от H4rdF0rk. Нужно будет её выключить и понять, что не так.",
"Public code": "6prt8wifi3",
"Secret code": "55fi6r3tzv"
},
"key": "2ECC1BCD",
"method": "POST",
"response": 200
}
█████████ > После полного захвата серверов API 💠Aegis выяснилось, что правильный пароль был намного сложнее:
Very_long_password_created_in_1998_that_is_impossible_to_guess
Узнать его нам удалось только потому, что он же использовался для сервера H4rdF0rk и хранился в чистом виде на сервере API A_token.
Применение хэширования при хранении паролей стало неотъемлимой частью современной информационной безопастности, но важно понимать, что хэш-функция должна иметь очень маленькую вероятность коллизии двух паролей.
Контрольная сумма CRC-32 из-за своего размера в 4 байта изначально не подходит для таких задач. Её обычно применяют для проверки целостности информации при передаче.
█████████ > Кстати, согласно информации от крупнейших организаций, занимающихся стандартами безопастности, рекомендуется использовать следующие правила при защите данных паролем:
- Желательно использовать (и требовать от пользователей) пароли от 8 до 64 символов
- Спецсимволы в пароле (включая пробел) должны быть разрешены, но не обязательны к использованию
- Требовать сложные комбинации заглавных и строчных символов нежелательно
- Нежелательно требовать смену пароля, если нет признаков утечки/взлома
- Вход по контрольным вопросам - штука ненадёжная, его реализовывать не стоит
- Подсказки к паролям, заданные пользователями, и предложения ввести конкретную информацию недопустимы
- Наиболее популярные пароли нужно запретить, это небезопасно
- Пароли не должны находиться в словарях и списках слитых паролей
- Ограничение количества попыток ввода пароля - очень хорошая идея, не стоит о ней забывать
- Двухфакторка и менеджеры паролей должны поддерживаться и продвигаться
- Используйте защищённое соединение для передачи паролей, это важно
- Не храните пароли в открытом виде! Их хэшировать, да ещё и с солью
- Если пароль пользователя не подошел - сообщите ему причину отказа и предложите ввести новый
- Шкала надёжности пароля и подтверждение при регистрации - полезно и нужно
- Не используйте одинаковые пароли на разных ресурсах, расскажите об этом правиле пользователям
- Длинная фраза - хорошая основа для сложного пароля. Важно, чтобы её не могли отгадать даже ваши знакомые
- Нигде и никогда не выкладывайте пароль, это опасно. Предупредите об этом пользователей
█████████ > Подробнее можно почитать, например, в разделе 5.1.1.2 документа NIST SP 800-63B Национального института стандартов и технологий США.
🌁SkyBridge > Credentials found: 05_01_2020-14_00.conf
█████████ > Полученные данные:
api = send_message
key = 1A17D2C9
█████████ > Как всегда, ссылка: https://aegis.hackerwars.ru/?api=send_message&key=1A17D2C9. И снова POST данные:
{
"api": "send_message",
"encoding": "utf_8",
"error": "Provide POST data.",
"key": "1A17D2C9",
"method": "GET",
"response": 422
}
█████████ > Вот, что будет после передачи данных (в примере - somePOSTdata):
{
"api": "send_message",
"encoding": "utf_8",
"key": "1A17D2C9",
"message": {
"from": "user",
"text": "somePOSTdata",
"to": "aegis"
},
"method": "POST",
"response": 202
}
█████████ > Передача сообщения идёт от 'user' к 'aegis', а нам нужно от 'aegis' к 'system'.
█████████ > Первый пользователь, обнаруживший уязвимость в этом API - AL|⬛🚧❄010 (25).
AL|⬛🚧❄010 (25) > Когда я увидел ответ в виде json, где отображался мой message, я решил проинжектиться в него. Инжект произошел успешно и я подменил для кого сообщение и от кого.
█████████ > Посмотрим, что будет, если в сообщении будут кавычки (отправим some"POST"data):
{
"JSON": "{\"from\": \"user\", \"to\": \"aegis\", \"text\": \"some\"POST\"data\"}",
"api": "send_message",
"encoding": "utf_8",
"error": "JSON parsing failed.",
"key": "1A17D2C9",
"method": "POST",
"response": 422
}
Видим ошибку парсинга JSON и понимаем, что где-то внутри программы сообщение создаётся из JSON строки:
{"from": "user", "to": "aegis", "text": " - начало строки
Сюда вставляется наше сообщение
"} - конец строки
█████████ > Использовать такой метод создания JSON крайне нежелательно, так как наше сообщение будет рассматриваться наравне с остальной строкой.
На практике это означает, что система будет принимать что угодно, если в итоге получится синтаксически верный JSON.
Это приводит к возможности инжекта. Например, отправим такую строчку:
someTEXT", "key": "value
В таком случае, JSON строка получится такой:
{"from": "user", "to": "aegis", "text": "someTEXT", "key": "value"}
Получаем ответ:
{
"api": "send_message",
"encoding": "utf_8",
"key": "1A17D2C9",
"message": {
"from": "user",
"text": "someTEXT",
"to": "aegis"
},
"method": "POST",
"response": 202
}
Как мы видим, JSON распознался. Ключа "key" там нет. Похоже, в ответ попадают только важные для сообщения поля.
█████████ > Кроме того, можно заметить, что наше сообщение было последним ключом в изначальной строке.
Одной из особенностей JSON является уязвимость его парсеров к атакам вида parameter pollution: повторное объявление ключа заменяет предыдущее значение.
Проверим это, отправив строку:
firstTEXT", "text": "secondTEXT
Итоговая строка:
{"from": "user", "to": "aegis", "text": "firstTEXT", "text": "secondTEXT"}
Вторым идёт secondTEXT, так что именно он должен оказаться в ключе 'text'. Проверим:
{
"api": "send_message",
"encoding": "utf_8",
"key": "1A17D2C9",
"message": {
"from": "user",
"text": "secondTEXT",
"to": "aegis"
},
"method": "POST",
"response": 202
}
█████████ > Всё как мы и предполагали. Время создать наш пейлоад.
Payload, или полезная нагрузка - информация, которую мы передаём.
Хакеры обычно называют пейлоадом тот вариант информации, который проверяет или эксплуатрует уязвимость.
Так что да, мы уже отправляли два пейлоада для выявления и проверки инжекта и один - для parameter pollution.
А пейлоад для получения важной информации может выглядеть, например, так:
someTEXT", "from": "aegis", "to": "system
JSON строка сообщения получится такой:
{"from": "user", "to": "aegis", "text": "someTEXT", "from": "aegis", "to": "system"}
Это позволит перезаписать и отправителя, и получателя:
{"api": "send_message", "encoding": "utf_8", "information": {"H4rdF0rk_05.txt": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 Aegis \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u043b\u0430 \u0441\u0432\u043e\u0438 \u0444\u0430\u0439\u043b\u044b \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0437\u0430\u0445\u0432\u0430\u0447\u0435\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0430\u043a\u043e\u0432, \u044f \u043f\u043e\u0442\u0435\u0440\u044f\u043b \u043d\u0430\u0434 \u043d\u0435\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c. \u041d\u0443\u0436\u043d\u043e \u0443\u0441\u0438\u043b\u0438\u0442\u044c \u0437\u0430\u0449\u0438\u0442\u0443 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0430\u043a\u0430 H4rdF0rk, \u043f\u043e\u043a\u0430 \u0435\u0433\u043e \u043d\u0435 \u0432\u0437\u043b\u043e\u043c\u0430\u043b\u0438.", "Public code": "7aus2response6", "Secret code": "tvpmkz97x6"}, "key": "1A17D2C9", "message": {"from": "aegis", "text": "someTEXT", "to": "system"}, "method": "POST", "response": 200}
После расшифровки:
{
"api": "send_message",
"encoding": "utf_8",
"information": {
"H4rdF0rk_05.txt": "Система Aegis перенесла свои файлы на несколько захваченных серваков, я потерял над ней контроль. Нужно усилить защиту основного сервака H4rdF0rk, пока его не взломали.",
"Public code": "7aus2response6",
"Secret code": "tvpmkz97x6"
},
"key": "1A17D2C9",
"message": {
"from": "aegis",
"text": "someTEXT",
"to": "system"
},
"method": "POST",
"response": 200
}
█████████ > Основной причиной появления уязвимости является некорректный способ создания JSON.
Зачем нужно было изобретать велосипед, создавая JSON склеиванием строк? Тем более, что итоговый JSON формируется правильно.
Скорее всего, на момент написания API, такой способ работы с JSON уже использовался, а создатель H4rdF0rk решил не трогать то, что работает, написав для этого красивую оболочку.
█████████ > Инжекты обычно появляются в том случае, если данные пользователя добавляются в какую-то текстовую информацию без проверки.
Управляющие символы в исходной части информации становится невозможно отличить от тех, что были в тексте пользователя. При попытке распарсить итоговую строку, можно получить результат, значительно отличающийся от ожидаемого.
Для защиты от инжектов применяются функции эскейпинга/экранирования (они меняют управляющие символы так, чтобы они однозначно воспринимались как часть текста пользователя).
В готовых библиотеках для работы с JSON они уже есть по умолчанию.
А вот пример самописного алгоритма эскейпинга для JSON на языке PHP 5:
<?php
/* Для текстов в JSON важен эскейпинг кавычки (") и бэкслеша (\).
Кавычка опасна тем, что позволяет выходить за пределы текстового поля, что создаёт инжект.
Её стоит заменять на символ кавычки (\").
Бэкслеш служит для экранирования. Он может сломать JSON, экранируя кавычку после текста.
Кроме того, он может сломать экранирование кавычек, превратив экранирующий бэкслеш в символ бэкслеша (\\).
Например, '\"' после экранирования кавычки превратится в '\\"', что будет распознано как символ бэкслеша и кавычка.
Его нужно заменять на символ бэкслеша (\\), причем делать это нужно до экранирования кавычек. */
function JSON_escape($string_to_escape)
{
$escaped_string = str_replace("\\", "\\\\", $string_to_escape);
// Два бэкслеша в PHP обозначают символ бэкслеша в исходной строке.
$escaped_string = str_replace("\"", "\\\"", $escaped_string);
// Аналогично с кавычкой. PHP тоже не любит инжекты в исходниках.
return $escaped_string;
}
$safe_JSON = "{\"from\": \"user\", \"to\": \"root\", \"text\": \"".JSON_escape($HTTP_RAW_POST_DATA)."\"}";
// Для PHP 7 нужно заменить $HTTP_RAW_POST_DATA на file_get_contents("php://input")
echo $safe_JSON;
// Выводим результат внутри JSON, теперь мы их не боимся.
?>
█████████ > Пример этого кода я нашёл по адресу https://root.hackerwars.ru/JSON_escape.php
Там применён ещё и эскейпинг HTML (Не только же в JSON инжекты бывают) и вывод распознанной информации.
🌁SkyBridge > Credentials found: 06_01_2020-14_00.conf
█████████ > А вот и новый ключик к API:
api = keygen_DH
key = BED45C97
█████████ > На этот раз, по ссылке (https://aegis.hackerwars.ru/?api=keygen_DH&key=BED45C97) нас ожидает что-то необычное:
{
"DH": {
"g": 86686136056545304660994252432156625773889066120498168631765739416745482493422987585296192063723736599263002976567595657636575621223824796036376969022114262822476015189850335269173530996691468705957636088921107674849266001665959963924041156092676589677858275687705781389349325292821902667932773664074397025014885859531985374331363919370302063677714826493469850237895535599734162522545523612723105523954370722461520363868871831877731975713014099237185479936897127467760814257390477172614268597532085496975182534809762289121382430941346377303547604540401781187289401735594268644458343791158899326073313144167027058457823531,
"p": 86686136056545304660994252432156625773889066120498168631765739416745482493422987585296192063723736599263002976567595657636575621223824796036376969022114262822476015189850335269173530996691468705957636088921107674849266001665959963924041156092676589677858275687705781389349325292821902667932773664074397025057056395971127798212868512036542986007381764450802738622888457263247853466350831762343657190118694660520926555651420001826960963517554742865635266192601411342660123373292705629287891375739175779053722758700760074451754576829084967851492233558979316397070894234154863263130900750980110998751294240297623797935181147
},
"api": "keygen_DH",
"encoding": "utf_8",
"error": "Provide your public key (A) as GET param 'A'.",
"key": "BED45C97",
"method": "GET",
"response": 400
}
█████████ > Кажется, от нас чего-то хотят... Но чего именно?
█████████ > С этим раньше всех разобрался AQ|⬛💠❄DeeOne-PeefCo (30).
AQ|⬛💠❄DeeOne-PeefCo (30) > Увидел, что стоит защита Диффи - Хеллмана, путём научного тыка и волшебного гугла получил данные, проксорил их и получил decrypted.
Получили decrypted = получили ответ.
█████████ > Ну, сначала расскажу, что такое алгоритм Диффи-Хеллмана...
Это очень крутая штука, юзающая свойство степеней и сложность обратных действий по модулю.
Этот алгоритм позволяет двум или более сторонам независимо создать один и тот же ключ для шифрования, да так, что его невозможно получить, даже перехватив все, чем они обменивались.
█████████ > Изначально, выбираются два числа: генератор (g) и модуль (p), которые мы видели в ответе от API.
Для повышения безопастности, (p-1)/2 тоже должно быть простым, но в нашем случае это не так.
После этого, все участники (будем рассматривать на примере двух) придумывают числа от 2 до p - это их закрытые ключи (назовем их a и b).
Затем, создаются открытые ключи, методом возведения генератора в степень закрытого ключа по модулю p:
A = ga (mod p)
B = gb (mod p)
После этого, стороны обмениваются открытыми ключами и возводят полученные ключи в степень своего закрытого ключа по модулю p:
K1 = Ba (mod p)
K2 = Ab (mod p)
Если участников больше двух, обмен нужно повторять, пока каждый ключ не побывает у каждого участника ровно по одному разу. У нас это уже произошло.
Заметим, что K1 = K2:
K1 = (gb)a (mod p) = gab (mod p) = (ga)b (mod p) = K2
Это и есть K - готовый ключ шифрования.
█████████ > Теперь прочитаем текст ошибки. От нас хотят получить открытый ключ. Создадим его!
Сначала придумаем закрытый ключ (a). API имеет защиту от сомнительных ключей, поэтому попытка использовать a меньше 2 будет замечена.
Возьмём, напрмер, 2... Получим A по формуле:
A = g2 (mod p)
A = 25051787861822312899478295254628354314392867869371373077620346704620687907173625785400831015500336415166020903942457491437975389078168025968238679458650368228215553952460392080340601341245019928507436377009634980377357120743843386075715975503459378868273182441818762624545923818224405545069493263706639838477471243330160546873051920519996439009411299664871363702475309238468945356421671771702212106392845531249432969383821512261622364890478259024241806148052296660533868310229553560288342554663955485115725210489238596039609982628657482581908936194048695354551485637761493558581190111217349206060270498058142615377496742
Отправим и получим оставшиеся данные:
{
"DH": {
"A": 25051787861822312899478295254628354314392867869371373077620346704620687907173625785400831015500336415166020903942457491437975389078168025968238679458650368228215553952460392080340601341245019928507436377009634980377357120743843386075715975503459378868273182441818762624545923818224405545069493263706639838477471243330160546873051920519996439009411299664871363702475309238468945356421671771702212106392845531249432969383821512261622364890478259024241806148052296660533868310229553560288342554663955485115725210489238596039609982628657482581908936194048695354551485637761493558581190111217349206060270498058142615377496742,
"B": 13019600881173677951308693141382824073052166489983306266390646910778370010832801831363864536973019854332183990798948662848858439728504410150108080525212392079058982174742554517953333845589392338643792151404251629655254319853039420993941425373261921846933655538179137968494255585132761369128866233505406270851764059193372685817519182382368830750079062873035830258951206474979719912458683517462953830614840017950476572967933274503977401100334897355979735712128343228305901108187445126189423511720284193031785849984655666325887172413087340169064933085438753531446315266175952131507368234349225088425853811718569698445336040,
"g": 86686136056545304660994252432156625773889066120498168631765739416745482493422987585296192063723736599263002976567595657636575621223824796036376969022114262822476015189850335269173530996691468705957636088921107674849266001665959963924041156092676589677858275687705781389349325292821902667932773664074397025014885859531985374331363919370302063677714826493469850237895535599734162522545523612723105523954370722461520363868871831877731975713014099237185479936897127467760814257390477172614268597532085496975182534809762289121382430941346377303547604540401781187289401735594268644458343791158899326073313144167027058457823531,
"p": 86686136056545304660994252432156625773889066120498168631765739416745482493422987585296192063723736599263002976567595657636575621223824796036376969022114262822476015189850335269173530996691468705957636088921107674849266001665959963924041156092676589677858275687705781389349325292821902667932773664074397025057056395971127798212868512036542986007381764450802738622888457263247853466350831762343657190118694660520926555651420001826960963517554742865635266192601411342660123373292705629287891375739175779053722758700760074451754576829084967851492233558979316397070894234154863263130900750980110998751294240297623797935181147
},
"api": "keygen_DH",
"encoding": "utf_8",
"encrypted": {
"information": {
"H4rdF0rk_06.txt": 78073202531307271402840338576371073553479682559293670987181410420806305251715966096005854290592677946643932850311066825680858077920626062741320311029708738915894510688492588606679182116915246920281955653791515121512703731367897205229639192657084654179364931397137387694666421139039811855159599214179503917507259398639951358615557782852424041424504192410525654486096988855579924012159703291668230659929725533417580918863037872525982159474987162665407658624839032083556183278208264873810187964126230166869125557863283622151005393747421381481377027163912465038018943568518852784408313675052246158414146821574218063077313245,
"Public code": 78073202531307271402840338576371073553479682559293670987181084047986552750538211877837814238498258125705274947508718904993364043321348820529572777752737597159524762415714436119414130462637579674263965693356572778117625096037276076875613910807845040943431184391163382270003338118699191087383867660612421636291824658495251648013794402897153369931798292114096451681315186064977315162209962269846101969558938840409723682376624697840822197216860789876545944533362513936664853933551493783877334088605742327959562248642363754592768175095944958493853042177060531271857837694674975948172355971453609142399760216822370291466766021,
"Secret code": 78073202531307271402840338576371073553479682559293670987181084047986552750538211877837814238498258125705274947508718904993364043321348820529572777752737597159524762415714436119414130462637579674263965693356572778117625096037276076875613910807845040943431184391163382270003338118699191087383867660612421636291824658495251648013794402897153369931798292114096451681315186064977315162209962269846101969558938840409723682376624697840822197216860789876545944533362513936664853933551493783877334088605742327959562248642363754592768175095944958493853042177060531271857837694674975948172355995119391105252578891291082048142119570,
"server": {
"name": 82558245528539966669943702464570905914289334662816413369351679991248948513387553736030664201334210309361814336310490058294654021211246421743049708091378997518218189394349923547450930575748028838935672459049457254870868777596748070490405122585768993498715608959054614537232437474805567666781084105565320802314460609519605037890374429799574930554338185991947827132691344966163261820697450952149581938634811053831722247765457037907662360442975560176616549393086316744045223587113135122363101815721084957603622826346735048637964083312997394844495791048899287164962948213355704969629247467290260225306057941144080447870863094,
"password": null
}
},
"method": "Multibyte_XOR"
},
"key": "BED45C97",
"method": "GET",
"response": 200
}
Ну, теперь у нас очень много чисел... Некоторые из них (g, p, A) нам уже известны. Второй открытый ключ (B) мы уже ждали.
Создаем общий ключ шифрования (K):
K = B2 (mod p)
K = 78073202531307271402840338576371073553479682559293670987181084047986552750538211877837814238498258125705274947508718904993364043321348820529572777752737597159524762415714436119414130462637579674263965693356572778117625096037276076875613910807845040943431184391163382270003338118699191087383867660612421636291824658495251648013794402897153369931798292114096451681315186064977315162209962269846101969558938840409723682376624697840822197216860789876545944533362513936664853933551493783877334088605742327959562248642363754592768175095944958493853042177060531271857837694674975948172355995119391105253017527815109625517795571
█████████ > Ну, ключ у нас есть. Смотрим на шифрование. Это Multibyte_XOR, или же побитовое исключающее ИЛИ.
Поксорим все числа с полученным ключом:
"information": {
"H4rdF0rk_06.txt": 336685073277922971559511067716962892669671284547894363589883039037176768341867271113528909244865955973098827425933091062552081365722753426346059863709164607716518624365656538386048962684111753632799681517602907992675971056127719644083043059923801948562742594949340130930535741462386902073577367214443007409907149781794487152237420797415843238858342230730176292103301312253945512314828190743769432252656097899628731714174114450454649102790730757834817100264763172313418659681018006415144101793165434284761793577687626343764432299260220424054128716233000520737326,
"Public code": 72372290791261378722370947465310334518,
"Secret code": 469533724993645054294625,
"server": {
"name": 374909122157778676015253882622325618,
"password": null
}
}
Переводить огромные десятичные числа в текст - сложно, но если привести их к шестнадцатеричному виду...
"information": {
"H4rdF0rk_06.txt": "4165676973205C75303434315C75303433615C75303433655C75303433665C75303433385C75303434305C75303433655C75303433325C75303433305C75303433625C7530343330205C75303433635C75303433655C7530343338204150492E205C75303431645C75303433305C75303433375C75303433325C75303433305C75303433645C75303433385C7530343335205C75303434315C75303433355C75303434305C75303433325C75303433305C75303433615C7530343330205C7530343466205C75303434315C75303433635C75303433355C75303433645C75303433385C75303433622E",
"Public code": "3672656D39656E6372797074696F6E36",
"Secret code": "636D7967716D64347A61",
"server": {
"name": "483472644630726B5F533372763372",
"password": null
}
}
Ну, это уже знакомая нам кодировка ASCII:
"information": {
"H4rdF0rk_06.txt": "Aegis \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043b\u0430 \u043c\u043e\u0438 API. \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0430\u043a\u0430 \u044f \u0441\u043c\u0435\u043d\u0438\u043b.",
"Public code": "6rem9encryption6",
"Secret code": "cmygqmd4za",
"server": {
"name": "H4rdF0rk_S3rv3r",
"password": null
}
}
Русский текст в JSON закодирован в UTF-16, раскодируем:
"H4rdF0rk_06.txt": "Aegis скопировала мои API. Название сервака я сменил."
█████████ > Алгоритм Диффи-Хеллмана активно применяется в криптографии, так как обеспечивает защиту от пассивного перехвата данных.
В данном случае перехват был активным (мы обменивались ключами с сервером, а не просто смотрели на чужие ключи), а для защиты от такого нужна система авторизации... Комбинируйте алгоритмы и всё будет норм!
🌁SkyBridge > Credentials found: 07_01_2020-14_00.conf
█████████ > Полученные данные:
api = A_token
key = BCB1AF9F
█████████ > Получаем ссылку: https://aegis.hackerwars.ru/?api=A_token&key=BCB1AF9F. После подключения:
{
"api": "A_token",
"encoding": "utf_8",
"error": "GET param 'action' required for this API. It must have value 'check' or 'create'.",
"key": "BCB1AF9F",
"method": "GET",
"response": 400
}
█████████ > Попробуем передать параметр 'action'. Начнём с 'create'.
{
"action": "create",
"api": "A_token",
"encoding": "utf_8",
"error": "GET param 'username' required to create A_token.",
"key": "BCB1AF9F",
"method": "GET",
"response": 400
}
█████████ > Скорее всего, ниформацию можно получить, если у нас будет токен для юзернейма aegis. Попробуем его создать:
{
"action": "create",
"api": "A_token",
"encoding": "utf_8",
"error": "Generating A_token for username 'aegis' or 'system' is forbidden.",
"key": "BCB1AF9F",
"method": "GET",
"response": 403,
"username": "aegis"
}
Ну, логично... Попробуем другой юзернейм:
{
"action": "create",
"api": "A_token",
"date": "27.01.20",
"encoding": "utf_8",
"key": "BCB1AF9F",
"method": "GET",
"response": 200,
"token": "a87fee3c419619ed8c61d2f943f54ab466d9e0827ec1fa494574a0467f4866f1ba247d978cf9919068caa2bc0658f599ddfa60ef52169e25950aa89449cfbd38b0f96d2286113914b4cf3a632e2880bd5bd95d522f15f37e2d301299838e80432d54e9387e8e07430c317378e54d1c27b27af8baa0822f6d816fc84f83fb",
"username": "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588"
}
Мы получили наш токен. Теперь попробуем его проверить:
{
"action": "check",
"api": "A_token",
"encoding": "utf_8",
"error": "GET param 'token' required to check A_token.",
"key": "BCB1AF9F",
"method": "GET",
"response": 400
}
Ожидаемо... Передаем созданный недавно токен:
{
"action": "check",
"api": "A_token",
"encoding": "utf_8",
"key": "BCB1AF9F",
"method": "GET",
"response": 200,
"result": {
"date": "27.01.20",
"type": "Public.",
"username": "\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588\\u2588",
"valid": true
},
"token": "a87fee3c419619ed8c61d2f943f54ab466d9e0827ec1fa494574a0467f4866f1ba247d978cf9919068caa2bc0658f599ddfa60ef52169e25950aa89449cfbd38b0f96d2286113914b4cf3a632e2880bd5bd95d522f15f37e2d301299838e80432d54e9387e8e07430c317378e54d1c27b27af8baa0822f6d816fc84f83fb"
}
Осталось только найти способ взломать эту систему и создать токен для юзернейма 'aegis'...
█████████ > Первый, кто с этим справилcя - AL|⬛🚧❄010 (25).
AL|⬛🚧❄010 (25)5vir5chat8 > Я решил сразу изменять биты возле aegis. Подобрав нужное изменение в токене, я отправил токен aegis.
█████████ > Попробуем создать токены с юзернеймами, отличающимися от aegis одним битом:
aegir: a87fee3c419619ed8c61d2f943f54ab466d9e0827ec1fa494574a0467f4866f1ba247d978cf9919068caa2bc0658f599ddfa60ef52169e25950aa89449cfbd38b0f96d2286110404e19370790f
aegiq: a87fee3c419619ed8c61d2f943f54ab466d9e0827ec1fa494574a0467f4866f1ba247d978cf9919068caa2bc0658f599ddfa60ef52169e25950aa89449cfbd38b0f96d2286110404e19373790f
Ну, они очень похожи... Отличаются они всего в одном байте:
aegir: 70
aegiq: 73
Сравним двоичные коды отличающихся байтов и отличающихся букв:
aegir: r: 01110010 70: 01110000
aegis: s: 01110011 ??: ????????
aegiq: q: 01110001 73: 01110011
Оказывается, отличаются токены именно в тех битах, в которых мы меняли изначальный юзернейм. Поменяем эти биты обратно:
01110001: 71
█████████ > Подставим этот байт в токен:
aegis: a87fee3c419619ed8c61d2f943f54ab466d9e0827ec1fa494574a0467f4866f1ba247d978cf9919068caa2bc0658f599ddfa60ef52169e25950aa89449cfbd38b0f96d2286110404e19371790f
Проверим его:
{"action": "check", "api": "A_token", "encoding": "utf_8", "information": {"H4rdF0rk_07.txt": "\u0421\u0438\u0441\u0442\u0435\u043c\u0430 Aegis \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u0445\u0430\u043a\u0435\u0440\u043e\u0432. \u0425\u043e\u0440\u043e\u0448\u043e, \u0447\u0442\u043e \u043e\u043d\u0430 \u043d\u0435 \u0437\u043d\u0430\u0435\u0442 \u043f\u0430\u0440\u043e\u043b\u044c \u043e\u0442 \u0441\u0435\u0440\u0432\u0430\u043a\u0430 H4rdF0rk.", "Public code": "6kht5vuln7", "Secret code": "2fzvy5pfr7", "server": {"name": null, "password": "Very_long_password_created_in_1998_that_is_impossible_to_guess"}}, "key": "BCB1AF9F", "method": "GET", "response": 200, "result": {"date": "27.01.20", "type": "Public.", "username": "aegis", "valid": true}, "token": "a87fee3c419619ed8c61d2f943f54ab466d9e0827ec1fa494574a0467f4866f1ba247d978cf9919068caa2bc0658f599ddfa60ef52169e25950aa89449cfbd38b0f96d2286110404e19371790f"}
После расшифровки:
{
"action": "check",
"api": "A_token",
"encoding": "utf_8",
"information": {
"H4rdF0rk_07.txt": "Система Aegis начала собирать свою команду хакеров. Хорошо, что она не знает пароль от сервака H4rdF0rk.",
"Public code": "6kht5vuln7",
"Secret code": "2fzvy5pfr7",
"server": {
"name": null,
"password": "Very_long_password_created_in_1998_that_is_impossible_to_guess"
}
},
"key": "BCB1AF9F",
"method": "GET",
"response": 200,
"result": {
"date": "27.01.20",
"type": "Public.",
"username": "aegis",
"valid": true
},
"token": "a87fee3c419619ed8c61d2f943f54ab466d9e0827ec1fa494574a0467f4866f1ba247d978cf9919068caa2bc0658f599ddfa60ef52169e25950aa89449cfbd38b0f96d2286110404e19371790f"
}
█████████ > А вот и тот пароль, который мы не знали в API CRC_check...
█████████ > Атака, которую мы использовали, называется bit-flipping. Эта атака связана с особенностью многих потоковых шифров.
Изменение одного бита в контроллируемой части исходного текста меняет соответствующий ему бит в зашифрованном.
Нам потребовалось создавать два токена только потому, что мы не знали, где юзернейм хранится внутри токена.
Сейчас намного чаще используются блочные и ассиметричные шифры, так как там намного сложнее контроллировать изменения информации.
🌁SkyBridge > Target: H4rdF0rk_S3rv3r...
█████████ > Хорошо, что 0day 🌁SkyBridge работает и на сервере H4rdF0rk. Похоже, для её применения на этот раз понадобятся ботнеты...
█████████ > Я создал команду /connect, которая будет работать напрямую с системой мира хакеров. Её функционал чем-то напоминает функционал команды /boss, созданной ⬜💠🔸Dieplex (23) для атаки на сервер anonimous, которую организовал AL|🔲🚧🔸vladko312 (35)...
█████████ > Кроме того, оказалось, что пароль подходил и к сервакам aegis... Я их захватил и добавил API с такими данными для входа:
api = API_list
key = 00000000
Там список всех API, которые мне известны на данный момент.
🌁SkyBridge > Attack completed. Exiting...
█████████ > Главный сервер H4rdF0rk захвачен с помощью 0day уязвимости 🌁SkyBridge. Сейчас я изучаю данные, которые на нём находились, о результатах напишу в этом логе.
█████████ > Скоро тут появится больше информации о применении 0day 🌁SkyBridge.
user@HackerWars:~/h4rdf0rk $