この大会は2023/9/24 1:00(JST)~2023/9/25 1:00(JST)に開催されました。
今回もチームで参戦。結果は2033点で600チーム中60位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity Check (Web)
Webページで右クリックやF12キーは効かない。
$ curl https://challs.vsc.tf/sanity-check <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Sanity Check</title> <style> body { background-color: #000021; font-family: "JetBrains Mono", monospace; } h1 { background: linear-gradient(to right, #fe5d00, #fe8c00); background-clip: text; -webkit-background-clip: text; color: transparent; text-align: center; } .centered { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } </style> <script> setInterval("debugger", 10) </script> </head> <body oncontextmenu="return false"> <script> document.addEventListener('contextmenu', (e) => { e.preventDefault(); } ); document.onkeydown = function (e) { if (event.keyCode == 123) { return false; } if (e.ctrlKey && e.shiftKey && e.keyCode == 'I'.charCodeAt(0)) { return false; } if (e.ctrlKey && e.shiftKey && e.keyCode == 'C'.charCodeAt(0)) { return false; } if (e.ctrlKey && e.shiftKey && e.keyCode == 'J'.charCodeAt(0)) { return false; } if (e.ctrlKey && e.keyCode == 'U'.charCodeAt(0)) { return false; } } </script> <div class="centered"> <h1>you know what to do.</h1> <!-- vsctf{c0ngratulati0ns_y0u_viewed_the_s0urc3!...welcome_to_vsctf!} --> </div> </body> </html>
vsctf{c0ngratulati0ns_y0u_viewed_the_s0urc3!...welcome_to_vsctf!}
Discord (Misc)
Discordに入り、#announcementsチャネルのメッセージを見ると、フラグが書いてあった。
vsctf{w3lc0m3_t0_vsctf_2023!}
x0rr3al?!! (Reverse)
Ghidraでデコンパイルする。
undefined8 FUN_001019a4(void) { char cVar1; int iVar2; int iVar3; long lVar4; size_t sVar5; long in_FS_OFFSET; int local_58; char local_48 [56]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); iVar2 = FUN_001017a4(); if (iVar2 != 0) { FUN_001016ad(); /* WARNING: Subroutine does not return */ exit(1); } lVar4 = ptrace(PTRACE_TRACEME,0,1,0); if (lVar4 == -1) { FUN_001016ad(); /* WARNING: Subroutine does not return */ exit(1); } iVar2 = FUN_00101483(FUN_001019a4,0x200); FUN_00101355(); FUN_001013f8(); iVar3 = FUN_00101483(FUN_001019a4,0x200); if (iVar2 != iVar3) { FUN_001016ad(); /* WARNING: Subroutine does not return */ exit(1); } printf("p4ss m3 th3 fl4g: "); __isoc99_scanf(&DAT_0010205d,local_48); sVar5 = strlen(local_48); if (sVar5 == 0x35) { for (local_58 = 0; local_58 < 0x35; local_58 = local_58 + 1) { cVar1 = FUN_0010150a((int)local_48[local_58],0); iVar3 = FUN_001014f7((int)cVar1); if (iVar3 != *(int *)(&DAT_001040a0 + (long)local_58 * 4)) { FUN_0010156a(); goto LAB_00101b6c; } iVar3 = FUN_00101483(FUN_001019a4,0x200); if (iVar2 != iVar3) { FUN_001016ad(); /* WARNING: Subroutine does not return */ exit(1); } } FUN_00101614(); } else { FUN_0010156a(); } LAB_00101b6c: if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; } byte FUN_0010150a(byte param_1,int param_2) { if (param_2 < 4) { param_1 = FUN_0010150a((int)(char)(param_1 ^ (&DAT_00104180)[(long)param_2 * 0xb]),param_2 + 1); } return param_1; } uint FUN_001014f7(uint param_1) { return param_1 ^ 0x12; } void FUN_001013f8(void) { int local_10; int local_c; for (local_10 = 0; local_10 < 0x1a; local_10 = local_10 + 1) { *(int *)(&DAT_001040a0 + (long)local_10 * 4) = (int)(char)(&DAT_00104040)[local_10]; } for (local_c = 0; local_c < 0x1b; local_c = local_c + 1) { *(int *)(&DAT_001040a0 + (long)(local_c + 0x1a) * 4) = (int)(char)(&DAT_00104060)[local_c]; } return; } DAT_00104040 XREF[2]: FUN_001013f8:0010140e(*), FUN_001013f8:00101415(*) 00104040 7e ?? 7Eh ~ 00104041 7b ?? 7Bh { 00104042 6b ?? 6Bh k 00104043 7c ?? 7Ch | 00104044 6e ?? 6Eh n 00104045 73 ?? 73h s 00104046 7f ?? 7Fh 00104047 3b ?? 3Bh ; 00104048 3c ?? 3Ch < 00104049 63 ?? 63h c 0010404a 57 ?? 57h W 0010404b 3c ?? 3Ch < 0010404c 66 ?? 66h f 0010404d 7c ?? 7Ch | 0010404e 39 ?? 39h 9 0010404f 57 ?? 57h W 00104050 6c ?? 6Ch l 00104051 3b ?? 3Bh ; 00104052 6a ?? 6Ah j 00104053 7d ?? 7Dh } 00104054 6f ?? 6Fh o 00104055 6f ?? 6Fh o 00104056 3b ?? 3Bh ; 00104057 7a ?? 7Ah z 00104058 7b ?? 7Bh { 00104059 57 ?? 57h W 0010405a 00 ?? 00h 0010405b 00 ?? 00h 0010405c 00 ?? 00h 0010405d 00 ?? 00h 0010405e 00 ?? 00h 0010405f 00 ?? 00h DAT_00104060 XREF[2]: FUN_001013f8:0010144c(*), FUN_001013f8:00101453(*) 00104060 3c ?? 3Ch < 00104061 7a ?? 7Ah z 00104062 3b ?? 3Bh ; 00104063 57 ?? 57h W 00104064 66 ?? 66h f 00104065 38 ?? 38h 8 00104066 57 ?? 57h W 00104067 65 ?? 65h e 00104068 3c ?? 3Ch < 00104069 7c ?? 7Ch | 0010406a 6b ?? 6Bh k 0010406b 60 ?? 60h ` 0010406c 57 ?? 57h W 0010406d 6e ?? 6Eh n 0010406e 38 ?? 38h 8 0010406f 7a ?? 7Ah z 00104070 57 ?? 57h W 00104071 7c ?? 7Ch | 00104072 60 ?? 60h ` 00104073 3b ?? 3Bh ; 00104074 57 ?? 57h W 00104075 3b ?? 3Bh ; 00104076 39 ?? 39h 9 00104077 3b ?? 3Bh ; 00104078 3b ?? 3Bh ; 00104079 3f ?? 3Fh ? 0010407a 75 ?? 75h u void FUN_00101355(void) { strcpy(&DAT_00104180,&DAT_00104010); strcat(&DAT_00104180,&DAT_00104020); strcpy(&DAT_0010418a,&DAT_00104015); strcat(&DAT_0010418a,s_fvsctiamfrnow0kkeyw0wkeyw_00104025); strcpy(&DAT_00104194,s_fvsctiamfrnow0kkeyw0wkeyw_00104025 + 5); strcat(&DAT_00104194,s_fvsctiamfrnow0kkeyw0wkeyw_00104025 + 10); strcpy(&DAT_0010419e,s_fvsctiamfrnow0kkeyw0wkeyw_00104025 + 0xf); strcat(&DAT_0010419e,s_fvsctiamfrnow0kkeyw0wkeyw_00104025 + 0x14); return; }
データ領域のデータをFUN_00101355に従って、書いてみる。
・2行目まで 00104180 s3cRvsct3ts3 ・4行目まで 00104180 s3cRvsct3tvsctfv 00104190 sctiamfrnow0kkey 001041a0 w0wkeyw ・6行目まで 00104180 s3cRvsct3tvsctfv 00104190 sctiiamfrnow0kke 001041a0 yw0wkeywnow0kkey 001041b0 w0wkeyw ・8行目まで 00104180 s3cRvsct3tvsctfv 00104190 sctiiamfrnow0kke 001041a0 yw0wkeywwkeyw\0ey 001041b0 w0wkeyw
入力文字0文字目の場合、以下のような処理をしている。
・cVar1 = FUN_0010150a((int)local_48[0],0) ・param_1 = FUN_0010150a((int)(char)((int)local_48[0] ^ (&DAT_00104180)[0]),1) ・param_1 = FUN_0010150a((int)(char)((int)local_48[0] ^ (&DAT_00104180)[0]) ^ (&DAT_00104180)[11]),2) ・param_1 = FUN_0010150a((int)(char)((int)local_48[0] ^ (&DAT_00104180)[0]) ^ (&DAT_00104180)[11]) ^ (&DAT_00104180)[22]),3) ・param_1 = FUN_0010150a((int)(char)((int)local_48[0] ^ (&DAT_00104180)[0]) ^ (&DAT_00104180)[11]) ^ (&DAT_00104180)[22]) ^ (&DAT_00104180)[33]),4) ・cVar1 =param_1 ・iVar3 = FUN_001014f7((int)cVar1) ・iVar3 = cVar1 ^ 0x12 ・001040a0の先頭と比較
これを元に入力文字列を割り出す。
#!/usr/bin/env python3 data = b's3cRvsct3tvsctfvsctiiamfrnow0kkeyw0wkeywwkeyw\x00eyw0wkeyw' target = b'~{k|ns\x7f;<cW<f|9Wl;j}oo;z{W<z;Wf8We<|k`Wn8zW|`;W;9;;?u' flag = '' for i in range(0x35): v = target[i] ^ 0x12 c = v ^ data[0] ^ data[11] ^ data[22] ^ data[33] flag += chr(c) print(flag)
vsctf{w34k_4nt1_d3bugg3rs_4r3_n0_m4tch_f0r_th3_31337}
Redundancy (Crypto)
RSA暗号。2つのe(GCDは5)でフラグを含むメッセージが暗号がされている。Common Modulus Attackでメッセージの5乗の値を復号する。
flagの"{}"の中以外のメッセージがわかっており、"{}"の中は16バイト未満であることから、"{}"の中の長さを総当たりしてCoppersmithの定理で復号する。
#!/usr/bin/env sage import gmpy2 from Crypto.Util.number import * def commom_modulus_attack(c1, c2, e1, e2, n): gcd, s1, s2 = gmpy2.gcdext(e1, e2) if s1 < 0: s1 = -s1 c1 = gmpy2.invert(c1, n) elif s2 < 0: s2 = -s2 c2 = gmpy2.invert(c2, n) v = pow(c1, s1, n) w = pow(c2, s2, n) x = (v*w) % n return int(x) with open('output.txt', 'r') as f: params = f.read().splitlines() n = int(params[0].split(' ')[-1]) c1 = int(params[1].split(' ')[-1]) c2 = int(params[2].split(' ')[-1]) e1 = 2 e2 = 3 _m = commom_modulus_attack(c1, c2, e1, e2, n) e = 5 for l in range(1, 16): mbar = "Wow good job the flag is (omg hype hype): vsctf{" + "\x00" * l +"}" mbar = int(bytes_to_long(mbar.encode())) PR.<x> = PolynomialRing(Zmod(n)) f = (mbar + x)^e - _m try: x0 = f.small_roots(X=256^(l + 1), beta=1)[0] break except: continue m = int(mbar + x0) msg = long_to_bytes(m).decode() print(msg)
復号結果は以下の通り。
Wow good job the flag is (omg hype hype): vsctf{WE<3COPPERSMITH}
vsctf{WE<3COPPERSMITH}