Affichage des articles dont le libellé est python. Afficher tous les articles
Affichage des articles dont le libellé est python. Afficher tous les articles

lundi 11 mars 2013

Prequals NDH 2k13 - Crackme Huge.js

Voici le write-up d'une d'une autre épreuve des prequals de la NDH 2k13.

Description

On nous donne un fichier .js mais assez "huge". Evidemment ca rame à mort si on l'ouvre avec notepad, etc ...

Analyse du script

Donc on l'ouvre avec notre éditeur héxa préféré ou même Chrome et là on découvre :

function d() {
  return (window.console &&
         (window.console.firebug || window.console.exception));
}

function x(c, k) {
  o = '';
  for (i = 0; i < c.length; i++) {
    o += String.fromCharCode(c.charCodeAt(i) ^ k.charCodeAt(i % k.length));
  }
}

On a donc une fonction d(debug) qui check si on debug le script et une fonction x(or) qui déchiffre une string xorée. Dans la suite du code, les strings sont évidemment xorées avec cette fonction.

window[x('\x44\x29\x42\x42','\x21\x5f\x23\x2e\x2b\x28\x3f\x2d')](x('\x42\x4b\x17\x0f\x5b\x01\x08\x02\x56\x48\x47\x51\x4d...));



Déchiffrement

Il suffit donc d'écrire un script qui va remplacer chaque appelle à la fonction "x" par son résultat. A la fin on aura notre fichier Javascript en clair et avec une taille correct !

def xor(c, k):
  return "".join([chr(ord(c[i])^ord(k[i%len(k)])) for i in range(len(c))])


def decode(f):
  data = open("huge.js", "r").read()
  pos = data.find("x('\\", 0)
  while pos != -1:
    sep = data.find(",", pos)
    end = data.find(")", pos)
    c = data[pos+3:sep-1].replace("\\x", "").decode("hex")
    k = data[sep+2:end-1].replace("\\x", "").decode("hex")
    data = data[:pos] + xor(c, k) + data[end+1:]
    pos = data.find("x('\\", 0)
  f = open("result.js", "w")
  f.write(data)
  f.close()

decode("huge.js")

Et oui, on parse à l'ancienne avec des "pos" et des "substrings" !

On obtient une fonction "xxx" qui est en fait un md5, "kkk" qui reverse une string et une fonction "unlock" pour tester le mot de passe :

function unlock(node){
  var code = node.value;
  if (code && (code.length == 5)){
    if ((xxx(code).substr(0,16)=='b3336efd42e29780') &&
        (xxx(kkk(code)).substr(16,16)=='261804c5f2f0a47e')){
      var flag = document.createElement('span');
      var t = document.createTextNode('YAY, you got the flag ! --> '+xxx(code));
      flag.appendChild(t);
      flag.style.font='Helvetica, 1.2em, bold';
      document.getElementById('panel').appendChild(flag);
      node.style.display = 'none';
      node.value = '';
    }
  }
}

Il suffit donc de trouver un pass de 5 caractères dont le début du md5 est "b3336efd42e29780". Bf :

import sys


charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789<>(){}-+"

h = "b3336efd42e29780".decode("hex")


def brute(n, s):
  if n>0:
    for c in charset:
      brute(n-1, s+c)
  else:
    if hashlib.md5(s).digest()[:8] == h:
      print s
      sys.exit(0)

brute(5, "")

Au bout de quelques temps on obtient : i<3Js

Et voilà :)

Prequals NDH 2k13 – Fappers Gonna Fap – Crypto 200 pts

Voici le write-up d'une des épreuves de crypto des prequals de la NDH 2k13. Au menu, du XOR, du format BMP et du Python ! 

Description

On nous fournit le fichier dafuq.bmp et un script python encrypt.py.
L'épreuve va consister à décoder l'image Bitmap qui a apparemment été chiffrée en utilisant le script python.

Analyse du script

On va regarder d'un peu plus près encrypt.py histoire de voir si on peut le reverse pour récupérer l'image d'origine.
Une classe BitmapFile est définie dans laquelle on trouve une fonction très intéressante :


On remarque que les données binaires de l'image sont xorées avec une clé de longueur 337 dont les valeurs des octets sont pris aléatoirement entre 0 et 255. L'algorithme n'est donc pas réversible mais il doit être possible de récupérer la clé en utilisant les caractéristiques du format BMP.

Récupération de la clé

La structure d'une image Bitmap est composée de plusieurs en-têtes et options en plus des données de l'image. On trouve notamment le Bitmap File Header et le DIB Header qui vont permettre de stocker des informations sur l'image.

On va alors pouvoir recomposer le File Header de l'image d'origine en utilisant notamment le fait que plusieurs champs sont communs à toutes les images BMP. Ensuite comme il s'agit d'un simple XOR il suffira de xorer les octets trouvés avec ceux de dafuq.bmp pour avoir le début de la clé (le File Header fait 14 octets).

Par exemple, on sait déjà que l'en-tête va commencer par les octets 42 4D qui sont en fait les caractères B et M. Le champ suivant qui occupe 4 octets contient la taille du fichier qui sera donc la même que dafuq.bmp c'est-à-dire 2993058 octets soit 2DABA2 en héxadécimal. Par ailleurs les données étant stockées en Little Endian on obtient A2AB2D00 pour ces 4 octets du File Header.
Les 4 octets suivants sont réservés et généralement à 0 et les 4 d'après représentent l'adresse de départ des données de l'image qui commencent habituellement après le File Header (14 octets) et le DIB Header (40 octets). On obtient donc la valeur 54 soit 36 en héxadécimal stockées sous la forme 36000000.

On arrive ensuite sur le premier champ du DIB Header qui est en fait invariable puisqu'il s'agit de la taille du Header soit 40 octets. On a donc la valeur héxadécimale 28 stockées sur 4 octets : 28000000.

Pour le moment, nous avons reconstruit les 18 premiers octets de l'image :
424DA2AB2D00000000003600000028000000

Nous pouvons donc d'ores et déjà les xorer avec les 18 premiers octets de l'image chiffrée pour obtenir le début de la clé.

Après avoir xoré tout ça, on obtient le début de clé suivant :
C31AD80D5C84E09149D9E19A79164A18338B
A ce stade, on sait que ça ne sert à rien de continuer à reconstruire les en-têtes car la clé est censé faire 337 octets ce qui est largement supérieur à la taille des en-têtes (54 octets).

Il va donc falloir utiliser le début de la clé pour en déduire le reste.

On peut légitimement supposer que les données de l'image d'origine contiennent par endroit des séquences de 0 qui une fois xorées avec la clé donneront des parties de la clé dans l'image chiffrée.

On va donc rechercher dans les données de l'image chiffrée toutes les occurrences de la séquence C31AD80D5C84E09149D9E19A79164A18338B.

On trouve de nombreuses occurrences de cette séquence dont 2 sont montrées au-dessus.
On constate qu'à chaque fois ces séquences sont séparées de 319 octets identiques ce qui nous donne des blocks de 337 octets soit la longueur de la clé utilisé lors du XOR.

On dirait bien qu'on vient juste de récupérer notre fameux « keystream ».

Décodage

On a tout ce qu'il faut pour décoder le fichier dafuq.bmp et obtenir l'image de départ.
On fait un petit script python qui va xorer les données binaires de dafuq.bmp avec la clé de 337 octets ci-dessous :

C31AD80D5C84E09149D9E19A79164A18338B1E78446DB8B8D25431071C320DE6231EBB0E882351217666AAB9209DD00AD142B10F45DE0AA522B3459A14523A5F01DC5ECC67F62584FB7064B7832AC9FC43CA8A299189F5818B8BE7970034600C3317A44883F7CB77060460FFE8048DF4CBF6AA60DCB73229BD0444504715614F43CE360B852B846D43B606B69F257672E02DAF7BC40554B5121A8A6D9AB933F28F8A9D95858CA7AFE947D63C979445B645DE920E9A7EE5F6F065AB7529E6DE1EDE9DFFA50F32632E4C9EE30E088238C106E385EA6C8B2296D7F485A171E7A8EE163B24E6BDCEF979490BF1839EA5773275D83D8583AFF2E6A5279A7DB36CBD1914B91389820E6576B84E832FFEC4184846E59980096FEBE28ED66FB08637D40530DCBE3AC481F38EEB04E00FB4F60F13B047343BC230F200C2419F4271E2584508BE3B56164F522060DA23D0DDCD97055C

On obtient alors l'image suivante :


On valide l'épreuve avec le flag : ecb_mode_is_weak_mofo

Liens

http://en.wikipedia.org/wiki/BMP_file_format

dimanche 1 juillet 2012

NDH 2k12 – Workerz (1500 pt)


  
La « nuit du hack » édition 2012 a eu lieu la semaine dernière et comme d’habitude il y a eu un challenge public … ah non comme il ne marchait pas il a été reporté à ce week-end ...
C’est donc parti pour une série de write-ups sur ces challenges. On commence par « Workerz » dans la catégorie WebApp à 1500 pt.

1.       What

On atterrit sur une jolie animation en JavaScript avec un champ pour un mot de passe. Dans le source on trouve une série de .js dont ceux-ci :
1.       Main.js qui lance une App
2.       App.js qui initialise l’animation ainsi que 3 checks !
Il n’y a pas besoin de d’aller voir ce qu’il se passe dans la classes Kode ou Worker, on peut directement aller voir les checkX.js

var App = function () {
   
var check1 = new Worker('js/check1.js'),
        check2
= new Worker('js/check2.js'),
        check3
= new Worker('js/check3.js');
    this.
music = new buzz.sound('audio/robd_chateau', {
        formats
: [ "mp3" ]
   
});
    this.
k = new Kode(check1, check2, check3);
    this.
k.initialize();
    this.
initialize();
};

2.       Check1.js
On trouve une fonction qui sera appelée à chaque fois que le passe change et qui renvoi un booleén pour savoir si c’est bon ou pas.

;(function () {
   
var messageHandler = function (e) {
       
var c,
            p
= e.data;
        if
(((p.charCodeAt(0) * 42) - 38 ===
             (Math.pow(68, 2) / 4) + 0xC0 )                  &&
             
(p.charCodeAt(1) === p.charCodeAt(0) + 0x3A)    &&
             
((p.charCodeAt(2) + 0x10) === Math.sqrt(0x961)) &&
             
(Math.pow(p.charCodeAt(3), 3) / 25 ===
           
 (Math.pow(p.charCodeAt(2), 3) * 2) + 0x186B)    &&
             
(Math.sqrt(p.charCodeAt(4) - 2) ===
           
 (p.charCodeAt(2) * 0x61) - ((0xB00B5 / 3) -
           
 (Math.pow(p.charCodeAt(3), 2) * 6) - (0xBABE * 3)))) {
                 self.
postMessage('1');
       
} else {
             self.
postMessage('0');
       
}
   
};
    self.
addEventListener('message', messageHandler, false);
    postMessage
('Loaded');
})();

On remarque que les caractères utilisés du passe sont seulement le 0, 1, 2, 3, 4, donc le début uniquement. Nettoyons un peu tout ça :

((p.charCodeAt(0) * 42) - 38 === (Math.pow(68, 2) / 4) + 0xC0)
On peut simplifier ça en p.charCodeAt(0)=== 33


(p.charCodeAt(1) === p.charCodeAt(0) + 0x3A)
Cela devient : p.charCodeAt(1)=== 91

((p.charCodeAt(2) + 0x10) === Math.sqrt(0x961))
En : p.charCodeAt(2)=== 33

(Math.pow(p.charCodeAt(3), 3) / 25 ===
(Math.pow(p.charCodeAt(2), 3) * 2) + 0x186B)
Avec une petite racine cubique à la fin (^1/3), on obtient : p.charCodeAt(2)=== 125

(Math.sqrt(p.charCodeAt(4) - 2) === (p.charCodeAt(2) * 0x61) -
((0xB00B5 / 3) - (Math.pow(p.charCodeAt(3), 2) * 6) -
(0xBABE * 3))))
Du calcul encore, on obtient rapidement : p.charCodeAt(4) === 102

