この大会は2023/5/6 0:00(JST)~2023/5/7 0:00(JST)に開催されました。
今回もチームで参戦。結果は522点で501チーム中125位でした。
自分で解けた問題をWriteupとして書いておきます。
Discord Rules (Other)
Discordに入り、#règlesチャネルのルールを見ると、フラグが書いてあった。
PWNME{G0od_LucK_4_PwnME_V2_CTF!!}
C Stands For C (Reverse)
Ghidraでデコンパイルする。
void main(void) { int iVar1; char *__s1; long in_FS_OFFSET; char local_58 [72]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); puts("Hi, please provide the password:"); fgets(local_58,0x40,stdin); __s1 = (char *)caesarCipher(local_58,0x40); iVar1 = strcmp(__s1,"JQHGY{Qbs_x1x_S0o_f00E_b3l3???y65zx03}\n"); if (iVar1 == 0) { puts("Welcome to the shop."); } else { puts("Who are you? What is your purpose here?"); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return; }
以下の文字列をシーザー暗号として復号すればよい。
JQHGY{Qbs_x1x_S0o_f00E_b3l3???y65zx03}
https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
Rotation 20: PWNME{Why_d1d_Y0u_l00K_h3r3???e65fd03}
PWNME{Why_d1d_Y0u_l00K_h3r3???e65fd03}
Xoxor (Reverse)
Ghidraでデコンパイルする。
void FUN_00101268(void) { int iVar1; size_t sVar2; size_t sVar3; char *__s2; long in_FS_OFFSET; char local_118 [264]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); sVar2 = strlen("aezx$K+`mcwL<+_3/0S^84B^V8}~8\\TXWmnmFP_@T^RTJ"); sVar3 = strlen("1245a0eP2475cr0Fpsg0grs02g0Mg4g02LOLg5gs2g0g7"); __s2 = (char *)FUN_001011e9("aezx$K+`mcwL<+_3/0S^84B^V8}~8\\TXWmnmFP_@T^RTJ", "1245a0eP2475cr0Fpsg0grs02g0Mg4g02LOLg5gs2g0g7",sVar2 & 0xffffffff, sVar3 & 0xffffffff); puts( "Hello! In order to access the shopping panel, please insert the password and do not cheat thi s time:" ); fgets(local_118,0xff,stdin); sVar2 = strlen(local_118); local_118[(int)sVar2 + -1] = '\0'; iVar1 = strcmp(local_118,__s2); if (iVar1 == 0) { puts("Welcome, you now have access to the shopping panel."); } else { puts("Please excuse us, only authorized persons can access this panel."); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return; } void * FUN_001011e9(long param_1,long param_2,int param_3,int param_4) { void *pvVar1; int local_14; pvVar1 = malloc((long)param_3); for (local_14 = 0; local_14 < param_3; local_14 = local_14 + 1) { *(byte *)((long)pvVar1 + (long)local_14) = *(byte *)(param_1 + local_14) ^ *(byte *)(param_2 + local_14 % param_4); } return pvVar1; }
2つの文字列のXORを取ればよい。
>>> from Crypto.Util.strxor import * >>> s1 = b"aezx$K+`mcwL<+_3/0S^84B^V8}~8\\TXWmnmFP_@T^RTJ" >>> s2 = b"1245a0eP2475cr0Fpsg0grs02g0Mg4g02LOLg5gs2g0g7" >>> strxor(s1, s2) b'PWNME{N0_W@y_You_C4n_F1nd_M3_h3he!!!!e83f9b3}'
PWNME{N0_W@y_You_C4n_F1nd_M3_h3he!!!!e83f9b3}
Tree Viewer (Web)
Sourceは以下のようになっている。
<?php $parsed = isset($_POST['input']) ? $_POST['input'] : "/home/"; preg_match_all('/[;|]/m', $parsed, $illegals, PREG_SET_ORDER, 0); if($illegals){ echo "Illegals chars found"; $parsed = "/home/"; } if(isset($_GET['source'])){ highlight_file(__FILE__); } ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Tree Viewer</title> </head> <body> <a href="/?source">Source code</a> <hr/> <form action="/" method="post"> <label for="input">Directory to check</label> <input type="text" placeholder="Directory to see" id="input" name="input" value="<?= $parsed ?>"> </form> <h3>Content of <?= $parsed ?>: <?= shell_exec('ls '.$parsed); ?></h3> </body> </html>
";"や"|"は使用できないので、以下を入力し、flag.txtの内容を読み取る。
&& cat /home/flag.txt
結果は以下の通りで、フラグが読み取れた。
Content of && cat /home/flag.txt: index.php Flag: PWNME{U53R_1NpU75_1n_5h3lL_3x3c_37}
PWNME{U53R_1NpU75_1n_5h3lL_3x3c_37}
kNOCk kNOCk (Forensic)
"http"でフィルタリングする。MalPack.debを受信しているので、エクスポートする。
MalPack.debを解凍すると、simplescript.shが展開される。このファイルには以下のように書かれていて、フラグが得られる。
#!/bin/bash echo "PWNME{P4ck4g3_1s_g00d_ID}"
PWNME{P4ck4g3_1s_g00d_ID}
Just a XOR (Crypto)
暗号化処理の概要は以下の通り。
・MESSAGE: original-message.txtの内容 ・SECRET: 0以上0x2600以下のランダム整数を文字化したものの16個の配列 ・ret = encrypt(MESSAGE) ・MESSAGEとSECRET(繰り返し)のXORの数値を文字列化したものの配列を返却 ・retを","区切りで出力
平文で分かっている部分があるので、16バイトごとのブロックに分けて、SECRETを割り出す。あとは割り出したSECRETで復号すればよい。
#!/ujsr/bin/env python3 with open('intercepted-original-mesage.txt', 'r') as f: pt_masked = f.read() with open('message-encrypted.txt', 'r') as f: ct = list(map(int, f.read().split(','))) SECRET = [0] * 16 for i in range(len(pt_masked)): if pt_masked[i] != '*': SECRET[i % 16] = ord(pt_masked[i]) ^ ct[i] pt = '' for i in range(len(ct)): pt += chr(ct[i] ^ SECRET[i % 16]) print(pt)
復号結果は以下の通り。
So, I can see you know how XOR works.. Congratulation :) Here is your flag: PWNME{1t_W4s_r34aLy_Ju3s7_A_x0R} ! Good luck for the next challenges
PWNME{1t_W4s_r34aLy_Ju3s7_A_x0R}
Gib me Initials of Victor (Crypto)
暗号化処理の概要は以下の通り。
・KEY: 16バイトランダム文字列 ・iv: 16バイトランダム文字列 ・encrypted: FLAGをパディングし、AES-CBC暗号化 ・signature: ivとKEYの逆順のXORをhexエンコードしたもの ・ciphertext: ivの16進数表記の4バイト目以降 + encryptedの16進数表記 + signature ・ciphertextを出力
ivがわかれば、signatureからKEYを算出することができる。ivの16進数表記の先頭4バイトが不明なので、ブルートフォースする。
#!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Util.Padding import unpad from Crypto.Util.strxor import strxor with open('output.txt', 'r') as f: output = eval(f.read()) ciphertext = output['ciphertext'] iv_tail = bytes.fromhex(ciphertext[:28]) encrypted = bytes.fromhex(ciphertext[28:-32]) signature = bytes.fromhex(ciphertext[-32:]) found = False for iv0 in range(256): for iv1 in range(256): iv = bytes([iv0, iv1]) + iv_tail KEY = strxor(iv, signature)[::-1] cipher = AES.new(KEY, AES.MODE_CBC, iv) FLAG = cipher.decrypt(encrypted) if FLAG.startswith(b'PWNME{'): found = True FLAG = unpad(FLAG, 16).decode() print(FLAG) break if found: break
PWNME{1t_1s_d4ng3r0us_wh3n_t0_m4ny_1nf0_4r3_g1v3n}
Scream Like Viking (Crypto)
サーバ処理の概要は以下の通り。
・e = 17 ・p: 512ビット素数 ・q: 512ビット素数 ・N = p * q ・以下繰り返し ・cmd: 入力 ・cmdが"Encrypt"の場合 ・m: 数値入力 ・c = encrypt(m) ・m1: mを文字列化し、パディング後、数値化したもの ・c = pow(m1, e, N) ・cを返却 ・cを表示 ・cmdが"Flag"の場合 ・c = encrypt_flag() ・m1: flagをパディング後、数値化したもの ・c = pow(m1, e, N) ・cを返却 ・cを表示 ・終了
まずNを求めたい。
mに0x0101...01(01を49個)の10進数を指定すると、パディングした後の数値は0x01...01(01を50個)となる。
mに0x0202...02(02を48個)の10進数を指定すると、パディングした後の数値は0x02...02(02を50個)となる。
mに0x0404...04(04を46個)の10進数を指定すると、パディングした後の数値は0x04...04(04を50個)となる。
mに0x1010...10(10を34個)の10進数を指定すると、パディングした後の数値は0x10...10(10を50個)となる。
上記のそれぞれの暗号化したものをc0, c1, c2, c3とすると、以下の式が成り立つ。
c0 = pow(m0, e, N) c1 = pow(m1, e, N) = pow(m0 * 2, e, N) = pow(m0, e, N) * pow(2, e, N) % N c2 = pow(m2, e, N) = pow(m0 * 4, e, N) = pow(m0, e, N) * pow(4, e, N) % N c3 = pow(m3, e, N) = pow(m0 * 16, e, N) = pow(m0, e, N) * pow(16, e, N) % N c1 ** 2 = pow(m0, e, N) ** 2 * pow(4, e, N) % N = c2 * c0 c2 ** 2 = pow(m0, e, N) ** 2 * pow(16, e, N) % N = c3 * c0 (c1 ** 2 - c0 * c2) % N = 0 (c2 ** 2 - c0 * c3) % N = 0
このことから以下を計算し、Nを割り出す。
GCD(c1 ** 2 - c0 * c2, c2 ** 2 - c0 * c3)
あとは17個のフラグのNとcの情報を集め、Hastad's Broadcast Attackで復号する。
#!/usr/bin/env python3 import socket from Crypto.Util.number import * from sympy.ntheory.modular import crt from gmpy2 import iroot def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) e = 17 ms = [] ms.append(int('01' * (50 - 1), 16)) ms.append(int('02' * (50 - 2), 16)) ms.append(int('04' * (50 - 4), 16)) ms.append(int('10' * (50 - 16), 16)) ns = [] cs = [] for i in range(e): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('13.37.17.31', 50826)) try_rsa_enc = [] for m in ms: data = recvuntil(s, b'> ') print(data + 'Encrypt') s.sendall(b'Encrypt\n') data = recvuntil(s, b'> ') print(data + str(m)) s.sendall(str(m).encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) try_rsa_enc.append(int(data)) diff1 = (try_rsa_enc[1]) ** 2 - try_rsa_enc[0] * try_rsa_enc[2] diff2 = (try_rsa_enc[2]) ** 2 - try_rsa_enc[0] * try_rsa_enc[3] N = GCD(diff1, diff2) for j in range(128, 1, -1): if N % j == 0: N = N // j ns.append(N) data = recvuntil(s, b'> ') print(data + 'Flag') s.sendall(b'Flag\n') data = recvuntil(s, b'\n').rstrip() print(data) c = int(data.split(' ')[-1]) cs.append(c) data = recvuntil(s, b'\n').rstrip() print(data) s.close() me, _ = crt(ns, cs) m, success = iroot(me, e) assert success flag = long_to_bytes(m).decode() print(flag)
実行結果は以下の通り。
: : Enter your option (Encrypt or Flag) > Encrypt Enter your integer to encrypt > 39556523867752888934680134375046137780785934326428056262724561221517273621581513398125694750002111388417634374582529 29633761006210799671079900862868953846620965510698716181289191451019372254407607821971507525514615344143220143236842405676330342078065560317144573874568182068936309264043154036785015344167898045818780966096895947373587419825710039031260032556421169844324882703080060884164900140948076666108490483898383157035 Enter your option (Encrypt or Flag) > Encrypt Enter your integer to encrypt > 309035342716819444802188549805047951412390111925219189552535634543103700168605573422856990234391495222012768551426 128083558475595433415966339646450338708213951915470947601427400794294574289697675308358729608310777818618335423067966360123927121562309003166598342100173291500854368152675807228383806204106364006051875366526471632927753790741727626397147873607563634083318236457492180334993861720119042978695226755613171841602 Enter your option (Encrypt or Flag) > Encrypt Enter your integer to encrypt > 9431010214746687158269914239656004376598819333655370774918690019015615849871996259242461860180404517273338884 2030173969955998081047276642914807292555882299524834822202648251388504491525242385512647350712654076305667498660162407487840741496752398157554477563259579760584158655906716712867185096760496244032932687904717683507453805445886480728088846392943252050219859343817907066007948513007010821736453651569663353629 Enter your option (Encrypt or Flag) > Encrypt Enter your integer to encrypt > 476144336329835556597907330103803653588825205650034196385248139641888305197355024 36867698018653225660646796911813096513896552187458954348668042407946732181664035935437205393530090513662633219424058272867059565745299990599147729704345702304350390563126649556411649086879518321648929149041447462918839877819645544933595122529548434389253294655502120212642876476616254917502272772746426078006 Enter your option (Encrypt or Flag) > Flag Flag cipher for you: 118178640531120002813489649187374749396638007537617761680519449467109891593676428105297803624604678778592660363014634306482278073291516748497375553282217093906961754170402445206627547551241543806364181800665131288734507203106000004949400678755865538501950878684696369241559600655381680349235109535121459317355 Enter your option (Encrypt or Flag) > Encrypt Enter your integer to encrypt > 39556523867752888934680134375046137780785934326428056262724561221517273621581513398125694750002111388417634374582529 24988348200700366323117331723978733262721034978344586088372494326201123568560953516130828633042178543713642534675295499438192580118342624158503119444269878625514653717174867128212085236268025572978484482171050294699385917331799042490755772864997744492751621263236745806598516963660294653279374429712734244518 Enter your option (Encrypt or Flag) > Encrypt Enter your integer to encrypt > 309035342716819444802188549805047951412390111925219189552535634543103700168605573422856990234391495222012768551426 64394564090285413077281557852248232558899594226504285011793151738285962134497528830441168301671047784664825529783442285883501449276313100251860086916081438650640789887493753001570711468406048883106582871302353293801375621518446347638484703292748265446044394663116529045892937120454887377096415160485307705185 Enter your option (Encrypt or Flag) > Encrypt Enter your integer to encrypt > 9431010214746687158269914239656004376598819333655370774918690019015615849871996259242461860180404517273338884 64420705452562595765855601654000387350221282257377887299060920348705624231173202829434728146419025689231709002098310921491910890742357255443517035631942672802851761459446697054291992336714690905853946998470646456310593356104339713520143262501950149881425736889256304366005014728208256404814923236934188623511 Enter your option (Encrypt or Flag) > Encrypt Enter your integer to encrypt > 476144336329835556597907330103803653588825205650034196385248139641888305197355024 10229535008043355337839552369745908652789428345920492252742367878711614571772336463643779901077048866699194393451649320412010939361176277448954629624176370164966569369369995979886953650786979385453526928436091310187924223208984516862802315958447416917418338253129225116143711690533304096251317775639628492354 Enter your option (Encrypt or Flag) > Flag Flag cipher for you: 72797884866378036732723267345532859660533267204876200124160446622417470035264689159495398403165727505001687803682459676198823846141415978216417430108587649011166743614922712595421623389546905713366101335904154156553764428130057629805301964988887571187976406622888492362055556293392891767112707540525469119418 PWNME{h4st4rd_br0adc4st_d3str0y_s1mple_p4dd1ng}
PWNME{h4st4rd_br0adc4st_d3str0y_s1mple_p4dd1ng}