この大会は2023/4/15 0:00(JST)~2023/4/16 0:00(JST)に開催されました。
今回もチームで参戦。結果は1022点で381チーム中29位でした。
自分で解けた問題をWriteupとして書いておきます。
Speed-Rev: Humans (rev)
$ nc cha.hackpack.club 41709 Welcome to the speedrun challenge! You have 30 minutes to solve 5 levels! Level 1, here is the binary! f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAcBA... What is the flag?
base64デコードすると、バイナリになる。何回か試し、Ghidraでデコンパイルしたところ、以下のようになり、毎回異なる文字列だが、validate関数で文字列の比較を行っている。
bool validate(char *param_1) { int iVar1; iVar1 = strncmp(param_1,"jKuhSWXRGzt5D8bK",0x10); return iVar1 != 0; }
バイナリのオフセット0x2004-0x2013の16バイトがフラグになる。
Level 2のバイナリでは、デコンパイル結果は以下のようになる。
undefined8 validate(char *param_1) { undefined8 uVar1; if (*param_1 == 'R') { if (param_1[1] == 'S') { if (param_1[2] == 'A') { if (param_1[3] == 'o') { if (param_1[4] == 'i') { if (param_1[5] == 'p') { if (param_1[6] == 'z') { if (param_1[7] == 'o') { if (param_1[8] == '8') { if (param_1[9] == 'Q') { if (param_1[10] == 'o') { if (param_1[0xb] == 'g') { if (param_1[0xc] == '6') { if (param_1[0xd] == 'p') { if (param_1[0xe] == 'a') { if (param_1[0xf] == 'X') { uVar1 = 0; } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } return uVar1; }
バイナリのオフセットが0x1155から25バイトごとに11個取り出し、さらに22バイトごとに5個取り出したものがフラグになる。
Level 3のバイナリでは、デコンパイル結果はLevel 2と同様で、アセンブリの構造も同じなので、フラグの取り出し方は同様にできる。
Level 4のバイナリでは、デコンパイル結果は以下のようになる。
undefined8 validate(char *param_1) { undefined8 uVar1; if ((int)param_1[1] + (int)*param_1 == 0xaf) { if ((int)param_1[2] + (int)param_1[1] == 0xa4) { if ((int)param_1[3] + (int)param_1[2] == 0x96) { if ((int)param_1[4] + (int)param_1[3] == 0xac) { if ((int)param_1[5] + (int)param_1[4] == 0x9b) { if ((int)param_1[6] + (int)param_1[5] == 0x80) { if ((int)param_1[7] + (int)param_1[6] == 0x8d) { if ((int)param_1[8] + (int)param_1[7] == 0xaa) { if ((int)param_1[9] + (int)param_1[8] == 0xa7) { if ((int)param_1[10] + (int)param_1[9] == 0x8a) { if ((int)param_1[0xb] + (int)param_1[10] == 0x8a) { if ((int)param_1[0xc] + (int)param_1[0xb] == 0x8b) { if ((int)param_1[0xd] + (int)param_1[0xc] == 0x9f) { if ((int)param_1[0xe] + (int)param_1[0xd] == 0xa7) { if ((int)param_1[0xf] + (int)param_1[0xe] == 0xa8) { uVar1 = 0; } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } return uVar1; }
バイナリの "\x48\x8b\x45\xf8\x0f\xb6\x00" の後から "\x3d"または "\x83\xf8" の次の値を15個取り出したものがそれぞれフラグの各文字の隣通しの和になる。あとは英数字でこの条件を満たすよう1文字目をブルートフォースし、フラグを求める。
Level 5のバイナリでは、デコンパイル結果は以下のようになる。
undefined8 validate(char *param_1) { undefined8 uVar1; if (*param_1 == 'X') { if ((int)param_1[2] + (int)param_1[1] == 0xa6) { if ((int)param_1[3] + (int)param_1[2] == 0x8c) { if ((int)param_1[4] + (int)param_1[3] == 0x99) { if ((int)param_1[5] + (int)param_1[4] == 0xa5) { if (param_1[5] == 'a') { if (param_1[6] == 'B') { if (param_1[7] == 'A') { if (param_1[8] == 'S') { if (param_1[9] == 'C') { if (param_1[10] == '8') { if ((int)param_1[0xc] + (int)param_1[0xb] == 0xac) { if ((int)param_1[0xd] + (int)param_1[0xc] == 0x91) { if ((int)param_1[0xe] + (int)param_1[0xd] == 0x8a) { if ((int)param_1[0xf] + (int)param_1[0xe] == 0xbc) { uVar1 = 0; } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } } else { uVar1 = 1; } return uVar1; }
バイナリの "\x48\x8b\x45\xf8\x0f\xb6\x00" の後から次のように条件を取り出す。
・"\x3c"の次の値がフラグ文字 ・"\x3d"の次の値または\x83\xf8"の次の値は和の値
z3でフラグを割り出す。
Level 6のバイナリでは、デコンパイル結果はLevel 5と同様で、アセンブリの構造も同じなので、フラグの取り出し方は同様にできる。
#!/usr/bin/env python3 import socket from base64 import * from z3 import * def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('cha.hackpack.club', 41709)) data = recvuntil(s, b'\n').rstrip() print(data) #### Level 1 #### for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data) level1 = b64decode(eval(data)) flag = level1[0x2004:0x2014].decode() data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### Level 2 #### for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data) level2 = b64decode(eval(data)) flag = '' offset = 0x1155 for i in range(16): flag += chr(level2[offset]) if i < 10: offset += 25 else: offset += 22 data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### Level 3 #### for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data) level3 = b64decode(eval(data)) flag = '' offset = 0x1155 for i in range(16): flag += chr(level3[offset]) if i < 10: offset += 25 else: offset += 22 data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### Level 4 #### for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data) level4 = b64decode(eval(data)) adds = [] offset = level4.index(b'\x48\x8b\x45\xf8\x0f\xb6\x00') for i in range(15): offset1 = level4.find(b'\x3d', offset) + 1 offset2 = level4.find(b'\x83\xf8', offset) + 2 if min(offset1, offset2) > offset: offset = min(offset1, offset2) else: offset = max(offset1, offset2) adds.append(level4[offset]) for f0 in range(48, 123): flag = chr(f0) error = False for i in range(15): code = adds[i] - ord(flag[-1]) if code < 0: error = True break flag += chr(code) if error: continue if flag.isalnum(): break data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### Level 5 #### for _ in range(7): data = recvuntil(s, b'\n').rstrip() print(data) level5 = b64decode(eval(data)) x = [BitVec('x%d' % i, 8) for i in range(16)] slv = Solver() for i in range(16): slv.add(Or(And(x[i] > 47, x[i] < 58), And(x[i] > 64, x[i] < 91), And(x[i] > 96, x[i] < 123))) offset = level5.index(b'\x48\x8b\x45\xf8\x0f\xb6\x00') for i in range(15): offset1 = level5.find(b'\x3c', offset) + 1 offset2 = level5.find(b'\x3d', offset) + 1 offset3 = level5.find(b'\x83\xf8', offset) + 2 if offset1 < offset: offset1 = 99999 if offset2 < offset: offset2 = 99999 if offset3 < offset: offset3 = 99999 offsets = [offset1, offset2, offset3] flg = -1 offset = 99999 for j in range(3): if offsets[j] < offset: flg = j offset = offsets[j] if flg == 2: flg = 1 if flg == 0: slv.add(x[i] == level5[offset]) else: slv.add(x[i] + x[i + 1] == level5[offset]) r = slv.check() assert r == sat m = slv.model() flag = '' for i in range(16): flag += chr(m[x[i]].as_long()) data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### Level 6 #### for _ in range(6): data = recvuntil(s, b'\n').rstrip() print(data) level6 = b64decode(eval(data)) x = [BitVec('x%d' % i, 8) for i in range(16)] slv = Solver() for i in range(16): slv.add(Or(And(x[i] > 47, x[i] < 58), And(x[i] > 64, x[i] < 91), And(x[i] > 96, x[i] < 123))) offset = level6.index(b'\x48\x8b\x45\xf8\x0f\xb6\x00') for i in range(15): offset1 = level6.find(b'\x3c', offset) + 1 offset2 = level6.find(b'\x3d', offset) + 1 offset3 = level6.find(b'\x83\xf8', offset) + 2 if offset1 < offset: offset1 = 99999 if offset2 < offset: offset2 = 99999 if offset3 < offset: offset3 = 99999 offsets = [offset1, offset2, offset3] flg = -1 offset = 99999 for j in range(3): if offsets[j] < offset: flg = j offset = offsets[j] if flg == 2: flg = 1 if flg == 0: slv.add(x[i] == level6[offset]) else: slv.add(x[i] + x[i + 1] == level6[offset]) r = slv.check() assert r == sat m = slv.model() flag = '' for i in range(16): flag += chr(m[x[i]].as_long()) data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### get flag #### for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
Welcome to the speedrun challenge! You have 30 minutes to solve 5 levels! Level 1, here is the binary! b'f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAcBAAAAAAAABAAAAAAAAAACA... : : Level 6, here is the binary! 4 6 3 2 b'f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAYBAAAAAAAABAAAAAAAAAAOg... What is the flag? k8aQtg3FZkTy6e0q Congrats! Here is your flag! flag{Human_or_r0b0t_1dk}
flag{Human_or_r0b0t_1dk}
Speed-Rev: Bots (rev)
Speed-Rev: Humans と同じスクリプトで接続先だけ変更する。
#!/usr/bin/env python3 import socket from base64 import * from z3 import * def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('cha.hackpack.club', 41702)) data = recvuntil(s, b'\n').rstrip() print(data) #### Level 1 #### for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data) level1 = b64decode(eval(data)) flag = level1[0x2004:0x2014].decode() data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### Level 2 #### for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data) level2 = b64decode(eval(data)) flag = '' offset = 0x1155 for i in range(16): flag += chr(level2[offset]) if i < 10: offset += 25 else: offset += 22 data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### Level 3 #### for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data) level3 = b64decode(eval(data)) flag = '' offset = 0x1155 for i in range(16): flag += chr(level3[offset]) if i < 10: offset += 25 else: offset += 22 data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### Level 4 #### for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data) level4 = b64decode(eval(data)) adds = [] offset = level4.index(b'\x48\x8b\x45\xf8\x0f\xb6\x00') for i in range(15): offset1 = level4.find(b'\x3d', offset) + 1 offset2 = level4.find(b'\x83\xf8', offset) + 2 if min(offset1, offset2) > offset: offset = min(offset1, offset2) else: offset = max(offset1, offset2) adds.append(level4[offset]) for f0 in range(48, 123): flag = chr(f0) error = False for i in range(15): code = adds[i] - ord(flag[-1]) if code < 0: error = True break flag += chr(code) if error: continue if flag.isalnum(): break data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### Level 5 #### for _ in range(7): data = recvuntil(s, b'\n').rstrip() print(data) level5 = b64decode(eval(data)) x = [BitVec('x%d' % i, 8) for i in range(16)] slv = Solver() for i in range(16): slv.add(Or(And(x[i] > 47, x[i] < 58), And(x[i] > 64, x[i] < 91), And(x[i] > 96, x[i] < 123))) offset = level5.index(b'\x48\x8b\x45\xf8\x0f\xb6\x00') for i in range(15): offset1 = level5.find(b'\x3c', offset) + 1 offset2 = level5.find(b'\x3d', offset) + 1 offset3 = level5.find(b'\x83\xf8', offset) + 2 if offset1 < offset: offset1 = 99999 if offset2 < offset: offset2 = 99999 if offset3 < offset: offset3 = 99999 offsets = [offset1, offset2, offset3] flg = -1 offset = 99999 for j in range(3): if offsets[j] < offset: flg = j offset = offsets[j] if flg == 2: flg = 1 if flg == 0: slv.add(x[i] == level5[offset]) else: slv.add(x[i] + x[i + 1] == level5[offset]) r = slv.check() assert r == sat m = slv.model() flag = '' for i in range(16): flag += chr(m[x[i]].as_long()) data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### Level 6 #### for _ in range(6): data = recvuntil(s, b'\n').rstrip() print(data) level6 = b64decode(eval(data)) x = [BitVec('x%d' % i, 8) for i in range(16)] slv = Solver() for i in range(16): slv.add(Or(And(x[i] > 47, x[i] < 58), And(x[i] > 64, x[i] < 91), And(x[i] > 96, x[i] < 123))) offset = level6.index(b'\x48\x8b\x45\xf8\x0f\xb6\x00') for i in range(15): offset1 = level6.find(b'\x3c', offset) + 1 offset2 = level6.find(b'\x3d', offset) + 1 offset3 = level6.find(b'\x83\xf8', offset) + 2 if offset1 < offset: offset1 = 99999 if offset2 < offset: offset2 = 99999 if offset3 < offset: offset3 = 99999 offsets = [offset1, offset2, offset3] flg = -1 offset = 99999 for j in range(3): if offsets[j] < offset: flg = j offset = offsets[j] if flg == 2: flg = 1 if flg == 0: slv.add(x[i] == level6[offset]) else: slv.add(x[i] + x[i + 1] == level6[offset]) r = slv.check() assert r == sat m = slv.model() flag = '' for i in range(16): flag += chr(m[x[i]].as_long()) data = recvuntil(s, b'\n').rstrip() print(data) print(flag) s.sendall(flag.encode() + b'\n') #### get flag #### for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
Welcome to the speedrun challenge! You have 3 minutes to solve 6 levels! Level 1, here is the binary! b'f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAcBAAAAAAA... : : Level 6, here is the binary! 2 3 4 6 b'f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAYBAAAAAAA... What is the flag? qb9IfqStzAvyHlGg Congrats! Here is your flag! flag{speedruns_are_aw3s0m3_4nd_4ll}
flag{speedruns_are_aw3s0m3_4nd_4ll}