On a plus qu’à convertir ces codes ascii en caractères : ![!}f


3.       Check2.js
Maintenant on a du code Python qui sera exécuté dans sa VM (p.js). On peut extraire le code et jouer avec le vrai interpréteur Python.

;(function () {
    self.
console = {
        log
: function () {}
   
};
    self.prompt
= function () {};
    importScripts
('p.js');
    Python.
initialize(null, function(chr) {
        if
(chr !== null) {
            postMessage
(String.fromCharCode(chr));
       
}
   
});

   
var messageHandler = function (e) {
        code
= "def c(p = '" + e.data + "'):\n" +
           
"    if (ord(p[5]) == ((1337 % ord('=')) + 7)):\n" +
           
"        if (ord(p[6]) == (((42 * ord(p[5])) % 13) + 45)):\n" +
           
"            if (ord(p[7]) == ((0xc0ffee % ord(p[6])) + 57)):\n" +
           
"                if (ord(p[8]) == ((0xbadf00d & ord(p[7])) + ord(p[6]) + 50)):\n" +
           
"                    return('1')\n" +
           
"    return('0')\n" +
           
"import sys; sys.stdout.write(c())\n";
        Python.eval
(code);
   
};
    addEventListener
('message', messageHandler, true);
    postMessage
('Loaded');
})();

