1330 views
# squarectf 2019 ## talktome the server greets us with a `Hello!` sending only a newline gives us `I wish you would greet me the way I greeted you.` any input including [a-zA-Z] seems to result in a `Sorry, I can't understand you.` response, so just replying with `Hello` dosen't work many inputs that don't contain those characters throw errors any integers for example throw ``` Hello! 1 undefined method `match' for 1:Integer /talk_to_me.rb:16:in `receive_data' /var/lib/gems/2.5.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in `run_machine' /var/lib/gems/2.5.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in `run' /talk_to_me.rb:31:in `<main>' ``` messages with symbols like `+` or `*` however return a slightly different error ``` Hello! * (eval):1: syntax error, unexpected end-of-input, expecting '=' /talk_to_me.rb:16:in `eval' /talk_to_me.rb:16:in `receive_data' /var/lib/gems/2.5.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in `run_machine' /var/lib/gems/2.5.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in `run' /talk_to_me.rb:31:in `<main>' ``` so our input is being thrown into `eval()`! we can confirm this by trying some math ``` Hello! 1+1 undefined method `match' for 2:Integer /talk_to_me.rb:16:in `receive_data' /var/lib/gems/2.5.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in `run_machine' /var/lib/gems/2.5.0/gems/eventmachine-1.2.7/lib/eventmachine.rb:195:in `run' /talk_to_me.rb:31:in `<main>' ``` our error now complains about the integer `2`, so we know we're definitely being evaled to reply to the server without using the alphabet we have to craft some ruby magic thankfully, there was a [talk at rubyconf about esoteric ruby coding](https://www.slideshare.net/mametter/esoteric-obfuscated-artistic-programming-in-ruby) and slide 10 contains the whole solution for this challenge ``` Hello! "" << 72 << 101 << 108 << 108 << 111 It's so great to talk to you! Maybe you know what to do with this flag-2b8f1139b0726726? ``` when validating that flag on your profile page a js alert pops up with the message `Happy Birthday, my little hacker! I’m sorry I couldn’t be there in person to celebrate your big twenty, but I hope I make up for it with this little nugget: “Change your keyboard layout every 5000 strokes.” -- Aunt Matilda` ## aesni The challenge is a 32-bit ELF binary that just prints `⛔` and exits when run. `alok & mbyczkowski` are the authors for this challenge btw, just in case you want to know who to get mad at :^) It uses an instruction from the [AES-NI instruction set](https://en.wikipedia.org/wiki/AES_instruction_set) extension to decrypt blocks of code one at a time, each of which then sets up the parameters for the next block. The actual payload is scattered across multiple blocks. After decrypting the first block, eax is getting popped off the stack to check if `argc == 2`. if true, a different block gets decrypted and a different codepath is taken. at `0x80480aa` a string compare is being prepared, at `0x80480b0` the string has been built at address `0x80480ef` and we see that the pass is "ThIs-iS-fInE". when that check finishes and returns true, it prints the flag. ``` $ ./aesni ThIs-iS-fInE flag-cdce7e89a7607239 ``` your aunties advice this time is ``` “Plug mysterious USB keys into your roommate's computer first.” -- Aunt Matilda ``` ## decode me the archive contains an encrypted png file and a pyc file that can be turned into readable code with uncompyle the code reads ```python import base64, string, sys from random import shuffle def encode(f, inp): s = string.printable init = lambda : (list(s), []) bag, buf = init() for x in inp: if x not in s: continue while True: r = bag[0] bag.remove(r) diff = (ord(x) - ord(r) + len(s)) % len(s) if diff == 0 or len(bag) == 0: shuffle(buf) f.write(('').join(buf)) f.write('\x00') bag, buf = init() shuffle(bag) else: break buf.extend(r * (diff - 1)) f.write(r) shuffle(buf) f.write(('').join(buf)) if __name__ == '__main__': with open(sys.argv[1], 'rb') as (r): w = open(sys.argv[1] + '.enc', 'wb') b64 = base64.b64encode(r.read()) encode(w, b64) ``` h, tamara here, the maths fucks with me but the basic principle of the decoding is as follows: ```python def decode(f, inp): bag = [] buf = [] for x in inp: if x=="\0": for b in bag: diff_count = buf.count(b) # Do things, b/diff_count difference, output result bag, buf = [],[] elif x in bag: buf += x else: bag += x ``` Bag and buf are set to printable chars and [], respectively. For each character, a difference value is calculated, and the first bag char is taken and removed. If the difference value is nil, or the bag is depleted, buf will be shuffled and then written, followed by a null byte. Bag and buf will be overwritten with the same values of initialisation, but unlike initialisation, bag will then be randomised. Then, the value pulled from the bag will be written, and the bag value, duplicated by the difference(-1) will be written to buf. ### (decodeme solution) ```python #!/usr/bin/env python3 import base64, string, sys B64 = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+/=' B64 = [ord(c) for c in B64] def decode(inp): inp += b"\x00" bag = [] buf = [] out = [] for x in inp: if x==0: for b in bag: diff_count = buf.count(b) ch = b+diff_count+1 - 100 while ch not in B64: ch += 100 out.append(chr(ch)) bag, buf = [],[] elif x in bag: buf.append(x) else: bag.append(x) return "".join(out) if __name__ == '__main__': with open(sys.argv[1], 'rb') as f: print(decode(f.read())) ``` Some of the values were unprintable, incrementing by 100 until they reached b64 range worked perfectly `./squarecrypt3.py decodeme/decodeme.png.enc | base64 -d > decodeme.png` ## inwasmble when checking the html of the page we can see a weird js ```javascript eval(unescape(escape('󠅶󠅡󠅲󠄠󠅣󠅯󠅤󠅥󠄠󠄽󠄠󠅮󠅥󠅷󠄠󠅕󠅩󠅮󠅴󠄸󠅁󠅲󠅲󠅡󠅹󠄨󠅛󠄊󠄠󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄶󠄱󠄬󠄠󠄰󠅸󠄷󠄳󠄬󠄠󠄰󠅸󠄶󠅤󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄵󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄶󠄰󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄷󠅦󠄬󠄠󠄰󠅸󠄰󠄳󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄵󠄬󠄠󠄰󠅸󠄰󠄳󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄷󠄬󠄠󠄰󠅸󠄱󠄵󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄰󠄶󠄬󠄠󠄰󠅸󠄶󠅤󠄬󠄠󠄰󠅸󠄶󠄵󠄬󠄠󠄰󠅸󠄶󠅤󠄬󠄠󠄰󠅸󠄶󠅦󠄬󠄠󠄰󠅸󠄷󠄲󠄬󠄠󠄰󠅸󠄷󠄹󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄸󠄬󠄠󠄰󠅸󠄷󠄶󠄬󠄠󠄰󠅸󠄶󠄱󠄬󠄠󠄰󠅸󠄶󠅣󠄬󠄠󠄰󠅸󠄶󠄹󠄬󠄠󠄰󠅸󠄶󠄴󠄬󠄠󠄰󠅸󠄶󠄱󠄬󠄠󠄰󠅸󠄷󠄴󠄬󠄠󠄰󠅸󠄶󠄵󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠅡󠄬󠄊󠄠󠄠󠄰󠅸󠄸󠄷󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄸󠄴󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄴󠄬󠄠󠄰󠅸󠄷󠅦󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄴󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄴󠄰󠄬󠄠󠄰󠅸󠄰󠄳󠄬󠄠󠄰󠅸󠄴󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄴󠄶󠄬󠄠󠄰󠅸󠄰󠅤󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄴󠄰󠄬󠄠󠄰󠅸󠄰󠄳󠄬󠄊󠄠󠄠󠄰󠅸󠄴󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄴󠄶󠄬󠄠󠄰󠅸󠄰󠅤󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄴󠄬󠄊󠄠󠄠󠄰󠅸󠄶󠅣󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄸󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠄸󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄶󠅣󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠅣󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄊󠄠󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄴󠄬󠄠󠄰󠅸󠄶󠅣󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄸󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄳󠄶󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠅤󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄊󠄠󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄸󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠅤󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄷󠄳󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄴󠄬󠄊󠄠󠄠󠄰󠅸󠄶󠅣󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄸󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠅤󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄷󠄬󠄠󠄰󠅸󠄰󠅤󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠅣󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠅦󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄲󠄷󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄸󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄴󠅡󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄵󠅢󠄬󠄠󠄰󠅸󠄶󠄰󠄬󠄠󠄰󠅸󠅡󠄰󠄬󠄠󠄰󠅸󠄶󠄴󠄬󠄠󠄰󠅸󠄹󠄲󠄬󠄠󠄰󠅸󠄷󠅤󠄬󠄠󠄰󠅸󠅣󠅦󠄬󠄠󠄰󠅸󠄴󠄲󠄬󠄊󠄠󠄠󠄰󠅸󠅥󠅢󠄬󠄠󠄰󠅸󠄴󠄶󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄱󠄷󠄬󠄠󠄰󠅸󠅦󠅤󠄬󠄠󠄰󠅸󠄵󠄰󠄬󠄠󠄰󠅸󠄳󠄱󠄬󠄠󠄰󠅸󠄶󠄷󠄬󠄠󠄰󠅸󠄱󠅦󠄬󠄠󠄰󠅸󠄲󠄷󠄬󠄠󠄰󠅸󠄷󠄶󠄬󠄠󠄰󠅸󠄷󠄷󠄬󠄊󠄠󠄠󠄰󠅸󠄴󠅥󠄬󠄠󠄰󠅸󠄳󠄱󠄬󠄠󠄰󠅸󠄹󠄴󠄬󠄠󠄰󠅸󠄰󠅥󠄬󠄠󠄰󠅸󠄶󠄷󠄬󠄠󠄰󠅸󠄰󠄳󠄬󠄠󠄰󠅸󠅤󠅡󠄬󠄠󠄰󠅸󠄱󠄹󠄬󠄠󠄰󠅸󠅢󠅣󠄬󠄠󠄰󠅸󠄵󠄱󠄊󠅝󠄩󠄻󠄊󠄊󠅶󠅡󠅲󠄠󠅷󠅡󠄠󠄽󠄠󠅮󠅥󠅷󠄠󠅗󠅥󠅢󠅁󠅳󠅳󠅥󠅭󠅢󠅬󠅹󠄮󠅉󠅮󠅳󠅴󠅡󠅮󠅣󠅥󠄨󠅮󠅥󠅷󠄠󠅗󠅥󠅢󠅁󠅳󠅳󠅥󠅭󠅢󠅬󠅹󠄮󠅍󠅯󠅤󠅵󠅬󠅥󠄨󠅣󠅯󠅤󠅥󠄩󠄩󠄻󠄊󠅶󠅡󠅲󠄠󠅢󠅵󠅦󠄠󠄽󠄠󠅮󠅥󠅷󠄠󠅕󠅩󠅮󠅴󠄸󠅁󠅲󠅲󠅡󠅹󠄨󠅷󠅡󠄮󠅥󠅸󠅰󠅯󠅲󠅴󠅳󠄮󠅭󠅥󠅭󠅯󠅲󠅹󠄮󠅢󠅵󠅦󠅦󠅥󠅲󠄩󠄻󠄊󠄊󠅡󠅳󠅹󠅮󠅣󠄠󠅦󠅵󠅮󠅣󠅴󠅩󠅯󠅮󠄠󠅧󠅯󠄨󠄩󠄠󠅻󠄊󠄠󠄠󠅳󠅩󠅺󠅥󠅳󠄠󠄽󠄠󠅛󠄮󠄮󠄮󠅛󠄮󠄮󠄮󠅁󠅲󠅲󠅡󠅹󠄨󠄴󠄩󠅝󠄮󠅫󠅥󠅹󠅳󠄨󠄩󠅝󠄮󠅭󠅡󠅰󠄨󠅸󠄽󠄾󠅸󠄪󠄱󠄲󠄸󠄩󠄻󠄊󠄠󠄠󠅢󠅵󠅦󠄮󠅳󠅥󠅴󠄨󠅸󠄮󠅶󠅡󠅬󠅵󠅥󠄮󠅳󠅵󠅢󠅳󠅴󠅲󠄨󠅳󠅩󠅺󠅥󠅳󠅛󠄰󠅝󠄬󠄠󠅳󠅩󠅺󠅥󠅳󠅛󠄱󠅝󠄩󠄮󠅰󠅡󠅤󠅅󠅮󠅤󠄨󠅳󠅩󠅺󠅥󠅳󠅛󠄱󠅝󠄩󠄮󠅳󠅰󠅬󠅩󠅴󠄨󠄧󠄧󠄩󠄮󠅭󠅡󠅰󠄨󠅸󠄽󠄾󠅸󠄮󠅣󠅨󠅡󠅲󠅃󠅯󠅤󠅥󠅁󠅴󠄨󠄧󠄧󠄩󠄩󠄩󠄻󠄊󠄠󠄠󠅩󠅦󠄠󠄨󠅷󠅡󠄮󠅥󠅸󠅰󠅯󠅲󠅴󠅳󠄮󠅶󠅡󠅬󠅩󠅤󠅡󠅴󠅥󠄨󠄩󠄩󠄠󠅻󠄊󠄠󠄠󠄠󠄠󠅨󠅡󠅳󠅨󠄠󠄽󠄠󠅡󠅷󠅡󠅩󠅴󠄠󠅷󠅩󠅮󠅤󠅯󠅷󠄮󠅣󠅲󠅹󠅰󠅴󠅯󠄮󠅳󠅵󠅢󠅴󠅬󠅥󠄮󠅤󠅩󠅧󠅥󠅳󠅴󠄨󠄢󠅓󠅈󠅁󠄭󠄱󠄢󠄬󠄠󠅢󠅵󠅦󠄮󠅳󠅬󠅩󠅣󠅥󠄨󠅳󠅩󠅺󠅥󠅳󠅛󠄲󠅝󠄬󠄠󠅳󠅩󠅺󠅥󠅳󠅛󠄳󠅝󠄩󠄩󠄻󠄊󠄠󠄠󠄠󠄠󠅲󠄮󠅩󠅮󠅮󠅥󠅲󠅔󠅥󠅸󠅴󠄠󠄽󠄠󠄢󠅜󠅵󠅄󠄸󠄳󠅄󠅜󠅵󠅄󠅅󠅁󠄹󠄠󠅦󠅬󠅡󠅧󠄭󠄢󠄠󠄫󠄠󠅛󠄮󠄮󠄮󠄠󠅮󠅥󠅷󠄠󠅕󠅩󠅮󠅴󠄸󠅁󠅲󠅲󠅡󠅹󠄨󠅨󠅡󠅳󠅨󠄩󠅝󠄮󠅭󠅡󠅰󠄨󠅸󠄠󠄽󠄾󠄠󠅸󠄮󠅴󠅯󠅓󠅴󠅲󠅩󠅮󠅧󠄨󠄱󠄶󠄩󠄩󠄮󠅪󠅯󠅩󠅮󠄨󠄧󠄧󠄩󠄻󠄊󠄠󠄠󠅽󠄠󠅥󠅬󠅳󠅥󠄠󠅻󠄊󠄠󠄠󠄠󠄠󠅲󠄮󠅩󠅮󠅮󠅥󠅲󠅈󠅔󠅍󠅌󠄠󠄽󠄠󠅸󠄮󠅶󠅡󠅬󠅵󠅥󠄠󠄽󠄽󠄠󠄢󠄢󠄠󠄿󠄠󠄢󠄦󠅮󠅢󠅳󠅰󠄻󠄢󠄠󠄺󠄠󠄢󠅜󠅵󠄲󠄶󠅄󠄴󠄢󠄻󠄊󠄠󠄠󠅽󠄊󠅽').replace(/u.{8}/g,''))) ``` it might seem like the string that is being passed to escape is empty, but it contains a whole bunch of unprintable characters when running this in the js console without the eval we get the follownig js ```javascript var code = new Uint8Array([ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x15, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x08, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, 0x0a, 0x87, 0x01, 0x01, 0x84, 0x01, 0x01, 0x04, 0x7f, 0x41, 0x00, 0x21, 0x00, 0x02, 0x40, 0x02, 0x40, 0x03, 0x40, 0x20, 0x00, 0x41, 0x20, 0x46, 0x0d, 0x01, 0x41, 0x02, 0x21, 0x02, 0x41, 0x00, 0x21, 0x01, 0x02, 0x40, 0x03, 0x40, 0x20, 0x00, 0x20, 0x01, 0x46, 0x0d, 0x01, 0x20, 0x01, 0x41, 0x04, 0x6c, 0x41, 0x80, 0x02, 0x6a, 0x28, 0x02, 0x00, 0x20, 0x02, 0x6c, 0x21, 0x02, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x0b, 0x20, 0x00, 0x41, 0x04, 0x6c, 0x41, 0x80, 0x02, 0x6a, 0x20, 0x02, 0x41, 0x01, 0x6a, 0x36, 0x02, 0x00, 0x20, 0x00, 0x2d, 0x00, 0x00, 0x20, 0x00, 0x41, 0x80, 0x01, 0x6a, 0x2d, 0x00, 0x00, 0x73, 0x20, 0x00, 0x41, 0x04, 0x6c, 0x41, 0x80, 0x02, 0x6a, 0x2d, 0x00, 0x00, 0x47, 0x0d, 0x02, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x21, 0x00, 0x0c, 0x00, 0x0b, 0x0b, 0x41, 0x01, 0x0f, 0x0b, 0x41, 0x00, 0x0b, 0x0b, 0x27, 0x01, 0x00, 0x41, 0x80, 0x01, 0x0b, 0x20, 0x4a, 0x6a, 0x5b, 0x60, 0xa0, 0x64, 0x92, 0x7d, 0xcf, 0x42, 0xeb, 0x46, 0x00, 0x17, 0xfd, 0x50, 0x31, 0x67, 0x1f, 0x27, 0x76, 0x77, 0x4e, 0x31, 0x94, 0x0e, 0x67, 0x03, 0xda, 0x19, 0xbc, 0x51 ]); var wa = new WebAssembly.Instance(new WebAssembly.Module(code)); var buf = new Uint8Array(wa.exports.memory.buffer); async function go() { sizes = [...[...Array(4)].keys()].map(x=>x*128); buf.set(x.value.substr(sizes[0], sizes[1]).padEnd(sizes[1]).split('').map(x=>x.charCodeAt(''))); if (wa.exports.validate()) { hash = await window.crypto.subtle.digest("SHA-1", buf.slice(sizes[2], sizes[3])); r.innerText = "\uD83D\uDEA9 flag-" + [... new Uint8Array(hash)].map(x => x.toString(16)).join(''); } else { r.innerHTML = x.value == "" ? "&nbsp;" : "\u26D4"; } } ``` `code` sets up a wasm object that is being loaded into `wa` which exports the function `validate()` to check if your input is correct you can turn that block of hex into a file to get the wasm file and disassemble/decompile it, or you can do into chromes debugger tab and refresh the page. it should show a wasm entry in the file list on the left which should only have one entry, our validate function. when opening it you are presented with the following disassembly ```wasm func (result i32) (local i32 i32 i32 i32) i32.const 0 local.set 0 block block loop local.get 0 i32.const 32 i32.eq br_if 1 i32.const 2 local.set 2 i32.const 0 local.set 1 block loop local.get 0 local.get 1 i32.eq br_if 1 local.get 1 i32.const 4 i32.mul i32.const 256 i32.add i32.load offset=0 align=4 local.get 2 i32.mul local.set 2 local.get 1 i32.const 1 i32.add local.set 1 br 0 end end local.get 0 i32.const 4 i32.mul i32.const 256 i32.add local.get 2 i32.const 1 i32.add i32.store offset=0 align=4 local.get 0 i32.load8_u offset=0 align=1 local.get 0 i32.const 128 i32.add i32.load8_u offset=0 align=1 i32.xor local.get 0 i32.const 4 i32.mul i32.const 256 i32.add i32.load8_u offset=0 align=1 i32.ne <<<<< br_if 2 <<<<< local.get 0 i32.const 1 i32.add local.set 0 br 0 end end i32.const 1 return end i32.const 0 end ``` there will be a stack and a few local variables/"registers" in the `Local` scope it also has is own "memory" which is a max_uint16 sized array in global. we don't need that to get to the solution, so let's not dump that in here too :^) the two marked instructions ```i32.ne; br_if 2``` perform a check on two values in the stack, so set a breakpoint there. the first character i got by brute forceing until the values on the stack were identical, xoring the values in the next rounds gets us the next character in the wrong case, so xor by 0x20 to fix it and convert from dec to ascii. enter that character in the field and keep stepping until you reach the next character and xor+increase the values again until you're done and get the flag. i recommmend using xor.pw since you can hand it the values straight as decs and get a hex, dec or ascii result but remember that memory i said was unimportant to us? that's not completely true, the string place in the input field on the page is being placed right at the beginning of that and it's initialized with dec 32, which in ascii corresponds to a space, so the code will just continue going even though you didn't enter the space character yet. (it's also why we have to add 32 to because the code xors by the value you give it earlier, which flips a single bit) after doing this 32 times you should be left with the string `Impossible is for the unwilling.` and the flag `flag-bee523b8ed974cb8929c3a5f2d89e4fb99694a2` is printed on the page flacs: alternatively you can look at the wasm assembly and write a program that computes the solution: ```python #!/usr/bin/env python3 from hashlib import sha1 from struct import pack l = [] for i in range(32): p = 2 for j in range(i): p = (p * l[j]) & 0xFFFFFFFF l.append(p + 1) h = sha1(b''.join([pack('<I', i) for i in l])).hexdigest() print('flag-' + h) ``` ## lockbox the challenge is a timelock website located at https://lockbox-6ebc413cec10999c.squarectf.com. the golang source code of the server is provided. the idea of the website is that you submit a short text message that the server keeps secret for a configurable amount of time after which it becomes available to anyone who has knowledge of the hash of the message. we're interested in the database entry with id=3 but we don't know the hash and the timelock only expires one year from now. after submitting an entry of our own, we are provided with a url with the id and hash of our message. the server doesn't handle the url parameters securely, so the entire sql database including the id=3 entry can be dumped using sqlmap. however, the messages are encrypted before they are stored into the database, so we still need a way to convince the server to decrypt them for us. let's look at the captcha: the server generates a random value, encrypts it, and serves it to the client. the client requests the captcha image using a url containing this value. the server decrypts the value and generates a jumbled image containing the decrypted characters. apart from the fact that the captcha is completely useless since the server doesn't prevent us from solving the same captcha over and over, what sticks out is that the server uses the same key to encrypt submitted messages and to encrypt the random captcha characters. to decrypt any of the messages that we dumped we can just request a captcha image with the content set to the encrypted message and the server will happily generate a captcha with the decrypted text for us.