NetOn CTF 2021 - Grades

Web – 486 pts (10 solves) – Chall author: Raulet

Easily reversible encryption function behind obfuscation, yuckie.

Challenge

So somebody hacked the final exam grades to inflate their own… up to 1337, alright. Well in that case it seems our friend ‘nUK<,IDt-.bvKL ./EO$%;k}@_’ is the culprit. However, his name is encrypted…

Solution

Within the html file we find some obfuscated javascript

function encrypt(_0x40a681) {
    var _0x16b830 = 0x20, _0x561689 = 0x5e, _0x3db275 = 0x0, _0x38a41d = '';
    for (var _0x278871 = 0x0; _0x278871 < _0x40a681['length']; _0x278871++) {
         _0x38a41d = _0x38a41d + String['fromCharCode']((_0x40a681['charCodeAt'](_0x278871) + _0x3db275) % _0x561689 + _0x16b830), _0x3db275 = _0x3db275 + _0x40a681['charCodeAt'](_0x278871);
    }
    return _0x38a41d;

We can de-obfuscate this simple function by replacing the nonsense variable names with names that make sense, and by inserting values wherever possible. After this, we find a more readable encrypt function

function encrypt(input) {
    var output = '', c0 = 0;
    for (var i = 0; i < input['length']; i++) {
        output = output + String['fromCharCode']((input['charCodeAt'](i) + c0) % 94 + 32),
        c0 = c0 + input['charCodeAt'](i);
    }
    return output;

Because of the modulo in there, it might be a bit rough to reverse directly, so let’s just brute-force it instead. We can do so by simply trying out a character and seeing if our trial flag matches with the given encrypted flag. I used the following script

#!/usr/bin/env python3

# Encryption function
def encrypt(msg):
    out = ''; c0 = 0
    # Loop over input message
    for i in range(len(msg)):
        # Increment character ord value
        out += chr(((ord(msg[i]) + c0) % 94) + 32)
        # Increment addition variable
        c0 += ord(msg[i])
    # Return
    return out

# Character set
chars = list(r'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_}!?.,')

# Trial flag and given mystery flag
flag = 'NETON{'
mystery = 'nUK<,IDt-.bvKL|./EO$%;k}@_'

i = 0 # iterator
while flag[-1] != '}':
    trial = encrypt(flag+chars[i])
    # Check if our encrypted trial flag matches the mystery one
    if trial == mystery[:len(flag)+1]:
        flag += chars[i]
        i = 0
    else:
        i += 1
    # If we run out of characters
    if i >= len(chars):
        print('Expand character list!')
        break
        
print('Gottem!:', flag)

Which neatly returns our desired flag

Gottem!: NETON{Y0u_4r3_0n_th3_t0p!}