After unzipping the file I found strange it was a hta file extension which I never heard off, but after analyzing the code it was just some html and javascript.
Some of the JavaScript code was minimized so I just used http://jsbeautifier.org/ to make it readable for humans.
The first thing you can see in the code are these three interesting hashes:
1 2 3 4 5 6 7
window.host=FnBJT9OVUieRCjeTgMPMBe4U.hs; var m = window.host; var nl = [0,2,1,12,7,15,5,4,8,16,17,3,9,10,14,11,13,6,0]; var ko="c33367701511b4f6020ec61ded352059"; var ka="61636f697b57b5b7d389db0edb801fc3"; var kq="d2172edf24129e06f3913376a12919a4";
The first one is actually very easy to crack I used john the ripper but you could find it even easier by searching it on google you can immediately find it’s a md5 hash by analysing the FnBJT9OVUieRCjeTgMPMBe4U.hs which is a known js code for md5 encryption.
1 2 3 4 5 6
kinyabitch@Debian ~/h/p/j/run> ./john --wordlist=password.lst pass.txt --format=raw-md5 Loaded 1 password hash (Raw MD5 [128/128 SSE2 intrinsics 12x]) 654321 (?) guesses: 1 time: 0:00:00:00 DONE (Sun Jul 3113:09:532016) c/s: 960 trying: 1q2w3e - blazer Use the "--show" option to display all of the cracked passwords reliably
After cracking it with john you find the hash c33367701511b4f6020ec61ded352059 was actually ‘654321’.
Analyzing this function was the key to this challenge with this code you can find out the flag has 18 characters always having this format “TMCTF{pass}” otherwise will be always wrong.
var pwbody = (" " + pass.substring(6, pass.length - 1)).split("");
After passing this first length checks you will notice that this for loop is actually mixing from different indexes splited into 3 strings (h1, h2, h3).
1 2 3 4 5 6
for (var i = 0; i < pwbody.length;) { h1 += pwbody[nl[++i]]; h2 += pwbody[nl[++i]]; h3 += pwbody[nl[++i]]; };
Then you will see somethig that is a little confusing 3 boolean conditions (co, ca and cq),the first regex expression replaces characters like spaces you will find the function in “m” that is actually the hash function into md5:
1 2
if (co(m(h1.replace(/(^\s+)|(\s+$)/g, ""))) && ca(m(h3.replace(/(^\s+)|(\s+$)/g, ""))) && cq(m(h2.replace(/(^\s+)|(\s+$)/g, "")))) {...}
And now after looking to this 3 functions you will realize why you couldn’t crack the other two, because some of its characters are being replaced by the right ones making the hash much easier to crack (you can copy the entire js code (browser JS console) and just run the replace code in ka and kq string).
1 2 3 4 5 6 7 8 9 10 11 12
functionco(o) { return (o === ko); }
functionca(a) { return (a === ka.replace(/6/g, '2').replace(/b/g, 'a').replace(/d/g, '4')); }
A now cracking it on john you find out they ‘qwerty’ and ‘admin’.
1 2 3 4 5 6
kinyabitch@Debian ~/h/p/j/run> ./john --wordlist=password.lst pass2.txt --format=raw-md5 Loaded 3 password hashes with no different salts (Raw MD5 [128/128 SSE2 intrinsics 12x]) Remaining 2 password hashes with no different salts qwerty (?) admin (?)
After finding this 3 hashes you can finally realize you can reconstruct the password basing on that for loop, I wrote a python script to make it easier to change (doing manually would take a lot of time and I could make mistakes):