Files
redl-gamepanel/БЕЗОПАСНОСТЬ.md
T
redl e0b96d3501 Двуязычный интерфейс (RU/EN) с автоопределением + документация на двух языках
Панель:
- engine/main/lang.php: класс Lang, автоопределение языка (?lang= -> cookie -> Accept-Language -> конфиг)
- перевод применяется к готовому ответу через ob_start(): покрывает всю панель, админку и письма,
  не требуя правки 200 файлов шаблонов; отсутствующая фраза остаётся русской
- замена только на границах слов, иначе короткий ключ портил длинные слова (Модуль -> Modуль)
- AJAX-ответы переводятся отдельно (json_encode экранирует кириллицу в \uXXXX)
- application/lang/en.php: 657 переводов; ru.php как точка расширения
- переключатель RU/EN в шапке кабинета, админки и в подвале страницы входа
- 'lang' в config.php — язык по умолчанию
- проверено обходом 20 разделов: 0 непереведённых фраз, 0 мешанины языков, 0 фаталов

Документация — теперь на русском и английском:
- README.en.md, CHANGES.en.md, SECURITY.en.md, GUIDE.en.md
- переключатели языка в начале каждого документа
- раздел «Язык интерфейса» в инструкции: как работает, как добавить свой язык
2026-07-30 02:25:34 +00:00

16 KiB
Raw Blame History

Русский · English

Аудит безопасности: что нашли и что исправили

Аудит исходников сборки HostinPL 5.6 («nulled»-копия). Проверялось: скрытые закладки и вебшеллы, обфускация, отправка данных на сторонние хосты, SQL-инъекции, RCE, обход авторизации, хранение паролей, установщик и Dockerfile.

Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом.


Главное в двух словах

Форк заявлял: «в панели вырезаны все шеллы/дыры». Проверка показала — заявление верно лишь частично. Явных работающих закладок мы не нашли, обфускации в коде панели нет. Но нашлись:

  • доказательство, что в этой родословной вебшеллы действительно были — один найден обезвреженным;
  • две SQL-инъекции, доступные без авторизации — про них в описании форка ничего не сказано;
  • пароли пользователей писались в базу в открытом виде;
  • установка кода на игровые ноды по обычному HTTP без проверки подписи.

Первые три пункта мы исправили. Ниже — детали.


1. Вебшелл в родословной сборки — обезврежен

Файл: panel/application/public/js/proxy/proxy.php — 7 строк целиком:

<?php
if(isset($_GET['cmd'])){
    print('Backdoor fixed by Xopowblu-4EJlOBEK aka Und3X (und3x.ru)');
}else{
    header("Location: /");
}
?>

Файл принимает параметр ?cmd= — это классическая сигнатура вебшелла, принимающего команды. Здесь тело подменено надписью, то есть конкретно этот шелл обезврежен. Но сам факт однозначен: в цепочке распространения этой сборки закладки размещались осознанно, и лежала одна из них в папке со скриптами, куда обычно никто не смотрит.

Наше решение: файл оставлен как есть — как свидетельство. Он ничего не исполняет. Если он вам не нужен, удалите: rm panel/application/public/js/proxy/proxy.php.

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

grep -rIn --include=*.php -E "eval\(|assert\(|base64_decode|gzinflate|\\\$_REQUEST\[" .
find . -path ./engine/libs -prune -o -name '*.php' -print | grep -E '(assets|public|tmp)/'

2. Чего не нашли (это хорошая новость)

