Intro

This challenge has a single HTML file. It has obfuscated JavaScript code.

Write-Up

Script has a lot of junk inside. It is very hard to clean up the mess manually. So I coded basic utility to clean up the script. Basically, I will use esprima to find junk function blocks and replace them with new lines.

const esprima = require('esprima')
const fs = require('fs');
const _ = require('lodash');

function isFunc(node) {
    const name = _.get(node,'id.name')
    const whiteList = ['Add','S8PnPCDSnKdSqe'] // don't remove those
    if (_.includes(whiteList,name)) {
        return false
    }
    if (node.type == 'FunctionDeclaration') {
        return true;
    }
    return false;
}

function removeCalls(source) {
    const entries = [];
    esprima.parseScript(source, {}, function (node, meta) {
        if (isFunc(node)) {
            entries.push({
                start: meta.start.offset,
                end: meta.end.offset
            });
        }
    });
    entries.sort((a, b) => { return b.end - a.end }).forEach(n => {
        let replacement = Array(n.end-n.start).fill('\n') // fill functions with new lines
        source = source.slice(0, n.start) + replacement + source.slice(n.end);
    });
    return source.replace(/\n,/g,'')
}
    let input = fs.readFileSync('./input.js','utf-8')
    let result = removeCalls(input);
    fs.writeFileSync('./cleaned.js',result)

After the cleanup, we end up with the below script.

PyKEvIqAmUkUVL0Anfn9FElFUN2dic3z = "4fny3z...."
GJrFu0fnwTxv2znmydOO5NG23UTO0MypKl = "b2JDN2luc2tiYXhLOFZaUWRRWTlSeXdJbk9lVWxLcHlrMXJsRnk5NjJaWkQ4SHdGVjhyOENQeFE5dGxUaEd1dGJ5ZDNOYTEzRmZRN1V1emxkZUJQNTN0Umt6WkxjbDdEaU1KVWF1M29LWURzOGxUWFR2YjJqQW1HUmNEU2RRcXdFSERzM0d3emhOaGVIYlE3dm9aeVJTMHdLY2Vhb3YyVGQ4UnQ2SXUwdm1ZbGlVYjA4YVRES2xESnlXU3NtZENMN0J4MnBYdlZET3RUSmlhY2V6Y3B6eUM2Mm4yOWs=";

Ljasr99E9HLv1BBnSfEHYw = 64

EuF8AepyhtkSXEWvNKIKZMaSHm4v = atob(GJrFu0fnwTxv2znmydOO5NG23UTO0MypKl).split('');

npxuau2RsDO0L4hSVCBHx = atob(PyKEvIqAmUkUVL0Anfn9FElFUN2dic3z).split('');
Oz9nOiwWfRL6yjIwvM4OgaZMIt0B = npxuau2RsDO0L4hSVCBHx
PeFEvMaDMvyrYg8UZgKKfVMBhak5 = npxuau2RsDO0L4hSVCBHx.length;

bNT5lGtaxYHeyHFeEdImdD12Csa7MlR='Cflsdgfdjgflkdsfjg4980utjkfdskfglsldfgjJLmSDA49sdfgjlfdsjjqdgjfj'.split('');

//  This is read from the edit box.

if(qguBomGfcTZ6L4lRxS0TWx1IwG[0].length==Ljasr99E9HLv1BBnSfEHYw)bNT5lGtaxYHeyHFeEdImdD12Csa7MlR=qguBomGfcTZ6L4lRxS0TWx1IwG[0].split('');

for (i=0; i < EuF8AepyhtkSXEWvNKIKZMaSHm4v.length; i++) { EuF8AepyhtkSXEWvNKIKZMaSHm4v[i] = (EuF8AepyhtkSXEWvNKIKZMaSHm4v[i].charCodeAt(0) + bNT5lGtaxYHeyHFeEdImdD12Csa7MlR[i % Ljasr99E9HLv1BBnSfEHYw].charCodeAt(0)) & 0xFF; }

for (i=0; i < PeFEvMaDMvyrYg8UZgKKfVMBhak5; i++) { Oz9nOiwWfRL6yjIwvM4OgaZMIt0B[i] = (Oz9nOiwWfRL6yjIwvM4OgaZMIt0B[i].charCodeAt(0) - EuF8AepyhtkSXEWvNKIKZMaSHm4v[i % EuF8AepyhtkSXEWvNKIKZMaSHm4v.length]) & 0xFF;  }

