- панель работает на PHP 8.4 (оригинал под PHP 7.0): elFinder utf8_encode, apache_get_modules, warnings - капча выключена и управляется из админки (флаг captcha_enable + блок настроек) - закрыты две SQL-инъекции без авторизации (createAuthLog: пароль из POST и CF-Connecting-IP) - убрано хранение паролей в открытом виде в authlog, XSS в поле ref - установщики install-panel.sh и install-node.sh (оригинальный затирал sources.list репозиториями Debian 9) - современный Dockerfile (оригинальный на debian:stretch больше не собирается) - планировщик и автозапуск работают без systemd - брендинг REDL.IO, год 2026, ссылки на redl.io - документация на русском: ИЗМЕНЕНИЯ, БЕЗОПАСНОСТЬ, ИНСТРУКЦИЯ
16 KiB
Аудит безопасности: что нашли и что исправили
Аудит исходников сборки 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даёт 54execи 8system, но все они внутри вендорных библиотек: в phpseclibexec()— это метод 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:114 — md5($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 — Хостинг с искусственным интеллектом.