Flare-On 8 Challenge 8 - beelogin

We are given a huge HTML file with lots of Javascript in it. On first glance, there seems to be a lot of redundant code.

Upon looking through the Javascript, it seems that removing the miscellaneous functions within the Add function would help to clean up some stuff.

original

Through discussions with a friend, he told me that we could actually use vim to remove all defined functions within Add through the usage of macros.
To remove all inner functions using vim, perform the following:

  1. Type qqq to clear all registers
  2. Type qq to begin recording the macro
  3. Type /function<cr>f{da{dd@q as the macro body
  4. Type q to finish recording
  5. Navigate to the first line of the Add function and execute @q

This will trigger the recursive macro until all inner functions are removed, and the macro dies(?)

We are then present with minimal JS that is the challenge.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function Add(xDyuf5ziRN1SvRgcaYDiFlXE3AwG) {
    PyKEvIqAmUkUVL0Anfn9FElFUN2dic3z = \\<huge base64 string\\>
    GJrFu0fnwTxv2znmydOO5NG23UTO0MypKl = "b2JDN2luc2tiYXhLOFZaUWRRWTlSeXdJbk9lVWxLcHlrMXJsRnk5NjJaWkQ4SHdGVjhyOENQeFE5dGxUaEd1dGJ5ZDNOYTEzRmZRN1V1emxkZUJQNTN0Umt6WkxjbDdEaU1KVWF1M29LWURzOGxUWFR2YjJqQW1HUmNEU2RRcXdFSERzM0d3emhOaGVIYlE3dm9aeVJTMHdLY2Vhb3YyVGQ4UnQ2SXUwdm1ZbGlVYjA4YVRES2xESnlXU3NtZENMN0J4MnBYdlZET3RUSmlhY2V6Y3B6eUM2Mm4yOWs=";
    Ljasr99E9HLv1BBnSfEHYw = 64

    qguBomGfcTZ6L4lRxS0TWx1IwG=xDyuf5ziRN1SvRgcaYDiFlXE3AwG.ZJqLM97qEThEw2Tgkd8VM5OWlcFN6hx4y2.value.split(';')
    npxuau2RsDO0L4hSVCBHx = atob(PyKEvIqAmUkUVL0Anfn9FElFUN2dic3z).split('');
    PeFEvMaDMvyrYg8UZgKKfVMBhak5 = npxuau2RsDO0L4hSVCBHx.length;
    EuF8AepyhtkSXEWvNKIKZMaSHm4v = atob(GJrFu0fnwTxv2znmydOO5NG23UTO0MypKl).split('');
    bNT5lGtaxYHeyHFeEdImdD12Csa7MlR='gflsdgfdjgflkdsfjg4980utjkfdskfglsldfgjJLmSDA49sdfgjlfdsjjqdgjfj'.split('');
    // if input/key is 64 bytes long, use that instead of hardcoded key here^
    if(qguBomGfcTZ6L4lRxS0TWx1IwG[0].length==Ljasr99E9HLv1BBnSfEHYw)bNT5lGtaxYHeyHFeEdImdD12Csa7MlR=qguBomGfcTZ6L4lRxS0TWx1IwG[0].split('');

    // length is 221
    // decode smaller base64 string into array, and transform that array using the supplied key
    for (i=0; i < EuF8AepyhtkSXEWvNKIKZMaSHm4v.length; i++) { 
    	EuF8AepyhtkSXEWvNKIKZMaSHm4v[i] = (EuF8AepyhtkSXEWvNKIKZMaSHm4v[i].charCodeAt(0) + bNT5lGtaxYHeyHFeEdImdD12Csa7MlR[i % Ljasr99E9HLv1BBnSfEHYw].charCodeAt(0)) & 0xFF;	
    }

    Oz9nOiwWfRL6yjIwvM4OgaZMIt0B = npxuau2RsDO0L4hSVCBHx

    // decrypt ciphertext based on transformed array in previous for loop
    for (i=0; i < PeFEvMaDMvyrYg8UZgKKfVMBhak5; i++) { 
        Oz9nOiwWfRL6yjIwvM4OgaZMIt0B[i] = (Oz9nOiwWfRL6yjIwvM4OgaZMIt0B[i].charCodeAt(0) - EuF8AepyhtkSXEWvNKIKZMaSHm4v[i % EuF8AepyhtkSXEWvNKIKZMaSHm4v.length]) & 0xFF;	
    }
    sEjdWWMFU4wObKZap4WeMBgdfgIfTHCvS="";

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

    // execute final output
    if('rFzmLyTiZ6AHlL1Q4xV7G8pW32'>=sEjdWWMFU4wObKZap4WeMBgdfgIfTHCvS)eval(sEjdWWMFU4wObKZap4WeMBgdfgIfTHCvS)
}

From the cleaned up Javascript, we can see an array being formed from a base64-decoded string, and being transformed with our key.

The array is then used to decrypt the huge base64 decoded blob. The final output is then passed into eval, to be interpreted as Javascript.

Knowing that the final output is Javascript, we can safely determine that the final output would be in the range of printable ascii characters.

From the algorithm, we can conclude the following:

  1. Key is 64 bytes long
  2. nth byte of key affects (n//64 + n)th byte of the first array (EuF8AepyhtkSXEWvNKIKZMaSHm4v)
  3. Xth byte of EuF8AepyhtkSXEWvNKIKZMaSHm4v array affects (X//221+X)th

As such, we can draw the conclusion that nth byte of the key affects [(n//64+n)//221+(n//64+n)]th byte of the plaintext.

Following this equation, we can write a script that brute-forces the key byte-by-byte.
The algorithm:

  1. Init a key of length 64, with the value of the current byte candidate
  2. Run through the array generation and decryption algorithm
  3. Find all bytes affected by current byte being brute-forced
  4. If the bytes contain non-ascii characters, determine the key byte to be invalid and move on

After this process, you should be left with only a few candidates (if not only 1) for each byte of the key. The candidates can be further chosen based on the resulting output and if it make sense.

After doing so, a JSFuck script will be decrypted.

jsfuck

Upon decoding the JSFuck, another similar JS script to the original JS will be revealed. Repeat the process and get another JSFuck script. Decode that and get the flag.

key1: ChVCVYzI1dU9cVg1ukBqO2u4UGr9aVCNWHpMUuYDLmDO22cdhXq3oqp8jmKBHUWI

key2: UQ8yjqwAkoVGm7VDdhLoDk0Q75eKKhTfXXke36UFdtKAi0etRZ3DoHPz7NxJPgHl

[email protected]