Post

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:

nmap services light nmap services dark

The webserver is trying to redirect to lookup.thm, so lets add this fqdn to our /etc/hosts file.

Website

login page light login page dark

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!

Username enumeration light Username enumeration dark

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:

ELFinder site light ELFinder site dark

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.

Archive to tar light Archive to tar dark

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

Meterpreter shell light Meterpreter shell dark

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.

PWM run light PWM run dark

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:

Content of .password light Content of .password dark

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

Download wordlist light Download wordlist dark

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

Crack think light Crack think dark

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:

Root private key light Root private key dark

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! :)


dark mode only

All rights reserved by Fatos Shala.