Voilà le code extrait :
def c(p = ''):
   
if (ord(p[5]) == ((1337 % ord('=')) + 7)):
       
if (ord(p[6]) == (((42 * ord(p[5])) % 13) + 45)):
           
if (ord(p[7]) == ((0xc0ffee % ord(p[6])) + 57)):
               
if (ord(p[8]) == ((0xbadf00d & ord(p[7])) +
                   
ord(p[6]) + 50)):
                   
return('1')
   
return('0')

On utilise la même méthode que plus haut ou  même un petit brute force vu qu’il n’y a que 4
caractères en jeu.
 On obtient ces 4 caractères : ?4;o

4.       Check3.js
Le dernier check est encore exécuté dans une vm LLVM codé en JavaScript qui répond au petit nom d’Emscripten (https://github.com/kripken/emscripten)
Dans le fichier on trouve uniquement un code qui alloue 17 octets, écrit le passe dedans et exécute la fonction _check3 définit dans check3_vm.js.
Voilà le code entier de la fonction _check3 :

function _check3($x) {
 
;
 
var __label__;
  __label__
= 2;
  while
(1) switch(__label__) {
    case
2:
     
var $1;
     
var $2;
     
var $p;
      $
2=$x;
      $p
=0;
     
var $3=_malloc(6);
      $p
=$3;
     
var $4=$2;
     
var $5=(($4+13)|0);
     
var $6=HEAP8[($5)];
     
var $7=$p;
     
var $8=(($7)|0);
      HEAP8
[($8)]=$6;
     
var $9=$2;
     
var $10=(($9+15)|0);
     
var $11=HEAP8[($10)];
     
var $12=$p;
     
var $13=(($12+1)|0);
      HEAP8
[($13)]=$11;
     
var $14=$2;
     
var $15=(($14+11)|0);
     
var $16=HEAP8[($15)];
     
var $17=$p;
     
var $18=(($17+2)|0);
      HEAP8
[($18)]=$16;
     
var $19=$2;
     
var $20=(($19+14)|0);
     
var $21=HEAP8[($20)];
     
var $22=$p;
     
var $23=(($22+3)|0);
      HEAP8
[($23)]=$21;
     
var $24=$2;
     
var $25=(($24+12)|0);
     
var $26=HEAP8[($25)];
     
var $27=$p;
     
var $28=(($27+4)|0);
      HEAP8
[($28)]=$26;
     
var $29=$2;
     
var $30=(($29+10)|0);
     
var $31=HEAP8[($30)];
     
var $32=$p;
     
var $33=(($32+5)|0);
      HEAP8
[($33)]=$31;
     
var $34=$p;
     
var $35=(($34+5)|0);
     
var $36=HEAP8[($35)];
     
var $37=(($36 << 24) >> 24);
     
var $38=_fmod($37, 42);
     
var $39=(($38)&-1);
     
var $40=(($39)|0)==7;
      if
($40) { __label__ = 3; break; } else { __label__ = 9; break; }
    case
3:
     
var $42=$p;
     
var $43=(($42+3)|0);
     
var $44=HEAP8[($43)];
     
var $45=(($44 << 24) >> 24);
     
var $46=(($45)|0)==42;
      if
($46) { __label__ = 4; break; } else { __label__ = 9; break; }
    case
4:
     
var $48=$p;
     
var $49=(($48)|0);
     
var $50=HEAP8[($49)];
     
var $51=(($50 << 24) >> 24);
     
var $52=((($51)+(3))|0);
     
var $53=(($52)|0);
     
var $54=_sqrt($53);
     
var $55=(($54)&-1);
     
var $56=$p;
     
var $57=(($56+4)|0);
     
var $58=HEAP8[($57)];
     
var $59=(($58 << 24) >> 24);
     
var $60=((($59)-(30))|0);
     
var $61=(($55)|0)==(($60)|0);
      if
($61) { __label__ = 5; break; } else { __label__ = 9; break; }
    case
5:
     
var $63=$p;
     
var $64=(($63+2)|0);
     
var $65=HEAP8[($64)];
     
var $66=(($65 << 24) >> 24);
     
var $67=_ldexp($66, 3);
     
var $68=(($67)&-1);
     
var $69=((($68)|0))%(3);
     
var $70=((($69)+(47))|0);
     
var $71=$p;
     
var $72=(($71+5)|0);
     
var $73=HEAP8[($72)];
     
var $74=(($73 << 24) >> 24);
     
var $75=(($70)|0)==(($74)|0);
      if
($75) { __label__ = 6; break; } else { __label__ = 9; break; }
    case
6:
     
var $77=$p;
     
var $78=(($77+4)|0);
     
var $79=HEAP8[($78)];
     
var $80=(($79 << 24) >> 24);
     
var $81=((($80)+(800))|0);
     
var $82=$p;
     
var $83=(($82+2)|0);
     
var $84=HEAP8[($83)];
     
var $85=(($84 << 24) >> 24);
     
var $86=((((($85)|0))/(2))&-1);
     
var $87=(($86)|0);
     
var $88=_llvm_pow_f64($87, 2);
     
var $89=(($88)&-1);
     
var $90=(($81)|0)==(($89)|0);
      if
($90) { __label__ = 7; break; } else { __label__ = 9; break; }
   
case 7:
     
var $92=$p;
     
var $93=(($92+1)|0);
     
var $94=HEAP8[($93)];
     
var $95=(($94 << 24) >> 24);
     
var $96=((($95)*(666))|0);
     
var $97=$96 >> 9;
     
var $98=(($97)|0)==57;
      if
($98) { __label__ = 8; break; } else { __label__ = 9; break; }
    case
8:
      $
1=1;
      __label__
= 10; break;
    case
9:
      $
1=0;
      __label__
= 10; break;
    case
10:
     
var $102=$1;
     
;
      return $
102;
   
default: assert(0, "bad label: " + __label__);
 
}
}

On là on se dit : WTF ?! Mais en fait ça n’est pas si compliqué que ça :

Au niveau structure, c’est un automate et on part de l’état 2. Ensuite on effectue le check de cet
état et si c’est bon, on passe au suivant. Si on tombe dans l’état 9 c’est foutu.
L’état 10 sert juste à renvoyer le résultat du check. En paramètre, on a $x qui est l’adresse où
est stocké notre mot de passe. Pour accéder à la valeur contenu à cette adresse,
on utilise HEAP8[ADRR].
Les opérations du style (($4+13)|0) ne font qu’ajouter (13) à $4, le |0 comme
le &-1 ne servent à rien dans ce cas. L’adresse du mot de passe est aussi copiée dans $2.
a.       Case 2
On crée un pointeur $p, on alloue 6 octet et on met l’adresse de ces octets dans $p.
Ensuite on trouvera beaucoup de suite d’instructions de ce style :

var $4=$2;
   
var $5=(($4+13)|0);
   
var $6=HEAP8[($5)];

Elle ne font que charger une adresse ($2) ajouter une valeur à cette adresse (13) puis récupère la valeur à cette adresse (HEAP8[($5)]). C’est équivalent à : pass[13] en C par exemple.
Celle valeur est mis ensuite dans le premier octet de $p :

var $7=$p;
   
var $8=(($7)|0);
   HEAP8
[($8)]=$6;
Ces opérations sont équivalentes à p[0] = pass[13].
Jusqu’à la ligne : var $34=$p; ce n’est que de la copie des caractères
10, 11, 12, 13, 14, 15 du mot de passe dans le buffer $p. Pour être plus précis :

p[0] = password[13]
   p[1] = password[15]
      p[2] = password[11]
      p[3] = password[14]
      p[4] = password[12]
      p[5] = password[10]

On remarque que le caractère 9 n’est pas utilisé, on peut donc mettre ce que l’on veut !
Ensuite nous avons notre premier check, on récupère p[5] c’est-à-dire passe[10], on garde que les 8 derniers bits ($37=(($36 << 24) >> 24)) , on prend le reste de la division avec 42 (_fmod($37, 42)) et on test si c’est bien égale à 7 ($40=(($39)|0)==7). Si ça n’est pas égale, on va dans l’état 9.
On a donc : fmod(pass[10], 42) == 7

b.      Case 3
On récupère p[3] c’est-à-dire passe[14] et on test si il est bien égale à 42 ($46=(($45)|0)==42).
On obtient ce test : pass[14] == 42

c.       Case 4
On récupère passe[13], on ajoute 3 ($52=((($51)+(3))|0)) puis on fait la racine carré ($54=_sqrt($53)). Et ceci doit être égale à pass[12] – 30. (Je passe les étapes …)
Test : sqrt(pass [13] + 3) == pass[12] - 30

d.      Case 5
Test : pass[10] == (ldexp(pass[11], 3) % 3) + 47

e.      Case 6
En regardant dans le code de la vm, llvm_pow_f64 est en fait Math.pow de JavaScript.
Test : pass[12] + 800 == pow(pass[11] / 2, 2)

f.        Case 7
Test : (pass[15] * 666) >> 9 == 57

On se retrouve donc avec une série de tests pour 5 caractères. Certains se jetteront sur le
brute force mais on peut aussi regarder de plus près !

Le test Case2 laisse que 2 valeurs possibles : 1 et [. Mais testant avec Case5 on voit que
seul « 1 » est bon pour pass[10]. « [» ne donne aucun caractère valide pour pass[11].
Ce qui nous fait pour l’instant : ![!}f?4;oA1 comme début de passe (J’ai pris A pour pass[9])

Case 7 nous dis que le caractère 15 ne peut être que « , ». Case 3 … que
le caractère 14 est « * ».

Pour le reste, pass[12] et pass[13] dépendent de pass[11], il suffit de brute forcer pass[11]
pour voir ce que ça donne :

Première solution : pass[11] = 16, pass[12] = 32 et pass[13] = 1
Deuxième solution : pass[11] = 112, pass[12] = 32 et pass[13] = 1
Ça m’étonnerait que le mot de passe contienne le caractère ascii 1 …

Il reste une dernière solution : pass[11] = 58, pass[12] = 41 et pass[13] = 118
Ce qui est plus acceptable et qui donne les lettres : :)v

Et voilà ! On a tout et cela donne : ![!}f?4;oA1:)v*,

5.       Résultat
On obtient cette image ainsi que le flag : !}$|==FLAG==|#[?%