Сборка 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:
+241
@@ -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) — Хостинг с искусственным интеллектом.**
|
||||
Reference in New Issue
Block a user