====== Updator ====== Exploit 200 points http://quals.nuitduhack.com/challenges/view/9 [[http://updator.challs.nuitduhack.com/]] ===== Énoncé ===== //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?// ===== challenge ===== 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.// ===== robots.txt ===== 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 ? ===== pyc ===== 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\n\n \n \n Updator - Update system\n \n \n \n \n
\n The update managing system is still under construction but will be available soon.\n
\n \n\n'
===== the private key ===== 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 '. On commence par faire un hexdump du fichier log.py.encrypted. [maggick@rootine updator]$ hexdump -C log.py.encrypted 00000000 5f 36 30 0b 03 56 06 17 08 19 15 1b 1b 19 45 6e |_60..V........En| 00000010 34 0e 1a 38 35 7f 2a 4f 22 68 7a 7b 28 32 6b 4f |4..85.*O"hz{(2kO| 00000020 33 39 30 7c 35 71 3e 4f 25 2e 1f 7f 23 36 6a 14 |390|5q>O%...#6j.| 00000030 3a 4f 55 11 72 34 6c 47 4c 67 61 14 77 7f 29 59 |:OU.r4lGLga.w.)Y| 00000040 1f 48 49 1f 62 57 2f 0a 09 33 6d 1e 75 55 65 16 |.HI.bW/..3m.uUe.| 00000050 58 40 58 09 61 05 2d 04 03 7e 34 4d 60 46 78 04 |X@X.a.-..~4M`Fx.| 00000060 42 4a 06 4d 38 5e 54 57 54 31 66 09 69 5c 52 46 |BJ.M8^TWT1f.i\RF| 00000070 03 19 43 03 79 13 11 15 08 62 24 42 7b 1e 12 15 |..C.y....b$B{...| 00000080 61 1c 17 01 2a 19 14 4e 1b 08 10 3a 1f 72 60 11 |a...*..N...:.r`.| 00000090 0d 20 24 2c 46 34 27 16 5e 0f 0d 25 52 38 65 04 |. $,F4'.^..%R8e.| 000000a0 1c 31 63 35 4c 7e 22 0e 02 43 0a 7d 1d 29 77 18 |.1c5L~"..C.}.)w.| 000000b0 46 76 2b 74 09 22 5b 4b 50 0d 4b 30 58 20 5d 4f |Fv+t."[KP.K0X ]O| 000000c0 0f 22 63 74 46 72 1e 52 11 1b 42 63 52 3e 59 4c |."ctFr.R..BcR>YL| 000000d0 42 6c 22 20 42 37 58 16 54 56 11 64 55 71 44 6f |Bl" B7X.TV.dUqDo| 000000e0 42 2d 71 20 04 73 42 3c 54 56 11 64 13 35 4a 38 |B-q .sB>> chr(0x5f^ord('i')) '6' >>> chr(0x36^ord('m')) '[' >>> chr(0x30^ord('p')) '@' >>> chr(0x0b^ord('o')) 'd' >>> chr(0x03^ord('r')) 'q' >>> chr(0x56^ord('t')) '"' >>> chr(0x06^ord(' ')) '&' >>> chr(0x17^ord('s')) 'd' On déchiffre le fichier avec la clé **6[@dq"&d**, on obtient la première ligne suivante: import satetime Il semble que la première ligne était plutôt import **datetime** >>> chr(0x17^ord('d')) 's' On déchiffre le fichier avec la clé **6[@dq"&s**, 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.