Le mot de passe de l'administrateur du site web du datacentre FICloud a fuité...
Ayant mis la main sur les logs du site web, vous devez retrouver le mot de passe et comment celui-ci a été obtenu par l'attaquant.
La société FICloud, spécialisée dans les solutions cloud innovantes, a récemment été la victime d'une cyberattaque.
L'équipe informatique a découvert que le compte administrateur de la machine sur laquelle réside le site web de FICloud a été compromis.
Une copie des derniers logs web a été récupérée avant que l'attaquant ne couvre ses traces.
Saurez-vous retrouver sa trace?
En feuilletant les journaux, on remarque rapidement des mots-clés SQL dans
certaines requêtes, par exemple :
... "get_data":{"id":"1' AND IF(SUBSTRING((SELECT password FROM users WHERE user='admin'),1,1)='a', SLEEP(3), 0)-- -","Submit":"Submit"} ...
Cette requête attend trois secondes avant de retourner un résultat uniquement
si le mot de passe du compte admin commence par un a.
Il s'agit d'une injection SQL à l'aveugle, basée sur le temps.
On remarque que les lignes effectuant l'attaque partagent un même identifiant
de session.
On peut alors filtrer uniquement les lignes causées par l'attaquant.
On peut raisonnablement supposer que l'attaquant passe au caractère suivant
dès qu’il trouve le bon par brute-force.
En utilisant cette supposition, on peut tenter de retrouver le mot de passe
récupéré par l'attaquant :
from json import loads
from urllib.parse import parse_qs
import re
password = ''
last_number = ''
char = ''
for line in open('files/webserver.log'):
data = loads(line)
# On filtre par PHPSESSIONID de l'attaquant
if '5164ef2b1f031fc8142f641445f9d28d' not in data['headers']['cookie']:
continue
query_string = parse_qs(data['query_string'])
if 'id' not in query_string:
continue
query = query_string['id'][0]
# On récupère la position/la lettre testée depuis la requête
pattern = r"admin'\),(\d+),\d+\)='(.)'"
match = re.search(pattern, query)
if match is None:
continue
# Si l'attaquant a changé de caractère, c'est que la tentative précédente
# était correcte.
number = match.group(1)
if number != last_number:
password += char
last_number = number
char = match.group(2)
print(number, char)
password += char
print(password) # Cela affiche le MD5 du mot de passe
Cela donne un condensat MD5.
Le mot de passe étant faible, on peut aisément retrouver le mot de passe
correctement en recherchant le condensat sur CrackStation.