Сборка REDL: панель на PHP 8, капча-тумблер, закрытые уязвимости, установщики в один клик

- панель работает на 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
- документация на русском: ИЗМЕНЕНИЯ, БЕЗОПАСНОСТЬ, ИНСТРУКЦИЯ
This commit is contained in:
2026-07-30 02:00:01 +00:00
commit 6ee2744e3a
492 changed files with 354172 additions and 0 deletions
+241
View File
@@ -0,0 +1,241 @@
# Аудит безопасности: что нашли и что исправили
Аудит исходников сборки HostinPL 5.6 («nulled»-копия). Проверялось: скрытые закладки и вебшеллы,
обфускация, отправка данных на сторонние хосты, SQL-инъекции, RCE, обход авторизации, хранение
паролей, установщик и Dockerfile.
**Поддерживается и развивается с помощью [REDL.IO](https://redl.io) — Хостинг с искусственным интеллектом.**
---
## Главное в двух словах
Форк заявлял: «в панели вырезаны все шеллы/дыры». Проверка показала — **заявление верно лишь
частично**. Явных работающих закладок мы не нашли, обфускации в коде панели нет. Но нашлись:
* **доказательство, что в этой родословной вебшеллы действительно были** — один найден обезвреженным;
* **две SQL-инъекции, доступные без авторизации** — про них в описании форка ничего не сказано;
* **пароли пользователей писались в базу в открытом виде**;
* установка кода на игровые ноды **по обычному HTTP без проверки подписи**.
Первые три пункта мы исправили. Ниже — детали.
---
## 1. Вебшелл в родословной сборки — обезврежен
**Файл:** `panel/application/public/js/proxy/proxy.php` — 7 строк целиком:
```php
<?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`.
**Вывод:** любая другая копия этой панели, взятая не из этого репозитория, может содержать
работающий шелл. Перед установкой чужой сборки проверяйте хотя бы так:
```bash
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 напрямую):
```php
$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`:
```php
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()` **реальный пароль** из формы:
```php
$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`. Меры,
которые снижают риск:
```bash
# на ноде: 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](https://redl.io) — Хостинг с искусственным интеллектом.**