Voici le write-up d'une d'une autre épreuve des prequals de la NDH 2k13.
Description
Description
Analyse du script
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));
}
}
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")
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 = '';
}
}
}
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, "")
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à :)