lundi 28 juin 2010

level3 wargame NDH2010 - crypto substitution de caracteres

Cet article décrit la résolution du wargame level 3 de la nuit du hack 2010. L'épreuve consiste en la recherche d'un mot de passe généré par une substitution des caractères entrés par l'utilisateur.


solution

level3@srv-public:~$ python /tmp/lvl3.py
mot de passe
You entered: YOU>are\A^tRuE[ 33 z
Ciphered: 2:8vytm&*9|)].l(ol;a
We want: 2:8vytm&*9|)].l(ol;a
Good Job!
YOU>are\A^tRuE[ 33 z
[89, 79, 85, 62, 97, 114, 101, 92, 65, 94, 116, 82, 117, 69, 91, 17, 51, 51, 23, 122]

résolution

Commencons par lister le répertoire
level3@srv-public:~$ ls -all
total 32
dr-xr-x---  2 level3 level3 4096 24 juin  13:24 .
drwxr-x--x 24 root   root   4096 18 juin  18:19 ..
lrwxrwxrwx  1 root   root      9 28 mai   16:09 .bash_history -> /dev/null
-rw-r--r--  1 root   root    220 12 mai    2008 .bash_logout
-rw-r--r--  1 root   root   3116 12 mai    2008 .bashrc
---s--x---  1 level4 level3 5966 16 juin  09:55 crackme
-r--r-----  1 level3 level3    7  1 janv.  2008 passwd
-rw-r--r--  1 root   root    675 12 mai    2008 .profile
le crackme ne peut pas être lu, seulement exécuté. Un petit essai pour voir.
level3@srv-public:~$ ./crackme 00000000000000000000
You entered: 00000000000000000000
Ciphered: YE]|(68JYW8K8[!'ni>+
We want: 2:8vytm&*9|)].l(ol;a
It's incorrect n00b!
Utilisons la méthode de cryptanalyse différentielle sur les caractères du clair
You entered: 01111111111111111111
Ciphered: YD\{)79KZV9J9Z"(mj=,
Nous remarquons que le premier caractère "Y" du chiffré est conservé. Voyons si ca marche encore pour le second:
You entered: 00111111111111111111
Ciphered: YE\{)79KZV9J9Z"(mj=,
Un dernier essai avec le 3ème
You entered: 00011111111111111111
Ciphered: YE]{)79KZV9J9Z"(mj=,
Visiblement nous avons affaire à un chiffrement de César ou de Vigenere, c'est à dire une substitution pour chaque caractère. Vérifions si nous pouvons lancer un script python:
level3@srv-public:~$ python
Python 2.5.5 (r255:77872, Apr 21 2010, 08:44:16)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

Et vérifions si le répertoire /tmp peut être utilisé:
level3@srv-public:~$ echo "print 'coucou'" > /tmp/essai
level3@srv-public:~$ python /tmp/essai
coucou


Nous allons donc créer un script python qui bruteforce chaque caractère en utilisant la sortie du programme comme oracle. Nous testerons notre script python sur un programme en local:
$ cat essai.sh
#!/bin/bash
echo "You entered: $1"
echo "Ciphered: $1"
echo "We want: 2:8vytm&*9|)].l(ol;a"
echo "It's incorrect n00b!"
Voici notre script
#!/bin/python
# solution level3 wargame NDH 2010 par t0ka7a
import string, os

clair = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
cipher = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
solution = [ord('2'),ord(':'),ord('8'),ord('v'),ord('y'),ord('t'),ord('m'),ord('&'),ord('*'),ord('9'),ord('|'),ord(')'),ord(']'),ord('.'),ord('l'),ord('('),ord('o'),ord('l'),ord(';'),ord('a')]
cipher_string = ''

# pour chaque caractere de la solution
for i in range(20):
  # pour chaque entier de la table ASCII sauf 0
  for j in range(1,256):
    # exclure LF et '
    if j==10: j+=1
    if j==39: j+=1
    clair[i] = j
    # construction de l'entree de la commande
    cmd = "./essai.sh '"
    # pour chaque caractere du clair
    for k in range(20):
      cmd += chr(clair[k])
    cmd += "'"
    out = os.popen(cmd).readlines()
    # pour reperer quand la sortie de la commande change
    if out[0].split(' ')[0] == "You":
      # cipher_string = 2eme mot de la 2eme ligne de la sortie de la commande
      cipher_string = out[1].split(' ')[1]
      # si le caractere cipher est un retour chariot, la longueur de la chaine sera plus petite
      if len(cipher_string) == 21:
        for k in range(20):
          cipher[k] = ord(cipher_string[k])
        if cipher[i] == solution [i]:
          break
    else:
      for line in out:
        print line
      break
  # affichage du clair pour faire joli
  sortie = ''
  for k in range(20):
    sortie += chr(clair[k])
  print sortie

Sans oublier de remplacer essai.sh par crackme, nous le placons dans un fichier dans le répertoire /tmp
$ vim /tmp/lvl3.py
i
SHIFT-INSERT
ESC
:wq

2 commentaires:

  1. Merci pour les solutions !
    Ça permet au noob que je suis de découvrir des trucs !

    Simple petit truc : c'est moche d'utiliser python pour faire ça : solution = [ord('2'),ord(':'),ord('8'),ord('v'),ord('y'),ord('t'),ord('m'),ord('&'),ord('*'),ord('9'),ord('|'),ord(')'),ord(']'),ord('.'),ord('l'),ord('('),ord('o'),ord('l'),ord(';'),ord('a')]

    => solution = map(ord, "2:8vytm&*9|)].l(ol;a")

    :-)

    RépondreSupprimer
  2. Beau boulot pour le brute force.

    Y'avait d'autres manière de résoudre dont celle de guessing ;).

    On remarque après quelques manipulations que ça ressemble à une crypto symmétrique.

    Donc on entre le "hash" attendu :
    "2:8vytm&*9|)].l(ol;a"
    On fini avec une partie de la solution.
    Certains caractères sont à deviner ou bruteforce ;).
    J'ai fini par devoir bruteforce 2 caractères :].

    RépondreSupprimer