- PVSM.RU - https://www.pvsm.ru -
Думаю, многие из читателей с добрым словом вспомнят серию игр «Казаки» [1], многочасовые баталии, военные хитрости и бесподобное звуковое сопровождение — отличная стратегия своего времени.
Спустя 15 лет они вернулись, и теперь уже в режиме онлайн, о проблемах и уязвимостях новой версии и пойдет речь в данной статье.
Все исследования проводились исключительно в благих целях, правообладатель и разработчик [2] был заведомо уведомлен о найденных проблемах, статья несет поучительных характер и не призывает к активной эксплуатации описанных проблем.
Все началось с простого вопроса — «как шифруются сетевые данные?», после отлова первого же пакета ответ стал очевиден — «никак». Никаких xor операций, никаких подписей, честные и правдивые байты.
В этот же момент (после осознания простоты возможного анализа), было принято решение идти дальше и попытаться воссоздать примитивную работу сервера (вход, регистрация, восстановление пароля), для этого требовалось понять:
После отлова очередного десятка пакетов «шапка» стала яркой и весьма четкой:
struct NetPacketHeader {
unsigned int Size; // размер чистых (без учета самой шапки) данных пакета
unsigned char Direction; // уникальный идентификатор пакета
unsigned char Mode; // предположительно описывает режим передачи пакета - броуд или приват
unsigned int SessionId0; // идентификатор пользователя, выданный сервером при входе
unsigned int SessionId1; // тоже самое, но для цели, например: приватного чата
};
Тем же путем были выявлены основные черты сериализации:
А список серверов был найден обычным поиском по названию (dataresourcesservers.dat), хранителем данных оказался читабельный скрипт собственного производства.
По завершению этих шагов разложение пакетов начало сводиться только к усидчивости и внимательности, например:
3D 00 00 00 9A 01 00 00 00 00 00 00 00 00 07 31 2E 30 2E 30 2E 37 05 31 2E 32 2E 33 14 61 61 61 61 61 61 61 61 61 61 40 67 6D 61 69 6C 2E 63 6F 6D 0A 61 61 61 61 61 61 61 61 61 61 0E 39 30 30 30 2D 38 30 30 30 2D 35 30 30 30
Size: 3D 00 00 00 (всего в пакете 75 байт, но 61 байт является телом, а 14 других шапкой)
Direction: 9A
Mode: 01
SessionId0: 00 00 00 00
SessionId1: 00 00 00 00VersionStringSize: 07
VersionString: 31 2E 30 2E 30 2E 37 (1.0.0.7)
UpdateStringSize: 05
UpdateString: 31 2E 32 2E 33 (1.2.3)
EmailStringSize: 14
EmailString: 61 61 61 61 61 61 61 61 61 61 40 67 6D 61 69 6C 2E 63 6F 6D (aaaaaaaaaa@gmail.com)
PasswordStringSize: 0A
PasswordString: 61 61 61 61 61 61 61 61 61 61 (aaaaaaaaaa)
GameKeyStringSize: 0E
GameKeyString: 39 30 30 30 2D 38 30 30 30 2D 35 30 30 30 (9000-8000-5000)
Таков пакет запроса входа на сервер от клиента.
За короткий промежуток времени был сделан примитивный сервер (на основе asio [4]), способный общаться с оригинальным клиентом, за час с небольшим он безупречно мог:
Столь быстрая реализация бодрила лучше любого кофе и было решено потратить остаток ночи на работу с лобби, а именно:
С первым же пунктом начались не совсем понятные (по началу) проблемы:
Получилось отловить и разложить три пары (запрос клиента и ответ сервера) пакетов — создание, обновление (что включало в себя и удаление), а так же вход в публичное лобби.
Именно последнее и вызывало головные боли, вход в публичное лобби проходил на «ура», а вот в приватное не совсем, было не ясно, где искать введенный пользователем пароль, что-бы проверить верность данных, ведь пакет один — как для входа в публичное, так и для входа в приватное лобби, и он содержит лишь шапку и одно целое (идентификатор сессии создателя лобби), за объяснениями пришлось лезть «под капот», но не привыкшие к результату компиляции Delphi кода глаза ничего толкового так и не нашли.
В конечном итоге стало очевидным — сервер никак не обрабатывает пароли лобби, от слова «совсем», а значит в теории было возможным зайти в любое лобби и на оригинальном сервере, ведь клиент получает пароль в чистом виде.
Теория была доказана в три шага:
Все верно — приватное == публичное.
Остальные пункты повестки ночи прошли относительно без особых затруднений, синхронизация пользователей — простое зеркало, от одного пользователя ко всем участникам лобби, закрытие слота (например, что-бы выгнать участника из лобби) вызвало небольшое беспокойство, сервер принимает запрос на закрытие слота, если слот был занят участником — выгоняет его, посылая оповещение, но, если игнорировать пакет на стороне клиента — визуально мы остаемся в лобби, не теряя связь, вызывает ли это проблемы с запуском игры у оставшихся участников — хороший вопрос, ответ на который получить в такое время суток не удалось.
Так же не малое беспокойство вызвало и выдача имени ПК создателя лобби, зачем оно вообще выдается участникам, если создатель != хост — вопрос, на который еще предстоит ответить.
По окончанию ночи удалось дойти до входа в игру, из десяти попыток синхронизации геймплея успехом завершилась лишь одна, и та была успешна лишь от части, один игрок не получает данных о действиях другого, что вызывает асинхронизацию и приводит к спешному ступору игры, а значит работы еще много.
Любой программист, которому приходилось доводить сетевое приложение до публики так или иначе ощутил на себе главное правило: никогда не доверять клиенту.
Каждое действие клиента должно иметь одобрение сервера, иначе есть существенный риск, если и не загубить весь проект, то заложить в него мины, которые будут взрываться в самое неожиданное время, хороший пример, но плохой сетевой работы — игра MU Online [5], ей более десяти лет, а тривиальные проблемы клонирования игровых предметов (с помощью манипуляций пакетами) привели к тому, что пришлось отключить [6] функционал персонального магазина.
Отдельная тема для дискуссий это сохранность персональных данных, как можно понять из описанных примеров: такой подход, передачи чистых байтов, а особенно строк — большой грех для любой компании работающей в информационной сфере, прямой путь в ад к краже аккаунтов.
→ Пример на GitHub [7]
Автор: RevalSoft
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/informatsionnaya-bezopasnost/228337
Ссылки в тексте:
[1] «Казаки»: https://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D0%B7%D0%B0%D0%BA%D0%B8_(%D1%81%D0%B5%D1%80%D0%B8%D1%8F_%D0%B8%D0%B3%D1%80)
[2] правообладатель и разработчик: https://ru.wikipedia.org/wiki/GSC_Game_World
[3] «шапка»: https://en.wikipedia.org/wiki/Header_(computing)
[4] asio: http://think-async.com/
[5] MU Online: http://muonline.webzen.com/
[6] пришлось отключить: http://muonline.webzen.com/news/notices/22266/notice-personal-store-function-disable-on-2016-12-13-maintenance?page=3
[7] Пример на GitHub: https://github.com/movqword/CossacksHijaking
[8] Источник: https://habrahabr.ru/post/318870/?utm_source=habrahabr&utm_medium=rss&utm_campaign=sandbox
Нажмите здесь для печати.