An Utsusemi of Keramas

Tales of security research, penetration testing, red teaming, and general hacking shenanigans.

19 November 2018

Square CTF - Dot-n-Dash

by Keramas

This was a fun little programming challenge from the recent Square CTF.

We are given an html file and an encoded 'instructions.txt' file, which is a cipher of sorts containing nothing but dots and dashes.

Loading the html file shows that there is JavaScript running that can encode and decode text. Unfortunately, the developers never finished the decode function off, so it is up to us to find a way to decode the message.



Looking at the source code, we can get a better understanding of what is going on, and also strip away functions and play with the app to get a clearer picture.


function encode() {
var t = input.value;
if (/^[-.]+$/.test(t)) {
alert("Your text is already e'coded!");
} else {
input.value = _encode(t);
}
return false;
}

function decode() {
var t = input.value;
if (/^[-.]*$/.test(t)) {
input.value = _decode(t);
} else {
alert("Your text is not e'coded!");
}
return false;
}

function _encode(input) {
var a=[];
for (var i=0; i < input.length; i++) {
var t = input.charCodeAt(i);
for (var j=0; j<8 if="" j="" t="">> j) & 1) {
a.push(1 + j + (input.length - 1 - i) * 8);
}
}
}

var b = [];
while (a.length) {
var t = (Math.random() * a.length)|0;
b.push(a[t]);
a = a.slice(0, t).concat(a.slice(t+1));
}

var r = '';
while (b.length) {
var t = b.pop();
r = r + "-".repeat(t) + ".";
}
return r;
}

// Everything below this line was lost due to cosmis radiation. The engineer who knows
// where the backups are stored already left.
function _decode(input) {
return "";
}


Trying to encode "a":


Trying to encode "aa":




Essentially what this code is doing is taking the input given, converting all characters in the string to a decimal value, and then determining which bits are active and creates an array based on the bit numbers that are a 1. The next part randomizes all of the numbers in the array, and then each number is represented by dashes equal to how large it is. Each number is delimited by dots.

Now that we have this information, we can create a Python script to convert the encoded instructions.txt into an array.


stri = open("instructions.txt","r").read()
stri_list = stri.split('.')
a = []
for i in stri_list[:-1]:
#print len(i)
a.append(len(i))
print a




While the encoder tries to make it tricky by randomizing all the numbers, the idea is that this is a very long binary string, and each "1" is being represented by a number in a total of 1296 bits, so we can sort the numbers easily.



Now it is just a matter of adding to our script to create a binary string with these numbers as the "1" and the other numbers as "0", and then convert that binary string to ASCII text.


import binascii

stri = open("instructions.txt","r").read()
stri_list = stri.split('.')
a = []
for i in stri_list[:-1]:
#print len(i)
a.append(len(i))

print "Encoded array: "
print sorted(a, key=int)
print "\n"

allbits = []
for bits in range(1,1297):
allbits.append(bits)

modbits = []
for i in allbits:
if i in a and i in allbits:
i=1
modbits.append(i)
else:
i=0
modbits.append(i)

binstring = ''.join(str(e) for e in modbits)

flag = int(binstring[::-1],2)
print binascii.unhexlify('%x' % flag)

Running the script and we get the flag!




























tags: CTF challenge