Привет. Знаете про Minecraft? Да-да это та самая нашумевшая игра из 2014 про кубики, дракона и злых школьников, которые объединились в невероятной амальгаме. В игре есть как singleplayer, так и multiplayer. Но что шифровать в одиночке? (Хотя идея защитить свою карту паролем, чтобы негодяи не убили вашего верного пса, довольно заманчива). Сегодня я расскажу про то, как работает шифрование в многопользовательском режиме. Но сначала нужно определиться с инструментарием.Вот что я буду использовать: MCWP  MITM прокси для исследования протокола майнкрафт. yggdrasil-server  Сервер авторизации, похожий на тот что использует mojang для лицензионных аккаунтов. И да, все команды приведены для Windows. Мультиплеер майнкрафт построен на клиент-серверной архитектуре поверх tcp. По официальным данным начиная с версии 1 (до этого были еще альфа и бета) сетевая подсистема игры построена на фреймворке Netty (Java).Если сильно упростить, то роли клиента и сервера сведены к выполнению в цикле этих действий: Подключается к серверу Подтверждает свой юзернейм (если сервер отправит Encryption Request) Загружает и рисует карту Перемещается по карте и отправляет свою позицию Взаимодействует с остальными миром и отправляет события на сервер Получает и проигрывает события от сервера Принимает игроков Проверяет их юзернеймы (если online-mode: true) Генерирует карту и отправляет клиентам Получает события и позицию от игроков и передает другим игрокам Майнкрафт - платная игра. И основной причиной использования криптографии в ней была именно аутентификация, то есть определение лицензионных игроков и подтверждение их доступа. А шифрование было уже бонусом. И хотя игра может работать без лицензии (изначально этот режим создавался для игры без доступа к сети интернет, когда нельзя проверить подлинность игрока, и называется в файлах конфигурации online-mode: false) без аутентификации пользователя шифрование не включается. Кстати, сам Notch (создатель игры) говорил про пиратов так: «Качайте пиратку. Если она все также будет нравиться Вам в будущем, когда Вы сможете позволить ее себе, тогда и купите. Также не забудьте почувствовать себя плохо;)». Лицензионной копией считается та, которая была куплена на сайте разработчика и имеет аккаунт Mojang (или Microsoft). А также привязанный к этому аккаунту uuid. UUID - это уникальный идентификатор каждого из игроков майнкрафт. Он не меняется даже после смены никнейма в игре. Процесс обмена информацией с реестром аккаунтов (сервером аутентификации и авторизации) начинается еще задолго до подключения к серверу. Игрок заходит в лаунчер(программа для запуска игры) Игрок вводит пароль и логин от аккаунта Лаунчер отправляет их на сервер и если они верны получает токен Лаунчер хранит и обновляет токен при каждом входе в игру Уже заметили? Лаунчер отправляет логин и пароль игрока на сервер. Да, через https, но что нам мешает узнать логин и пароль подменив сервер авторизации? Так и поступим.Разворачиваем yggdrasil-server:Потребуется Python 3.8+ Теперь нужно заставить лаунчер жертвы доверять нашему tls сертификату. Раньше, лаунчер был, как и сама игра просто .jar файлом и заполучить его доверие можно было так же просто, как и клиента игры, но теперь лаунчер это нативная .exe программа которая использует корневые сертификаты системы. Углубляться в то, как креативно можно заставить жертву понажимать пару неизвестных кнопок не будем, но посчитаем что этот этап уже пройден. Тогда остается направить запросы на наш сервер. Например, изменя ip домена authserver.mojang.com на ip собственного сервера в файле hosts. Теперь, когда жертва захочет зайти в игру, она разлогинится в лаунчере, так как сессия не зарегистрирована на нашем сервере. И клиенту придется ввести логин и пароль от его аккаунта. Когда это произойдет, в консоли сервера авторизации мы увидим это: В дебаг логе видно данные учетной записи. Атака удалась. А игрок так и не смог войти в лаунчер, так как такого пользователя на нашем сервере не было.Да, это выглядит интересно, но как заставить игрока установить корневой сертификат и отредактировать hosts?Игроки майнкрафт в основном - дети и обмануть их проще всего. Хочешь скачать новый мод на слона в майнкрафт? Скачай наш .exe нажми два раза и при запросе прав администратора нажми - да, и все слон - твой, а точнее кот в мешке.Но да, это по сути глупость ведь попадя на компьютер пользователя и получив права администратора можно сделать гораздо 'больше'. Я никого не призываю кого-либо взламывать или обманывать таким или любым другим способом. Вся ответственность за интерпретацию этого текста как побуждения к действию лежит на вас. Хорошо, мы узнали, что можно подменить сервер авторизации. Но что, если подменить сервер аутентификации (сервер сессий) который отвечает за подтверждение того, что игрок с конкретным uuid и никнеймом входит на сервер? Для этого потребуется локальный игровой сервер, но его установку я показывать не буду. Также потребуется лицензионный аккаунт (ну да), или "локальный" на yggdrasil-server.Чтобы увидеть, какими пакетами обмениваются сервер и клиент, воспользуемся mcwp.Python 3.7+ Заходим на порт прокси, и видим в консоли такую картину: Что мы видим? Сервер посылает запрос на включение шифрования, и клиент на него отвечает, а затем они оба включают симметричное шифрование AES.После включения шифрования mcwp уже не показывает дальнейший обмен пакетами, так как не может их расшифровать, ведь не знает ключ. Но так у меня нет лицензионного аккаунта майнкрафт, и аккаунт m7 зарегистрирован локально на сервере yggdrasil-server и сервер тоже использует этот сервер аутентификации, мы можем расшифровать это соединение загрузив в mcwp модуль для локальной дешифровки. Попробуем еще раз: И сразу видим отличия в логе инициализации: Видно что используется дешифратор протокола и был сгенирирован ключ RSA.Подключаемся еще раз и смотрим в консоль: Теперь мы видим все пакеты, которые передаются между клиентом и сервером (локальным сервером, который использует наш 'игрушечный' сервер авторизации).Видя все пакеты, теперь мы можем отслеживать местоположение игрока и узнать где его дом. (ну и забрать алмазы конечно) Как работает шифрование в майнкрафт и как его можно расшифровать.У протокола майнкрафт есть 3 состояния: Рукопожатие, Вход в игру, Игра. Рассмотрим поближе что и кому отсылают сервер и клиент во время входа в игру: Client -> Server:  ClassicLogin.LoginStart(Username='m7')Клиент передает юзернейм, с которым хочет войти Client <- Server:  ClassicLogin.EncryptionRequest(ServerID, PublicKey, VerifyToken)Сервер передает свой публичный ключ и токен проверки Client -> SessionServer:  SessionJoin(accessToken, selectedProfile, serverId)Клиент передает на сервер сессий информацию о сервере к которому собирается подключится. Клиент фактически говорит: у меня есть действительный токен 123 связанный с юзернеймом m7 и я хочу зайти на сервер с sha1(публичным ключем 456 и общим секретом 789) Client -> Server:  ClassicLogin.EncryptionResponse(SharedSecret, VerifyToken)Клиент отвечает зашифрованными публичным ключем сервера, симметричным ключем и токеном проверки SessionServer <- Server:  hasJoined(serverId: str, username: str) Сервер проверяет правда ли лицензионный игрок с таким юзернеймом хочет зайти на этот сервер Client <- Server:  ClassicLogin.LoginSuccess(UUID='917cd927-86a2-42cd-b7b0-b1838ea4c933', Username='m7')Сервер передает клиенту его юзернейм и UUID и переводит протокол в состояние Игра Когда сервер запрашивает у клиента включение шифрования, он посылает ему свой публичный ключ RSA и данные проверки, которые клиент должен зашифровать ключем сервера. Когда прокси видит этот пакет, он подменяет ключ и/или данные проверки на собственные. Это нужно, что-бы когда клиент ответит общим симметричным ключем, зашифрованным публичным ключем сервера, мы могли его расшифровать и сохранить для расшифровки последющих сообщений. Затем клиент отвечает общим симметричным ключем и рандомными данными, которые отправил сервер, но зашифрованные публичным ключем сервера. А перед тем как ответить, клиент делает запрос на sessionserver.mojang.com и отправляет туда свой токен, полученный при авторизации, и хеш sha1(shared_secret + public_key).Это нужно для защиты от атаки человека по середине, который подменит публичный ключ (но не подменит сессию). Когда прокси получает ответ от клиента, он обращается к серверу сессий и дает команду подменить sha1(shared_secret + proxy_public_key)  на sha1(shared_secret + server_public_key). Когда сервер получает ответ от прокси, он обращается к серверу сессий на конечную точку session/minecraft/hasJoined с serverId, равным sha1(shared_secret + server_public_key) с общим секретом и username получеными от клиента, но так как прокси подменил serverId, сервер получает ответ 204  от сервера сессий (означает что проверка юзернейма прошла успешно) и подтверждает вход в игру пакетом LoginSuccess(UUID='917cd927-86a2-42cd-b7b0-b1838ea4c933', Username='m7') который передаётся уже зашифрованным с помощью общего секрета и шифра AES. https://wiki.vg/Protocol_Encryption#Authentication https://wiki.vg/Legacy_Mojang_Authentication#Authenticate