Проверено отдельно, результат отрицательный:

  • Обфускации в коде панели нет. Ноль вхождений eval, assert, create_function, gzinflate, $$, $_REQUEST вне вендорных библиотек. Ни одного длинного base64-блоба.
  • Посторонних PHP-файлов нет в каталогах ассетов, загрузок и временных файлов — кроме описанного proxy.php.
  • Утечки данных на хосты автора нет. Упоминания vipadmin.club были только в мета-теге keywords и адресе отправителя писем, osmp.ga — ссылка в подвале. Никакой отправки паролей, лицензий или данных БД наружу.
  • Админ-гейт цел. Во всех контроллерах application/controllers/admin/** есть проверка getAccessLevel() (уровень 2 — админка, 3 — игры и локации). Ни одного файла без проверки.
  • Панель не вызывает shell напрямую. Наивный grep даёт 54 exec и 8 system, но все они внутри вендорных библиотек: в phpseclib exec() — это метод SSH, а не запуск процесса, остальное в elFinder. Код самой панели shell не вызывает.

3. Две SQL-инъекции без авторизации — ИСПРАВЛЕНО

Самое серьёзное из найденного. В описании форка об этом не сказано ничего.

Файл: panel/application/models/users.php, функция createAuthLog().

Функция пишет журнал попыток входа. Было так (значения подставлялись в SQL напрямую):

$query=$this->db->query("INSERT INTO `authlog` (... ,`ip`, ... ,`password`)
  VALUES (NULL, '".$userid."', '".$ip."', ... , '".$password."');");

Вектор 1 — через форму входа. $password — это сырой пароль из $_POST, без экранирования. Вызывается в account/login.php при каждой попытке входа, в том числе неудачной, то есть до какой-либо авторизации. Любой человек с улицы мог инжектить SQL через форму логина.

Вектор 2 — через подделку заголовка. $ip брался из getRealIpAdress() (engine/main/user.php), а та возвращала заголовок CF-Connecting-IP без всякой проверки — ранний return стоял до блока с filter_var:

if (!empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
    return $_SERVER["HTTP_CF_CONNECTING_IP"];   // как есть, без валидации
}

Заголовок подставляется клиентом, значит атакующий полностью контролировал значение, попадавшее в SQL. Этот же неproверенный IP уходил в запрос к внешнему сервису ip-api.com.

Что сделано:

  • все значения в INSERT проходят через $this->db->escape() либо приводятся к (int);
  • CF-Connecting-IP проверяется через filter_var($ip, FILTER_VALIDATE_IP), иначе игнорируется и берётся REMOTE_ADDR;
  • запрос к ip-api.com выполняется только для валидного IP и с urlencode().

Проверено на живой установке: попытка инъекции через поле пароля и через подделанный заголовок больше не проходит, таблица authlog на месте, все 27 таблиц целы.


4. Пароли в открытом виде — ИСПРАВЛЕНО

account/login.php передавал в createAuthLog() реальный пароль из формы:

$this->usersModel->createAuthLog($userid['user_id'], $ip, '1', $password);  // успешный вход
$this->usersModel->createAuthLog($userid['user_id'], $ip, '0', $password);  // неудачный тоже

То есть таблица authlog копила пароли всех пользователей в открытом виде — включая опечатки и пароли от других сервисов, которые люди случайно вводят. Читались они кем угодно с доступом к базе, а с учётом инъекции из пункта 3 — и снаружи.

Что сделано: пароль больше не передаётся, в колонку пишется пустая строка. Проверено: после успешного и после неудачного входа колонка пустая.


5. XSS в форме регистрации — ИСПРАВЛЕНО

panel/application/views/common/loginheader.php: параметр ?ref= (реферальный код) выводился в скрытое поле формы без экранирования — <?echo $_GET['ref']?>. Добавлен htmlspecialchars(..., ENT_QUOTES, 'UTF-8'), в контроллере значение приводится к (int).


6. Пароли хранятся как MD5 без соли — НЕ исправлено

login.php:114md5($password), колонка user_password varchar(32).

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

Мы это не меняли: переход на password_hash() затрагивает вход, регистрацию, восстановление и смену пароля и требует миграции существующих пользователей — это отдельная работа, а не правка одной строки.

Что делать: если панель используется с реальными людьми — переводите на password_hash() с прозрачной миграцией (при успешном входе по старому MD5 пересохранять хеш новым алгоритмом).


7. Установка кода на игровые ноды по HTTP — источник убран

panel/engine/games/game_settings.php:54-69 — 18 модулей Node.js для RAGE:MP скачиваются по обычному HTTP с постороннего хоста mc.hostinpl.ru, в виде zip-архивов без подписи и без проверки контрольных сумм.

Любой посредник на пути (или владелец того хоста) может подменить содержимое, и код выполнится на игровых нодах. Оригинальный установщик так же тянул сборки игр с dl.und3x.ru и vipadmin.club.

Что сделано: из нашего установщика ноды скачивание сборок с этих хостов убрано — сборки вы кладёте сами в /home/cp/gameservers/files/<код_игры>/. Строки в game_settings.php не тронуты (это функциональность панели), но пользоваться ими не стоит: либо замените адреса на свои по HTTPS, либо разворачивайте модули вручную.

Node.js в нашем docker/Dockerfile устанавливается из NodeSource по HTTPS с проверкой ключа репозитория.


8. Панель подключается к нодам паролем root — особенность устройства

Таблица locations хранит location_user и location_password (varchar(32)) в открытом виде, подключение идёт через ssh2_auth_password(). Команды панели (useradd, docker, chown /home) требуют прав root, то есть на практике панель ходит на ноду root'ом с паролем.

Изменить это без переработки панели нельзя: она не умеет ни ключи, ни sudo. Меры, которые снижают риск:

# на ноде: SSH и MySQL — только с адреса панели
ufw allow from IP_ПАНЕЛИ to any port 22 proto tcp
ufw allow from IP_ПАНЕЛИ to any port 3306 proto tcp
ufw deny 3306

Установщик ноды печатает эти команды в конце работы. Дополнительно: отдельный пароль на каждую ноду и доступ к базе панели по минимуму — кто прочитает locations, получит root на всех нодах.


9. MariaDB на ноде слушает все интерфейсы — так требует панель

Панель создаёт базы игровых серверов на самой ноде и подключается к ним по сети, поэтому bind-address = 0.0.0.0 обязателен. Установщик так и делает, но выводит предупреждение и печатает команды файрвола — в отличие от оригинального, который менял настройку молча.


10. Регистрационное письмо содержит пароль

panel/application/views/mail/account/register.php отправляет новому пользователю его пароль в открытом виде в письме. Так задумано в оригинале; мы не меняли, чтобы не ломать сценарий регистрации и активации. Учтите, что письмо остаётся в почтовом ящике и на промежуточных серверах.


Итоговая оценка

Что Статус
Работающие закладки в коде панели не найдены
Вебшелл в родословной сборки найден обезвреженным, оставлен как свидетельство
Обфускация, утечка данных наружу не найдены
Обход админ-авторизации не найден
SQL-инъекции без авторизации (2 шт.) исправлены
Пароли в открытом виде в базе исправлено
XSS в форме регистрации исправлено
MD5 без соли остаётся, требует отдельной работы
Установка кода по HTTP на ноды источник убран из установщика
Root-пароль для доступа к нодам особенность устройства, закрывается файрволом
Пароль в письме о регистрации остаётся

Для коммерческого хостинга с реальными клиентами и платежами эту панель использовать не стоит — и из-за правового статуса (см. README), и из-за MD5-паролей, и потому что происхождение кода не заслуживает доверия. Для изучения, внутренних задач и аудита — пригодна.


Поддерживается и развивается с помощью REDL.IO — Хостинг с искусственным интеллектом.