// HackTheBox

Curling

über secret.txt geleakte Joomla-Credentials → Template-Edit-RCE → curl -K Konfigurations-Injection über einen root-Cron-Job

HackTheBoxLeichtLinux 21. Juli 2026 4 Min. Lesezeit

Aufklärung

Zwei Ports, und nur einer davon ist eine echte Angriffsfläche:

Port Dienst
22 SSH
80 Apache + Joomla

SSH gibt ohne Credentials nichts her, also ist die Joomla-Seite auf Port 80 die ganze Box. Joomla ist ein PHP-CMS, dessen Administrator-Panel Template-Quellcode direkt bearbeiten kann — wenn wir uns also als Admin einloggen können, ist dieses Panel ein direkter Weg zur Codeausführung. Die erste Aufgabe ist daher, ein Admin-Passwort zu finden.

Erstzugang

1. Geleaktes Passwort in secret.txt

Der Quelltext der Startseite enthält einen HTML-Kommentar, den ein normaler Besucher nie sieht, der aber im Markup ausgeliefert wird:

<!-- secret.txt -->

Der Aufruf von /secret.txt liefert einen einzelnen Base64-String, Q3VybGluZzIwMTgh. Base64 ist trivial umkehrbar, also dekodieren wir ihn:

$ echo Q3VybGluZzIwMTgh | base64 -d
Curling2018!

Die Seite schreibt ihre Artikel dem Autor floris zu, was uns einen Benutzernamen zum wiedergewonnenen Passwort Curling2018! liefert.

2. Admin-Login → Template-Edit-RCE

floris:Curling2018! loggt direkt in das Joomla-Administrator-Panel ein. Joomlas Template-Manager erlaubt einem Super User, die PHP-Dateien des aktiven Templates im Browser zu bearbeiten — der klassische Joomla-zu-RCE-Trick ohne CVE: PHP in eine Template-Datei schreiben und diese Datei dann über das Web aufrufen.

Bearbeite die error.php des Protostar-Templates und injiziere eine einzeilige Kommando-Shell:

<?php system($_REQUEST['c']); ?>

Das Speichern wird vom template.save-Task der com_templates-Komponente verarbeitet, der neben dem bearbeiteten Quellcode die Felder jform[extension_id] und jform[filename] sowie ein gültiges CSRF-Token benötigt — Joomla verweigert das Schreiben stillschweigend, wenn eines davon fehlt.

Die platzierte Datei führt nun jeden Befehl aus, der im Parameter c übergeben wird. Der Aufruf von /templates/protostar/error.php?c=id führt id aus, und der Code läuft als www-data, dem Apache/PHP-Benutzer.

3. password_backup → floris’ SSH-Passwort

Im Home-Verzeichnis von floris liegt eine Datei namens password_backup. Ihr Inhalt ist ein Text-Hexdump — die druckbare Ausgabe von xxd — statt reiner Binärdaten, der erste Schritt ist also, den Hexdump mit xxd -r wieder in Bytes umzukehren. file auf dem Ergebnis zeigt, dass es bzip2-komprimiert ist, und jede Schicht darunter entpuppt sich als weiterer Kompressor: die Verschachtelung ist bzip2 → gzip → bzip2 → tar. Schäle sie eine nach der anderen mit dem passenden Werkzeug ab und entpacke dann das Archiv:

$ xxd -r password_backup > stage1.bz2
$ bunzip2 -c stage1.bz2 > stage2.gz
$ gunzip -c stage2.gz > stage3.bz2
$ bunzip2 -c stage3.bz2 > stage4.tar
$ tar xf stage4.tar
$ cat password.txt
5d<wdCbdZu)|hChXll

5d<wdCbdZu)|hChXll ist floris’ SSH-Passwort, also verwandelt ssh floris@... die www-data-Webshell in eine echte interaktive Sitzung als floris und liefert user.txt.

Rechteausweitung — curl -K Konfigurations-Injection über root-Cron

Beim Durchsehen von floris’ Dateien taucht ~/admin-area/input auf, gehört root:floris mit dem Modus rw-rw---- — für floris schreibbar. Es ist keine Datendatei, sondern eine curl-Konfigurationsdatei (das Format, das curl mit seinem -K-Flag liest), und ein root-Cron-Job führt in kurzen Abständen curl -K input -o report aus und schreibt das Ergebnis nach report (das floris lesen kann). Die Standardkonfiguration ruft einfach nur den lokalen Webserver ab:

url = "http://127.0.0.1"

Da curl-Konfigurationsdirektiven url umfassen und curls file://-Schema lokale Dateien liest, können wir diesen Abruf auf eine beliebige Stelle im Dateisystem richten. curl läuft über den Cron als root, kann also root-eigene Dateien lesen. Überschreibe die URL der Konfiguration:

url = "file:///root/root.txt"

Beim nächsten Cron-Tick liest roots curl /root/root.txt und schreibt es nach report, das floris dann lesen kann:

$ echo 'url = "file:///root/root.txt"' > ~/admin-area/input
$ cat ~/admin-area/report

Timing. Eine Anti-Tamper-Routine der Box setzt input alle ~10-15 Sekunden auf den Standard zurück, und der curl-Cron feuert innerhalb desselben Fensters — schreibe input und lies report also innerhalb weniger Sekunden voneinander, bevor der Reset die injizierte URL überschreibt.

Der Ausgabepfad -o report ist im Cron-Befehl fest einkodiert, das Primitive ist also streng nur lesend: wir können root beliebige Dateien in report einlesen lassen, aber wir können diese Ausgabe nicht umleiten, um etwas zu überschreiben (z. B. eine authorized_keys oder ein SUID-Binary) für eine vollständige Shell. root.txt wird direkt erfasst, und da root keinen id_rsa zum Stehlen hat, ist dieses Lese-Primitive genau genug, um die Box abzuschließen.

← Alle Writeups