COMPFEST13 CTF crypto writeup
書くのが遅れ、Resultなどは分からなくなりました…
Writeup
Snab? Yes, Snab
Snab? Yes, Snab
Snab likes to give you a challenge. It is a simple challenge of RSA encrypted messages, and you only have to find out what those messages really are! Be careful though, Snab has some trick up his sleeve.
ファイル:Snab.py, output.txt
# Snab.py from Cryptodome.Util.number import* e = 0x10001 s = pow(p + q, 2) n = p*q a = pow(s, 3, r) b = (s - q*(2*p + q))*r m_list = [findme] c_list = [] for i in range(len(m_list)): m = bytes_to_long(m_list[i]) c = pow(m*r, e, n) c_list.append(c) output = open("output.txt", "w") output.writelines([str(i) + "\n" for i in [e, s, n, a, b, c_list]]) output.close() """ output.txt 65537 518521484172073259043145502034694599512935443283076107003494840643504150663248402410462928864122818999731280619095755616648044687630285476363367234348295346345048643618601901838740100 121789376487960809489253386587170686658768726657045553214623415992384832614485249137256874454267032401365173859563210814953487893574413409932117585950570225259024509903129746392143101 14910 1443061772954701335732136128869248910288995712185482317126411260349775148932784597588115548780067761993841192961205698418501468762734686695975550561598140253475710348439640002745286347562 [95844532553991737600355244654272099305361975575150371319709729091243030203575898742071987199800250922501746626433985253038713853151746857514762678605619742310839669559545627531098676, 42098262117872607180245376226279234844537189667792611290978137770131205295202393318329675438677406769928295941768074280915365884838027414974072838410934952571392616562898636004189303, 8604504123043858588289398284978073629384165878986588408956445422750740896636700840713408309772547146776823067482307495576552057400894861616123713400577813256614795674220942022738198, 66896916235028791010554130879834163456721897024453929564151545727202320039792487273512943832159287883050106923587075192390665897004465138382234040927275478139131450371794658563343368, 88176130128782413821390318550151008388570132120182664342566671328546119423517817326934034720909238554168653863093116429325532932401977519369212892117707167802400008407395125896733332, 42250039274640778630603717605163827961176577828564055370588929192401015587247485151024369147022833032549004175634147831360114651662490704138925606397505368573040950634048151235675964, 106267843822546752528780879737401351948170741446817769684516569656816005147897267321452764634553751488085440938706773625287154372645991244141121226180609731226228509942129690482744498, 7344462713592491879813960159075800353984094813742489003735150623847056840460595091048879286634691169764793649426176975158414555454778075430233699780146900520609629142406422725693811, 68155732896092345896827379516624133280166986984023541993085330906321960888421556683672078055376548346464764100036149614632795220030187229733989823788323988946361921828069707823065198, 2456638129741631242062051214133833843357605035108383884677777076160879939756985403557604264648903511528401478876871578775440101482814072714355366084122429853207060638683606389504551, 99671982271645788903414016384550975165361965345980177928115018027271173062935625698434769263846972984813377601618481025600240081090732166957299336765744471217496851539810214590361856] """
未知数はfindmeと書いてあるようです。
1) p, q を求める
output.txtからsを持ってきます。
s = pow(p + q, 2)
とあるので、gmpyなどで2乗根を求めます。p+qとpq (=n) が分かっているので解と係数の関係でp, qを求めます。
2) r を求める。
b = (s - q*(2*p + q))*r
とありここで未知数はrだけなので、output.txtからb,sを持ってきて 1)の結果使えばrは求まります。うまくいかない場合は p, q を入れ替えましょう。
3) m_list を求める。
1) で求めたp, qから秘密鍵dを導出し、c_listの各値を復号すると、m×rとなるので、inverse(r,n)をかけてmを求めます。
b"#Snab says good job! But you're not done yet\n" b'flag = findme\n' b"halfa = ''.join([flag[i] for i in range (0, len(flag), 2)])\n" b"halfb = ''.join([flag[i] for i in range (1, len(flag), 2)]\n" b"p = bytes_to_long(bytes(halfa, encoding = 'utf-8'))\n" b"q = bytes_to_long(bytes(halfb, encoding = 'utf-8'))\n" b'r = 0\n' b'while (not(isPrime(p) and isPrime(q))):\n' b' p += 1\n' b' q += 1\n' b' r += 1\n'
復号するとこのようになりました。
4) flag を求める。
p, qを1ずつ足していって共に素数になったら終わるプログラムのようです。元のp, qはflagの偶数、奇数文字目をbytes_to_longしたものみたいです。
p-r, q-rをlong_to_bytesして、1文字毎組み合わせればOKです。
import gmpy2 from Crypto.Util.number import * import math e = 65537 s = n = a = 14910 B = c_list = b ,ch =gmpy2.iroot(s,2) b = int(b) c = n rt = b*b-4*c rt2, ch = gmpy2.iroot(rt,2) p = (int(rt2)+b)//2 q = n//p r = math.gcd(s**3-a,B) phi = (p-1)*(q-1) d = inverse(e,phi) assert math.gcd(e,phi) == 1 for c in c_list: m = pow(c,d,n) print(long_to_bytes((m*inverse(r,n))%n)) m2 = long_to_bytes(p-r) m1 = long_to_bytes(q-r) for i in range(len(m1)): x1 = m1[i] x2 = m2[i] print(chr(x1),end="") print(chr(x2),end="") print()
COMPFEST13{y0U_d1DnT_3xpEcT_t0_FinD_pQ_4s_a_fl4g_DiD_y0u_7e1877a801}
You AES Me Up
So I can stand on scoreboard~
nc 103.152.242.242 10016
ファイル:chall.py
#chall.py #!/usr/bin/env python3 import sys import os import random import binascii from Crypto.Cipher import AES from Crypto.Util.number import long_to_bytes, bytes_to_long from secret import FLAG IV = os.urandom(AES.block_size) KEY = os.urandom(AES.block_size) class Unbuffered(object): def __init__(self, stream): self.stream = stream def write(self, data): self.stream.write(data) self.stream.flush() def writelines(self, datas): self.stream.writelines(datas) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) sys.stdout = Unbuffered(sys.stdout) def pad(msg): return msg + (chr(16 - len(msg) % 16) * (16 - len(msg) % 16)).encode() def get_flag(): flag = pad(FLAG) cipher = AES.new(IV, AES.MODE_ECB) flag = cipher.encrypt(flag) enc = b'' flag = pad(flag) iv = IV for i in range(0, len(flag), 16): cipher = AES.new(KEY, AES.MODE_CBC, iv) enc += cipher.encrypt(flag[i:i+16]) iv = long_to_bytes(bytes_to_long(enc[i:i+16]) ^ bytes_to_long(flag[i:i+16])) print('flag (in hex) =', binascii.hexlify(enc).decode()) def encrypt(): msg = input('msg (in hex) = ') if (len(msg) % 2 != 0): print('Invalid input!') return msg = binascii.unhexlify(msg.encode()) cipher = AES.new(KEY, AES.MODE_CBC, IV) enc = cipher.encrypt(pad(msg)) print('enc (in hex) =', binascii.hexlify(enc).decode()) def decrypt(): enc = input('enc (in hex) = ') if (len(enc) % 32 != 0): print('Invalid input!') return enc = binascii.unhexlify(enc.encode()) cipher = AES.new(KEY, AES.MODE_CBC, IV) msg = cipher.decrypt(enc) print('msg (in hex) =', binascii.hexlify(msg).decode()) def menu(): print('1. Get encrypted flag') print('2. Encrypt a message') print('3. Decrypt a message') print('4. Exit') if __name__ == '__main__': while True: try: menu() choice = input('> ') if choice == '1': get_flag() elif (choice == '2'): encrypt() elif (choice == '3'): decrypt() elif (choice == '4'): print('Bye.') break else: print('Invalid input!') except: print('Something went wrong.') break
encrypt, decryptは所定のKEY, IVで行うのに対し、flagの暗号化だけはIVを変えながら、そしてflagを16文字毎暗号化して、暗号文の後ろに繋げています。
1) IV を求める
求められるのか、求められない前提で解くのか予想つけられない中、ググったらドンピシャのサイト発見。
Finding the IV of AES CBC (CTF) - Cryptography Stack Exchange
言われてみたら、そりゃそうだって感じなんですけどね。
2) ECBモードで復号後のflag を求める
KEYは不明なので、サーバ上で復号させます。flag[0:16]はenc[0:16]を投げればOKです。
以降のflagは少し特殊です。暗号化時にはiv (=long_to_bytes(bytes_to_long(enc[i-16:i]) xor bytes_to_long(flag[i-16:i]) ) )を使っていますが、サーバ上ではIVでしか復号できません。ivは求められるので、enc[i:i+16]を復号させてflag[i:i+16] xor IV を得ます。IVとivをxorさせればflag[i:i+16]が分かります。
3) flag を求める
IVをkeyとしたECBモードで暗号化したのが2)で求めたflagです。あとは今のように復号するだけです。
from Crypto.Util.number import * from functools import reduce from operator import mul from itertools import combinations import sys import socket, struct, telnetlib from tqdm import tqdm import base64 from Crypto.Util.Padding import pad,unpad from Crypto.Cipher import AES import binascii # --- common funcs --- def sock(remoteip, remoteport): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((remoteip, remoteport)) return s, s.makefile('rw') def read_until(f, delim='\n'): data = '' while not data.endswith(delim): data += f.read(1) return data def reading(f): for _ in range(4): read_until(f) read_until(f,"> ") return #HOSTはIPアドレスでも可 HOST, PORT = "103.152.242.242", 10016 s, f = sock(HOST, PORT) reading(f) # 暗号文取得 s.send(b"1\n") enc_flag = read_until(f).split()[-1] enc = [] for i in range(0,len(enc_flag),32): enc.append(enc_flag[i:i+32]) print(enc) #IVを求める c0 = "00"*16 c1 = "00"*16 reading(f) s.send(b"3\n") read_until(f,"= ") s.send(c1.encode()+b"\n") p1 = read_until(f).split()[-1] reading(f) s.send(b"3\n") read_until(f,"= ") s.send((c0+c1).encode()+b"\n") p2 = read_until(f).split()[-1] IV = int(p1,16)^int(p2[32:],16) print("IV =",long_to_bytes(IV)) print("len IV =",len(long_to_bytes(IV))) #flagを求める flag = [] reading(f) s.send(b"3\n") read_until(f,"= ") s.send(enc[0].encode()+b"\n") recv_m = read_until(f).split() flag0 = recv_m[-1] #flag0 = flag[0:16] print("flag0 =",flag0) flag.append(flag0) ft = flag0 for i in range(1,len(enc)): iv = int(enc[i-1],16)^int(flag[-1],16) reading(f) s.send(b"3\n") read_until(f,"= ") s.send(enc[i].encode()+b"\n") recv_m = read_until(f).split() ret = recv_m[-1] ft = int(ret,16)^iv^IV flag.append(hex(ft)[2:]) print(flag) enc = "" for cf in flag: t = "0"*(32-len(cf))+cf enc += t print("enc =",enc) enc = binascii.unhexlify(enc.encode()) enc = unpad(enc,16) print("enc =",enc) print("len IV =",len(long_to_bytes(IV))) cipher = AES.new(long_to_bytes(IV), AES.MODE_ECB) flag = cipher.decrypt(enc) print(flag)
COMPFEST13{y0u_aes_me_UpPpppPp_____t0_c0d3_on_st0rmy_Sea4aA5____e0212d1a34}
Secure Channel
You are able to watch the encrypted chat of Alice and Bob. They are talking about the flag right now. Can you get the flag from them?
P.S.: Alice and Bob once have said that they will communicate with some trivial encoding, so people will not notice their message at first glance. Although, people will know it if they search it on the internet! Sure, all of it is printable characters.
Watch the conversation: nc 103.152.242.56 8231
Talk with Alice: nc 103.152.242.56 8230
Talk with Bob: nc 103.152.242.56 8232
ファイル:alice-bob.py, secrets.py, talk-with-alice.py, talk-with-bob.py
# alice-bob.py #!/usr/bin/env python3 import sys from base64 import b64encode, b64decode from Crypto.Util.number import getPrime, bytes_to_long as bl, long_to_bytes as lb from secrets import Alice, Bob from chats import alice_dialogue, bob_dialogue import time class Unbuffered(object): def __init__(self, stream): self.stream = stream def write(self, data): self.stream.write(data) self.stream.flush() def writelines(self, datas): self.stream.writelines(datas) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) sys.stdout = Unbuffered(sys.stdout) try: g = bl(b64decode(input('g: '))) assert g > 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF p = getPrime(512) alice = Alice() bob = Bob() alice_public_part = alice.make_public_part(g, p) bob_public_part = bob.make_public_part(g, p) alice.make_private_part(bob_public_part, p) bob.make_private_part(alice_public_part, p) print('p:', p) print('Alice\'s public part:', b64encode(lb(alice_public_part)).decode()) #print('Bob\'s public part:', b64encode(lb(bob_public_part)).decode()) # Bob doesn't want to share it to you :( print() assert len(alice_dialogue) == len(bob_dialogue) while True: for i in range(len(alice_dialogue)): print('Messages from Alice:') msg = alice.send_message(alice_dialogue[i]) print(b64encode(msg).decode()) print(bob.receive_message(msg)) print() time.sleep(0.5) print('Messages from Bob:') msg = bob.send_message(bob_dialogue[i]) print(b64encode(msg).decode()) print(alice.receive_message(msg)) print() time.sleep(0.5) except: print('Something is wrong.') exit(0)
# secrets.py #!/usr/bin/env python3 from Crypto.Util.number import bytes_to_long as bl, long_to_bytes as lb from Crypto.Cipher import AES import os import random import string sp = list(map(ord, list(string.printable))) def pad(msg): pad_length = random.randint(20, 100) for i in range(pad_length): c = random.randint(0, 255) while c in sp: c = random.randint(0, 255) pos = random.randint(0, len(msg) - 1) msg = msg[:pos] + chr(c).encode() + msg[pos:] while True: c = random.randint(0, 255) while c in sp: c = random.randint(0, 255) pos = random.randint(0, len(msg) - 1) msg = msg[:pos] + chr(c).encode() + msg[pos:] if (len(msg) % 16 == 0): break return msg class Person: def make_public_part(self, g, p): return pow(g, self.secret, p) def make_private_part(self, gx, p): self.key = pow(gx, self.secret, p) % 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF self.key = lb(self.key) while (len(self.key) != 16): self.key += b'\x01' return self.key def send_message(self, msg): iv = os.urandom(16) cipher = AES.new(self.key, AES.MODE_CBC, iv) enc = iv + cipher.encrypt(pad(msg)) return enc def receive_message(self, enc_message): try: iv = enc_message[:16] enc = enc_message[16:] cipher = AES.new(self.key, AES.MODE_CBC, iv) msg = cipher.decrypt(enc) return 'Message received!' except: return 'Message not received!' class Alice(Person): def __init__(self): self.secret = 0 # REDACTED class Bob(Person): def __init__(self): self.secret = 0 # REDACTED assert 2 < self.secret < 100 class You(Person): def __init__(self, secret): self.secret = secret
# talk-with-alice.py #!/usr/bin/env python3 import sys from base64 import b64encode, b64decode from Crypto.Util.number import getPrime, bytes_to_long as bl, long_to_bytes as lb from secrets import Alice, You class Unbuffered(object): def __init__(self, stream): self.stream = stream def write(self, data): self.stream.write(data) self.stream.flush() def writelines(self, datas): self.stream.writelines(datas) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) sys.stdout = Unbuffered(sys.stdout) try: your_secret = bl(b64decode(input('Your secret: '))) g = bl(b64decode(input('g: '))) assert g > 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF p = getPrime(512) alice = Alice() you = You(your_secret) alice_public_part = alice.make_public_part(g, p) your_public_part = you.make_public_part(g, p) alice.make_private_part(your_public_part, p) your_private_part = you.make_private_part(alice_public_part, p) print('p:', p) print('Alice\'s public part:', b64encode(lb(alice_public_part)).decode()) print('Your public part:', b64encode(lb(your_public_part)).decode()) print('Your private part:', b64encode(your_private_part).decode()) print() while True: msg = b64decode(input('Message to Alice: ')) print(alice.receive_message(msg)) print() except: print('Something is wrong.') exit(0)
# talk-with-bob.py #!/usr/bin/env python3 import sys from base64 import b64encode, b64decode from Crypto.Util.number import getPrime, bytes_to_long as bl, long_to_bytes as lb from secrets import Bob, You class Unbuffered(object): def __init__(self, stream): self.stream = stream def write(self, data): self.stream.write(data) self.stream.flush() def writelines(self, datas): self.stream.writelines(datas) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) sys.stdout = Unbuffered(sys.stdout) try: your_secret = bl(b64decode(input('Your secret: '))) g = bl(b64decode(input('g: '))) assert g > 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF p = getPrime(512) bob = Bob() you = You(your_secret) bob_public_part = bob.make_public_part(g, p) your_public_part = you.make_public_part(g, p) bob.make_private_part(your_public_part, p) your_private_part = you.make_private_part(bob_public_part, p) print('p:', p) print('Bob\'s public part:', b64encode(lb(bob_public_part)).decode()) print('Your public part:', b64encode(lb(your_public_part)).decode()) print('Your private part:', b64encode(your_private_part).decode()) print() while True: msg = b64decode(input('Message to Bob: ')) print(bob.receive_message(msg)) print() except: print('Something is wrong.') exit(0)
色んなpythonプログラムがありますが、要約するとAlice, Bobはそれぞれ秘密鍵を持っていてDiffie-Hellman鍵共有で鍵を生成し、その鍵をAESのCBCモードで暗号化する際に使って会話しているという感じです。
1) Bobの秘密鍵を求める
Bobの秘密鍵はsecrets.pyにあるように2から100までの間と簡単にbrute forceできる値となっています。gはassert文に引っかからないようにしてtalk-with-bob.pyと通信すると、 "Bob's public part:" から計算して、Bobの秘密鍵が73ということが分かります。
2) 二人の共有している鍵を求める
alice-bob.pyと通信すると、"Alice's public part:"を教えてくれます。二人の秘密鍵をa,bとすると、これはga mod pです。実際に暗号化に使う鍵はgab mod pでありbは1)で73と求めたので、("Alice's public part:")b mod pで鍵は求まりました。
3) 二人の会話の内容を求める
二人の暗号化された会話文を復号してみると、こんな感じです。
b'\x1c\xc2\x05\xc3\x04\x1f\xc3\xc2\xbe\x13\x1d\xc3\x18\x84\xc3\xbb\xc3\xc3\x9a\xc3\xb7\xc3\x83\x87\xc2\x9c\xc2\x98\xc3\x85\xb4\x99\xb4\xc2\x9c\xc2\x99\xc3\xc3\xc2\x88\xa0\x15\xc3\xac\x0f\xc2\x96\x16\x1d\x84\x15\xc2\x9a\xc3\xbf\xc2\xc2\x8c\x94\xc2\xc2\xc2\x97\xc2\xa0\x9f\xc2\x89\xbb\x10\xc3\xa1\xc2\xbf\xc2\xc3\x91\xad\x05\xc2\x998\xc3\x817\xc3\x94d\xc3\xc3\x8f\xc2\xc2\x9e\x95\x9f\xc2\xc3\x93\xc3\x83\xb5\xc2\xc3\x9c\xb9\xc2\xc3\x00\xc3\xbb\xa2\xc2\x9d\xc3\x9f\xc3\xa4\xc3\x91\x05\x08\xc2\xc3\x1e\x82\xc2\xb5\xa7\xb4\xc2\x95\xc2\xa0\xc3\xa6\x0f\xc2\x12\xb1\xc2\xb5&' b"8\xc3\x88\xc3\xbc\xc3\xbc\x1c\x04\xc2\x02\xb6\xc2\x8b\x07\xc3\xc2\xb1\x9d\xc3\xc3\xc2\x83\x92\xc3\xbf\xb87\xc2\x8d\x1b\xc3\x90\xc3\xc2\xbd\xb9\x00\xc2\xc2\x83\x84\x7f\x12d\x15'2" b'\xc2\x90\x0f\xc3\xa1\xc3\xc2\x90\x17\xc3\xaf\x07\xc3\xab\x88\x19\xc3\xc3\x0f\xbf\xad\xc2\xc2\x12\x88\xc2\x9a\x8c\xc3\xa5\xc3\x80\xc3\x9d\xc2\xc3\xb7\x1a\xbd6\xc3\xc3\xac\x87\xc3\xbc\xc2\x8d\xc3\x80\xc3\x96\xc2\xc3\xab\x9a\xc2\x8e\xc2\xbc>\xc2\xc3\x8d\x9a\xc2\xc2\xad\xab\xc3\xc3\x9a\x1b\xc2\x11\xc2\x85\x15\x0f\xc3\x95\xa8\x80\xc3\xc2\x86\x82\xc2\x11\xb8\x02\xc2\x98\xc2\xbd\xc2\xc2\x8a\x84\xc3\xb1\xc2\x86\xc3\xb0\xc3\x99\xc2\xc3\xc3\xc2\x9b\x02\x17\x9f\xc2\xc3\x87\xc3\x87\xc3\x91\xc2\xb9\x80\x7f\x9e\xc2\xaa\xc3\xc2\xc2\x9f\xba\xc3\xc2\x92\xc3\xc3\xae\xaf\xc3\x81\x80\xc2\xa4\xc2\xc3\xaa\xa5\xae\xc2\xc3\xc3\xbd\x8f\x93\x0f\xc2\xb1\x1a\xc2\x91\xbd\xc2\xa6p<\x16\xc3\x93\x1ac/\xc2\xbb\xc2\x02\x9d\x1c\xc3\xc2\xbc\xc3\x8b\x83\xc2\xc3\x99\x83\x1fc'
復号うまくいってないように見えますが、これうまくいってます。\xc2がやけに多く見られます。
これ、pad関数がこんな風な変な平文にしているのです。pad(msg)とあると、いつものパディングかと見過ごしてしまいました。結局、pad関数はprintableではない文字を付け足しているだけです。なので非ASCII文字を除外すればOKです。
87d& 87d'2 6>p<c/c =(l#a 6uO2nDfm1=Bkq9&@3BW&@rc.&56 =(lLpA8c%#DC9NKCh[Zr56 :2+3L/g*_.BOQ'q+EV:2F!,(2@:q1 =(l#a56 6VgEQ7R^6T0f+/5An3YW1c@1! 88W2r+A-ctF<G[=AKYT!EcZ=F1,'h\BOPpi@ru:&F$B 8LJ?tE,oN3FEo!MF`M%9H#IgJBOQ'q+EV:.+ED%7F8 =_2#T/0JP@@:re"0KM*G>l 8K_\TG%De<BOr;uCggs\2D@0t+EVO?/hSa :MVL(8K`4kCht58ASu$$BlkJ+AoqU)+F.mJ/g*Z&+E)-M 1GE8q0f:XC5tP'F:N^>XAiOC2@qI_]An?A 1,r\s@P^#%2*#8*An*\P0Ocjn@l.XL2e?JY@:V;R2)-jB3Ab/( :2+3L/0K.J+D>2,AKZ).AKYT$@:p^#Dg*? 8K_\bE+L/*@:O(aEcW@5@;]t$F<GX9AKZ).Blbm <+oue+Cf(nDJj$%+DGm>F(Jj(Eb-A6BkM+$56 =_2#T/c 8K_\bE+L/;DfmFJAKZ#-B4uB> 8K_\bE+L/5D_; :MV(pBOu& 6@!,p 6@!, @X2M
取り除いてみるとこんな感じになりました。問題文からも何かしら暗号化を施しているのでしょう。base64では使えない文字があるのでbase85だろうと思ったが復号できず…。ここでタイムオーバーとなりました。
答えはASCII85でした。
base85とASCII85の違いは知りませんでした…。符号化の時の使う文字の順番が異なるようですね(ASCII85はASCIIコードでの昇順)。
pythonではbase64.685decode([byte列]) で復号できます。
b'Hey' b'Hey?' b'Bob..' b'What' b'Do you like a secret?' b"Who doesn't like?" b'Nice. I have this flag.' b'What?' b'COMPFEST13{4fd29464a' b'Hmm. Just the first 20 characters?' b"I'm pretty sure you have the rest" b'Yeah, flag[20:60]' b'I have the flag[60:] too...' b'Ok, I will send it after you. Go on.' b'30b51506AIUEOuh_f8facf99fe}' b'28a1b39559f4fc500b41c4b17ec8ad74512394a8' b'Nice, we have the flag now!' b'I hope hackers cannot see this.' b'This channel is secure right?' b'Yeah.' b"I hope you're right." b'I hope so.' b'Ok then' b'Bye.' b'Bye' b'bye'
COMPFEST13{4fd29464a28a1b39559f4fc500b41c4b17ec8ad74512394a830b51506AIUEOuh_f8facf99fe}