RingZer0 Team Online CTF Javascript challenges

6 min readSep 29, 2017

This is the second in my gradual series of write ups on CTF’s as I complete them. I previously wrote about using CTF’s to stay sharp and this is the next installment in my progress and focuses on some of the JavaScript challenges I have completed.

1. Client side validation is bad!

On this challenge a JS validation is used to check the password is correct before submission. Everyone should know that client side validation is a poor approach.

The password is stored as a set of char codes. It is simply a case of converting this char code to extract the password and since the username is also present within the js this challenge is very straightforward. To convert the character codes there is a wide variety of tools available, even just using the web browser console. I quite like CyberChef as a general purpose tool which I use quite frequently.

// Look’s like weak JavaScript auth script :)
$(“.c_submit”).click(function(event) {
var u = $(“#cuser”).val();
var p = $(“#cpass”).val();
if(u == “admin” && p == String.fromCharCode(74,97,118,97,83,99,114,105,112,116,73,115,83,101,99,117,114,101)) {
if(document.location.href.indexOf(“?p=”) == -1) {
document.location = document.location.href + “?p=” + p;
} else {
$(“#cresponse”).html(“<div class=’alert alert-danger’>Wrong password sorry.</div>”);

In this case I used a console in your web browser of choice and running the code below to decode the string and obtain the password.


With both of these values submit these to obtain the flag value and bag yourself an easy point.

2. Hashing is more secure

In this challenge the js checks a SHA1 value.

// Look’s like weak JavaScript auth script :)$(“.c_submit”).click(function(event) {event.preventDefault();var p = $(“#cpass”).val();if(Sha1.hash(p) == “b89356ff6151527e89c4f3e3d30c8e6586c63962”) {if(document.location.href.indexOf(“?p=”) == -1) {document.location = document.location.href + “?p=” + p;}} else {$(“#cresponse”).html(“<div class=’alert alert-danger’>Wrong password sorry.</div>”);}});

We have the SHA1 hash value that is being compared. Ultimately we will need to brute force the hash to guess the password. Given that it is a CTF this shouldn’t be a complex password that requires a dedicate password cracking rig. I took the approach that the password was going to be a simple text based password first and used hashcat which is a password cracking tool to conduct a simple brute force attack on a short password up to 8 characters that contains only alphabetic characters, if this didn’t work I would have tried a broader character set.

Start by adding the hash from the javascript above to a plain text file then use hashcat with a pattern to only try alphabetic characters.

hashcat -m 100 -a 3 /root/hashes.txt ?l?l?l?l?l?l?l?l? — force -i

The config above tells hashcat to work on sha1 hashes (-m 100) and to use a mask attack (-a 3) with the pattern specified, patterns are described on the hashcat wiki.

Once completed the results are stored in a .potfile similar to johntheripper and you can extract the password to submit and obtain the flag.

3. Obsfucation is more secure

The web form has a JS check function that also contains the password. It has be obsfucated but still readable.

_0xc360=[“\x76\x61\x6C”,”\x23\x63\x70\x61\x73\x73",”\x61\x6C\x6B\x33",”\x30\x32\x6C\x31",”\x3F\x70\x3D”,”\x69\x6E\x64\x65\x78\x4F\x66",”\x68\x72\x65\x66",”\x6C\x6F\x63\x61\x74\x69\x6F\x6E”,”\x3C\x64\x69\x76\x20\x63\x6C\x61\x73\x73\x3D\x27\x65\x72\x72\x6F\x72\x27\x3E\x57\x72\x6F\x6E\x67\x20\x70\x61\x73\x73\x77\x6F\x72\x64\x20\x73\x6F\x72\x72\x79\x2E\x3C\x2F\x64\x69\x76\x3E”,”\x68\x74\x6D\x6C”,”\x23\x63\x72\x65\x73\x70\x6F\x6E\x73\x65",”\x63\x6C\x69\x63\x6B”,”\x2E\x63\x5F\x73\x75\x62\x6D\x69\x74"];$(_0xc360[12])[_0xc360[11]](function (){var _0xf382x1=$(_0xc360[1])[_0xc360[0]]();var _0xf382x2=_0xc360[2];if(_0xf382x1==_0xc360[3]+_0xf382x2){if(document[_0xc360[7]][_0xc360[6]][_0xc360[5]](_0xc360[4])==-1){document[_0xc360[7]]=document[_0xc360[7]][_0xc360[6]]+_0xc360[4]+_0xf382x1;} ;} else {$(_0xc360[10])[_0xc360[9]](_0xc360[8]);} ;} )

I used http://jsnice.org/ to parse the script above and convert the hex codes to ascii codes and read the functions just to speed the process up. I could have done the same manually converting each array element.

/** @type {Array} */
var _0xc360 = ["val", "#cpass", "alk3", "02l1", "?p=", "indexOf", "href", "location", "<div class='error'>Wrong password sorry.</div>", "html", "#cresponse", "click", ".c_submit"];
$(_0xc360[12])[_0xc360[11]](function() {
var bgPos = $(_0xc360[1])[_0xc360[0]]();
var y = _0xc360[2];
if (bgPos == _0xc360[3] + y) {
if (document[_0xc360[7]][_0xc360[6]][_0xc360[5]](_0xc360[4]) == -1) {
document[_0xc360[7]] = document[_0xc360[7]][_0xc360[6]] + _0xc360[4] + bgPos;
} else {

The important section of the code above is

var y = _0xc360[2];
(bgPos == _0xc360[3] + y) {

Here we need to take the 3rd and 4th array elements of _0xc360 and concatenate them to get the resulting password then submit this to get the flag.

4. Why not?

Weak JS validation code again

// Look's like weak JavaScript auth script :)
$(".c_submit").click(function(event) {
var k = new Array(176,214,205,246,264,255,227,237,242,244,265,270,283);
var u = $("#cuser").val();
var p = $("#cpass").val();
var t = true;

if(u == "administrator") {
for(i = 0; i < u.length; i++) {
if((u.charCodeAt(i) + p.charCodeAt(i) + i * 10) != k[i]) {
$("#cresponse").html("<div class='alert alert-danger'>Wrong password sorry.</div>");
t = false;
} else {
$("#cresponse").html("<div class='alert alert-danger'>Wrong password sorry.</div>");
t = false;
if(t) {
if(document.location.href.indexOf("?p=") == -1) {
document.location = document.location.href + "?p=" + p;

First thoughts. K is a character code array and the username is adminsitrator.

The code uses the position of each character in the username and password to compare against the array in K as you can see below.

for(i = 0; i < u.length; i++) {if((u.charCodeAt(i) + p.charCodeAt(i) + i * 10) != k[i])

To decode we need a quick bit of script to decode. Mathematic rules of operation state multiplication before addition so we can extract the value out with ease. https://en.wikipedia.org/wiki/Order_of_operations a quick bit of js coding later and we can get the password.

var k = new Array(176,214,205,246,264,255,227,237,242,244,265,270,283);var u = “administrator”;var p = “”for(i = 0; i < u.length; i++) {p = p.concat(String.fromCharCode(k[i] — (u.charCodeAt(i) + i*10)));}console.log(p);

Run the above code in a JS interpreter and read the output to the console and submit with the username administrator to obtain the flag.

5. Valid key required

In this challenge a single key string must be submitted. The JS that checks the submitted key is split based on “-” sign and then compared to validate it and require it has 5 sections.

_strKey = document.getElementById(“key”).value;var a = _strKey.split(“-”);if(a.length !== 5)e = true;var o=a.map(genFunc).reduceRight(callback, new (genFunc(a[4]))(Function));if(!equal(o,ref))e = true;

The map function above uses a separate JS check to validate that the section is 4 digits. The key is a shuffled and generated into an object that should match:

{T : “BG8”,J : “jep”,j : “M2L”,K : “L23”,H : “r1A”}

Using jsfiddle I passed in positional arguments


This generates an object and we can map this back to the key since it is in effect a transposition of the input.

{T : “BG8”,J : “jep”,j : “M2L”,K : “L23”,H : “r1A”}

{ t: “bcq”, p: “fgm”, l: “jki”, h: “noe”, d: “rsa” }

So our key after adding the dashes back in:


I submitted this using Burp Repeater to the CTF to obtain the flag.

6. Most secure crypto

In this challenge the ante has been upped slightly by trying to use CryptoJS. However there are some obvious issues, its all exposed on the client side with the IV, key and encryped phrase.

The javascript to solve is:

$(“.c_submit”).click(function(event) {event.preventDefault();var k = CryptoJS.SHA256(“\x93\x39\x02\x49\x83\x02\x82\xf3\x23\xf8\xd3\x13\x37”);var u = $(“#cuser”).val();var p = $(“#cpass”).val();var t = true;if(u == “\x68\x34\x78\x30\x72”) {if(!CryptoJS.AES.encrypt(p, CryptoJS.enc.Hex.parse(k.toString().substring(0,32)), { iv: CryptoJS.enc.Hex.parse(k.toString().substring(32,64)) }) == “ob1xQz5ms9hRkPTx+ZHbVg==”) {t = false;}} else {$(“#cresponse”).html(“<div class=’alert alert-danger’>Wrong password sorry.</div>”);t = false;}if(t) {if(document.location.href.indexOf(“?p=”) == -1) {document.location = document.location.href + “?p=” + p;}}})

Using the web console and debugger within a web browser we can start to understand the code. k.toString() decodes to


Since we can see how the encryption is carried out and the initialisation vector to see the AES encryption is setup with CryptoJS we can use this knowledge to guess the password. Since the IV is a subset of k we have the key and IV already.


So far we have:

  • Key d8439507642eb76a4050adb27891d38a
  • IV 01fdb35ac5309d45a99f89c0a4ca0db6
  • BASE64 Encrypted password ob1xQz5ms9hRkPTx+ZHbVg==

We can use the decrypt function to obtain the plaintext password

var d = CryptoJS.AES.decrypt(“ob1xQz5ms9hRkPTx+ZHbVg==”, CryptoJS.enc.Hex.parse(k.toString().substring(0,32)), { iv: CryptoJS.enc.Hex.parse(k.toString().substring(32,64)) })//Result of d

This resulting value above is hex encoded ascii and needs to be decoded. I used CyberChef to decode from Hex.

The username is specified in hex codes and decodes to “h4xor” but doesnt need to be submitted.

Once you have the decoded ascii value submit the request to get the flag.




Security addict, 17+ years in industry making systems more secure and finding those that aren’t