Ceci est une ancienne révision du document !
Exploit 200 points http://quals.nuitduhack.com/challenges/view/9 http://updator.challs.nuitduhack.com/
Unhackable : “Not hackable; that cannot be hacked or broken into.” We manage updates and thus have fixes, this is not a PS3 as it is unhackable … or is it?
On arrive sur une page avec une interface de login dont on ne possède aucun credentials et un bouton Update. Lorsque l'on clique sur ce bouton, la page http://updator.challs.nuitduhack.com/update.py nous affiche le message suivant : The update managing system is still under construction but will be available soon.
Le fichier robots.txt révèle la présence du dossier temp/ :
Disallow: temp/*
Dans se dossier on trouve un fichier log.py.encrypted. Comment peut on le déchiffrer ?
Le bouton Update appel un fichier update.py. On se dit que peut être il y a une fichier .pyc, la page http://updator.challs.nuitduhack.com/update.pyc permet effectivement de télécharger un fichier python compilé. On décompile le fichier avec https://github.com/wibiti/uncompyle2 :
import config import sys KEY = config.KEY def xor(*args): if len(args) < 2: sys.exit(0) length = len(args[0]) for arg in args: if len(arg) != length: sys.exit(0) length = len(arg) cipher = args[0] for arg in args[1:]: cipher = ''.join([ chr(ord(arg[i]) ^ ord(cipher[i])) for i in range(len(arg)) ]) return cipher class Crypto: @staticmethod def encrypt(file): with open(file, 'r') as fd: content = fd.read() content = content.ljust(len(content) + (8 - len(content) % 8), '0') blocks = [ content[i * 8:(i + 1) * 8] for i in range(len(content) / 8) ] with open('%s.encrypted' % file, 'w') as fd: encrypted = [] for i in range(len(blocks)): if i == 0: encrypted.append(xor(KEY, blocks[i])) else: encrypted.append(xor(KEY, blocks[i], encrypted[i - 1])) fd.write(''.join(encrypted)) @staticmethod def decrypt(file): with open(file, 'r') as fd: content = fd.read() blocks = [ content[i * 8:(i + 1) * 8] for i in range(len(content) / 8) ] with open('.'.join(file.split('.')[:-1]), 'w') as fd: plain = [] for i in range(len(blocks)): if i == 0: plain.append(xor(KEY, blocks[i])) else: plain.append(xor(KEY, blocks[i], blocks[i - 1])) fd.write(''.join(plain).rstrip('0')) print 'Content-Type: text/html' print '\n<!DOCTYPE html>\n<html>\n <head>\n <meta charset="UTF-8">\n <title>Updator - Update system</title>\n <link rel="stylesheet" href="static/font-awesome/css/font-awesome.css">\n <link rel="stylesheet" href="static/css/style.css">\n </head>\n <body>\n <div id="info">\n The update managing system is still under construction but will be available soon.\n </div>\n </body>\n</html>\n'
On connait maintenant l'algorithme utilisé pour chiffrer le fichier log.py.encrypted : du chiffrement XOR par bloc de 8 caractères. Il ne nous manque que la clé. Bruteforcer 8 caractères nous prendrais trop de temps, il nous faut une autre méthode. Le fichier étant du python il est fort probablement que la première ligne commence par 'import '. Regardons ce que ça donne :
TODO
On déchiffre le fichier avec la clé, on obtient :
import datetime LOG_DIR = 'logs' class Logger(): @staticmethod def log(username, password): basename = '%s/%s_%s' % (LOG_DIR, str(datetime.date.today()), username) with open(basename, 'a+') as fd: fd.write('[%s] Login with password %s\n' % (str(datetime.datetime.today()), password))
On voit que les logs de l'application sont dans un dossier de type date_admin avec la date au format YYYY-MM-DD
On se rend donc à l'url http://updator.challs.nuitduhack.com/logs/2015-04-04_admin :
[2015-04-04 18:49:48.839448] Login with password Mpt2P4sse2Ouf [2015-04-04 18:49:54.044382] Login with password Mot2P4sse2Ouf
On se log sur l'interface avec avec le mot de passe des logs et on obtient le flag.