Outils d'utilisateurs

Outils du Site


ndhquals2015:updator

Updator

É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 :

  <code python>
  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'
  </code>

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<TV.d.5J8|
  000000f0  10 64 25 65 4a 30 37 6e  55 62 45 4d 54 75 78 73  |.d%eJ07nUbEMTuxs|
  00000100  43 4e 6c 5d 4d 77 2e 61  06 66 5b 56 4e 31 28 37  |CNl]Mw.a.f[VN1(7|
  00000110  43 61 75 15 1f 36 2e 6c  06 4e 47 59 0a 75 7c 7a  |Cau..6.l.NGY.u|z|
  00000120  44 7c 6a 58 55 33 3b 7d  17 53 43 51 41 3f 69 61  |D|jXU3;}.SCQA?ia|
  00000130  45 69 7a 1d 19 34 63 32  03 53 49 0a 1f 79 37 25  |Eiz..4c2.SI..y7%|
  00000140  1c 21 03 5e 5e 6b 21 66                           |.!.^^k!f|
  00000148

XOR étant un algorithme symétrique, on va XOR les premier octet du fichier avec le mot clé 'import s' en supposant que le début du fichier est 'import sys':

  >>> 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.

ndhquals2015/updator.txt · Dernière modification: 2017/04/09 15:33 (modification externe)