sEjdWWMFU4wObKZap4WeMBgdfgIfTHCvS="";

for (i=0; i < npxuau2RsDO0L4hSVCBHx.length; i++) { sEjdWWMFU4wObKZap4WeMBgdfgIfTHCvS+=String.fromCharCode(Oz9nOiwWfRL6yjIwvM4OgaZMIt0B[i]);}

We have to find key for bNT5lGtaxYHeyHFeEdImdD12Csa7MlR (table) so that when this code runs, it should create a valid JavaScript code for sEjdWWMFU4wObKZap4WeMBgdfgIfTHCvS(code) variable. Table values must be valid ASCII values because they would be entered with the keyboard. Code should also have valid chars between 0x20-0x7F after the decryption. So I extracted two big tables to different text files and run the below script.

import base64

def isPrintable(char):
    whitespace = [' ', '\r', '\n', '\t']
    if chr(char) in whitespace:
        return True
    return char > 0x20 and char < 0x7F

def readbase64File(name):
    with open(name,'rb') as f:
        result = bytearray(base64.b64decode(f.read()))
        f.close()
        return result

def writeFile(name,content):
    with open(name,'wb') as f:
        f.write(content)
        f.close()

first_table = readbase64File('./first_table.txt') #4fny3zLzDRYIOe37Ax
second_table = readbase64File('./second_table.txt') #b2JDN2
# first_table = readbase64File('./second_layer_first.txt') #4fny3zLzDRYIOe37Ax
# second_table = readbase64File('./second_layer_second.txt') #b2JDN2
dictlist = [dict() for x in range(64)]
for i,val in enumerate(first_table):
    for j in range(0x20,0x7F):
        plain = (first_table[i] - second_table[i % len(second_table)] - j) & 0xFF
        if isPrintable(plain):
            index = i % len(second_table) % 64
            if j in dictlist[index]:
                dictlist[index][j] += 1
            else:
                dictlist[index][j] = 1

all_possibilities = []
for index,val in enumerate(dictlist):
    counts = sorted(val.items(), key=lambda item: item[1], reverse=True)
    letter_candidates = []
    (x,highfreq) = counts[0]

    for i in range(20):
        (l,frequency) = counts[i]
        if (frequency == highfreq):
            letter_candidates.append(chr(l))
    all_possibilities.append(letter_candidates)

print(all_possibilities)
final = ''
for item in all_possibilities:
    if len(item) > 1:
        final += '*'
    else:
        final += item[0]
print(final)

final = ''
for item in all_possibilities:
    print(item)
    if len(item) > 1:
        final += item[len(item)-1]
    else:
        final += item[0]
print(final)

When we run the above script, we get our key as ChVCVYzI1dU9cVg1ukBqO2u4UGr9aVCNWHpMUuYDLmDO22cdhXq3oqp8jmKBHUWI

When we decrypt it using the above code, then we get another JavaScript file that starts with

//Yes, but who can deny the heart that is yearning?
//Affirmative!
//Uh-oh!
//This.

//Here's your change. Have a great afternoon! Can I help who's next?
[][(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!

It is a JSFuck encoded script. We use a decoder to decode this and we get another JS file

(function (qguBomGfcTZ6L4lRxS0TWx1IwG) {     sInNWkbompb8pOyDG5D = "u8n2Ffpa3OQdRcn9UvkS3T8CAR+mOc/7/wYBJu/...

We modify the above script and run it against this one and we get UQ8yjqwAkoVGm7VDdhLoDk0Q75eKKhTfXXke36UFdtKAi0etRZ3DoHPz7NxJPgHl as a password. Upon decryption, we get the next JavaScript code.

//He's not bothering anybody.
//Why would you question anything? We're bees.

//Listen, you better go 'cause we're really busy working.
[][(![]+[])[....

We again use the same decoder and this time we get

alert("I_h4d_v1rtU411y_n0_r3h34rs4l_f0r_th4t@flare-on.com")

Flare-On 2021 Write-ups

I am available for new work
Interested? Feel free to reach