この大会は2023/4/1 23:00(JST)~2023/4/3 5:00(JST)に開催されました。
今回もチームで参戦。結果は4250点で237チーム中13位でした。
自分で解けた問題をWriteupとして書いておきます。
Approved ! (MISC)
CTFTIMEでイベントの情報を見ると、以下のように書いてある。
A CTF from team Bits & Pieces, BITS Pilani. It is aimed for beginners but anyone can participate. Hope you enjoy it. 475245507b57336c63306d655f74305f475233505f4354467d
hexエンコード文字列があるので、hexデコードする。
$ echo 475245507b57336c63306d655f74305f475233505f4354467d | xxd -r -p GREP{W3lc0me_t0_GR3P_CTF}
Layouts (MISC)
QWERTYキーボードのつもりでWorkmanキーボード配列のキーでタイプした模様。対応する文字に置換する。
#!/usr/bin/env python3 with open('msg.txt', 'r') as f: enc = f.read() P = 'qwertyuiopasdfghjkl;zxcvbnmWYUIFHN' C = 'qdrwbjfup;ashtgyneoizxmcvklDJFUTYK' out = '' for c in enc: if c in ' .,\'-_1234567890\n': out += c elif c in C: out += P[C.index(c)] else: out += '?' print(out)
実行結果は以下の通り。
Uh, summa-lumma, dooma-lumma, you assumin' I'm a human What I gotta do to get it through to you I'm superhuman?? Innovative and I'm made of rubber so that anything You say is ricochetin' off of me, and it'll glue to you and I'm devastating, more than ever demonstrating How to give a motherfuckin' audience a feeling like it's levitating Never fading, and I know the haters are forever waiting For the day that they can say I fell off, they'll be celebrating Your flag is r4pg0d_em1n3m_3256gd62 in the usual format
grepCTF{r4pg0d_em1n3m_3256gd62}
Lost Card (MISC)
カード番号が以下のように書いてあるが、2桁が不明。
538XX10365956729
Luhnアルゴリズムによりチェックし、適合するものを探す。
#!/usr/bin/env python3 def check_number(digits): _sum = 0 alt = False if digits[0] == "0": return False for d in reversed(digits): d = int(d) assert 0 <= d <= 9 if alt: d *= 2 if d > 9: d -= 9 _sum += d alt = not alt return (_sum % 10) == 0 for i in range(10): for j in range(10): number = '538%d%d10365956729' % (i, j) if check_number(number): print(number)
10パターン出てくる。
5380010365956729 5381910365956729 5382410365956729 5383810365956729 5384310365956729 5385710365956729 5386210365956729 5387610365956729 5388110365956729 5389510365956729
順にフラグとして投入してみる。
GREP{5380010365956729} → NG GREP{5381910365956729} → NG GREP{5382410365956729} → NG GREP{5383810365956729} → NG GREP{5384310365956729} → NG GREP{5385710365956729} → NG GREP{5386210365956729} → NG GREP{5387610365956729} → NG GREP{5388110365956729} → OK
GREP{5388110365956729}
Consensual Non Consent (MISC)
GCodeで書かれているようだ。以下のURLのオンラインのNC viewerで開くと、フラグが見える。
https://ncviewer.com/
grepCTF{w0rksh0p_pract1ce_b3st_c0urs3}
Simple rev (REVERSE ENGINEERING)
$ strings outfile | grep grepCTF grepCTF{4p0g33_h1vem1nd_g3n3s1s}
grepCTF{4p0g33_h1vem1nd_g3n3s1s}
EXORcist (REVERSE ENGINEERING)
Ghidraでデコンパイルする。
undefined8 main(void) { int iVar1; long in_FS_OFFSET; int local_40; byte local_38 [40]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); printf("Enter the flag: "); __isoc99_scanf(&DAT_00102015,local_38); for (local_40 = 0; local_40 < 0x17; local_40 = local_40 + 1) { local_38[local_40] = (byte)local_40 ^ local_38[local_40]; } iVar1 = strcmp((char *)local_38,"gsgsGQ@|9gn8tRy>c\"Mk$gk"); if (iVar1 == 0) { puts("Correct flag!"); } else { puts("Wrong flag!"); } if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; }
各インデックスとXORして"gsgsGQ@|9gn8tRy>c\"Mk$gk"になるものがフラグ。
#!/usr/bin/env python3 enc = "gsgsGQ@|9gn8tRy>c\"Mk$gk" flag = '' for i in range(len(enc)): flag += chr(i ^ ord(enc[i])) print(flag)
grepCTF{1nd3x_w1s3_x0r}
Monke (FORENSICS)
jpgの後ろにbase64文字列らしきものがあるので、デコードする。
$ echo Z3JlcENURntyM2ozY3RfaHVtNG4xdHlfZzBfYjRja190MF9tMG5rM30K | base64 -d grepCTF{r3j3ct_hum4n1ty_g0_b4ck_t0_m0nk3}
grepCTF{r3j3ct_hum4n1ty_g0_b4ck_t0_m0nk3}
IronMan (FORENSICS)
StegSolveで開き、[Analyse]-[Data Extact]の画面で、RGBのLSBにチェックを付ける。さらに、Extract Byで[Column]にし、列方向でLSBを読むよう設定し、Previewすると、フラグが表示された。
grepCTF{i_d0n't_f3el_s0_g00d}
R36 (FORENSICS)
sstvでデコードしてみる。
$ sstv -d r36.wav -o flag.png [sstv] Searching for calibration header... Found! [sstv] Detected SSTV mode Robot 36 [sstv] Decoding image... [#################################################] 100% [sstv] Drawing image data... [sstv] ...Done!
出力した画像にフラグが書いてあった。
grepCTF{psych3d3l1c_fr0g}
Royal Steg (FORENSICS)
$ stegseek steg.jpg dict/rockyou.txt StegSeek 0.6 - https://github.com/RickdeJager/StegSeek [i] Found passphrase: "cuteessort37" [i] Original filename: "orig.zip". [i] Extracting to "steg.jpg.out". $ file steg.jpg.out steg.jpg.out: Zip archive data, at least v1.0 to extract, compression method=store $ mv steg.jpg.out steg.zip
抽出したファイルはzipファイルでパスワードがかかっている。
$ fcrackzip -u -D -p dict/rockyou.txt steg.zip PASSWORD FOUND!!!!: pw == jesuslove
このパスワードで解凍・展開する。
$ unzip steg.zip Archive: steg.zip [steg.zip] flag.txt password: extracting: flag.txt $ cat flag.txt grepCTF{tw0_l3v3ls_0f_st3g}
grepCTF{tw0_l3v3ls_0f_st3g}
Last Seen Beauty (FORENSICS)
暗号化処理の概要は以下の通り。
・image: mem2.pngのイメージオブジェクト ・width, height: imageの幅、高さ ・img_array: イメージデータのリスト ・channels: 画像モードが"RGBA"の場合は4、そうでない場合は3 ・pixels: 画像のピクセルの数 ・stop_indicator = "$ckc$" ・stop_length: stop_indicatorの長さ ・messageにstop_indicatorを追加 ・byte_message: messageの2進数表記文字列 ・bits: byte_messageの長さ ・index = 0 ・bitsの長さの間、以下を実行 ・LSBに埋め込み ・encoded.pngに書き込み
画像を横方向でLSBを取得し、メッセージを取得する。
#!/usr/bin/env python3 from PIL import Image def bin_to_str(b): l = len(b) // 8 * 8 s = b[:l] s = ''.join([chr(int(s[i:i+8], 2)) for i in range(0, len(s), 8)]) return s stop_indicator='$ckc$' img = Image.open('encoded.png').convert('RGB') w, h = img.size bin_flag = '' final = False for y in range(h): for x in range(w): r, g, b = img.getpixel((x, y)) bin_flag += str(r & 1) bin_flag += str(g & 1) bin_flag += str(b & 1) flag = bin_to_str(bin_flag) if stop_indicator in flag: index = flag.index(stop_indicator) flag = flag[:index] print(flag) final = True break if final: break
取得したメッセージは以下の通り。
GVNJ{B1ur_c4u_f3a_lman}
元の画像のEXIF情報を見てみる。
$ exiftool encoded.png ExifTool Version Number : 12.40 File Name : encoded.png Directory : . File Size : 11 MiB File Modification Date/Time : 2023:04:02 23:23:28+09:00 File Access Date/Time : 2023:04:03 00:06:51+09:00 File Inode Change Date/Time : 2023:04:02 23:23:28+09:00 File Permissions : -rwxrwxrwx File Type : PNG File Type Extension : png MIME Type : image/png Image Width : 3456 Image Height : 5184 Bit Depth : 8 Color Type : RGB with Alpha Compression : Deflate/Inflate Filter : Adaptive Interlace : Noninterlaced Exif Byte Order : Big-endian (Motorola, MM) X Resolution : 72 Y Resolution : 72 Resolution Unit : inches Y Cb Cr Positioning : Centered Exif Version : 0232 Components Configuration : Y, Cb, Cr, - User Comment : Hint: Port Royal, SC Flashpix Version : 0100 Color Space : Uncalibrated Image Size : 3456x5184 Megapixels : 17.9
User Commentに「Hint: Port Royal, SC」と書いてある。Google mapで「Port Royal, SC」を調べると、以下のようなことが書かれていることがわかる。
Port Royal is a town on Port Royal Island in Beaufort County, South Carolina, United States.
Beaufort暗号が関係ありそう。https://www.boxentriq.com/code-breaking/beaufort-cipherで復号する。復号結果が"GREP"で始まるよう、鍵を操作し、"mmry"のときにフラグになった。
GREP{L1sa_w4s_h3r_name}
Missing Kitty (FORENSICS)
$ stegseek Missing.jpg dict/rockyou.txt StegSeek 0.6 - https://github.com/RickdeJager/StegSeek [i] Found passphrase: "kitty123" [i] Original filename: "secret.txt". [i] Extracting to "Missing.jpg.out". $ cat Missing.jpg.out Dk what's this, some kitten language memmemmmmeemmememeemeemmmeemeemmmeemeeeemmemmmmmmeemeememeeeemmemmemmmmmmeememeemeememmemeeememmmeeememmmeeeemmemmemeemmmmmmememmmmmememmemmmeemmeememmemeemeeemmeemmmmemeemeemmmeemeemmmeeeemmemmemmmmmmeeeemmemeemeeeemeeemememmemmmmmmeemmeemmeemeeeemeeemememeemeeemmeemmemmmmemmmmmmeememmmmeemmememeeemmemmmemeeemmmemmmmmmemmemmemmemmmmmmeemmmmemeemeememmemmmmmmeemmemmmeemmememeemeemmmeememmemeemmeeemeememmmmeeememmmeemmememeemmemmmmemeeemmmmmememmmmmememmemememmmeemmmmemeememeemeemmememmemmmmmmeememmemeeememmmmemeemmmmemmmmmmeememmmmeemmememeeemmemmeemmememmemmeeemeeemmeemmemmmmmmeeeemmemeemeeeemeeemememeeemmemmmemmmmmmeemmeeemeememmemeemmeemmeeememmmmmmememmeemmeemmeemeemmmeemmmmemeemmeeemmemmmmmmmeeememmmemmmmmmeeeemeemememmeemeeemeeemmeemmeemmeemmeemeeememmmemeeeeemeemeemmmmeemmmemeeememmmeeememmmeemeemmmeemmemememeeeeemeememeemmeemmmemeeememmmeeememmmmeemmeemeemeeemmeeeeeme
mを"0"、eを"1"として、2進数データをデコードする。
#!/usr/bin/env python3 enc = 'memmemmmmeemmememeemeemmmeemeemmmeemeeeemmemmmmmmeemeememeeeemmemmemmmmmmeememeemeememmemeeememmmeeememmmeeeemmemmemeemmmmmmememmmmmememmemmmeemmeememmemeemeeemmeemmmmemeemeemmmeemeemmmeeeemmemmemmmmmmeeeemmemeemeeeemeeemememmemmmmmmeemmeemmeemeeeemeeemememeemeeemmeemmemmmmemmmmmmeememmmmeemmememeeemmemmmemeeemmmemmmmmmemmemmemmemmmmmmeemmmmemeemeememmemmmmmmeemmemmmeemmememeemeemmmeememmemeemmeeemeememmmmeeememmmeemmememeemmemmmmemeeemmmmmememmmmmememmemememmmeemmmmemeememeemeemmememmemmmmmmeememmemeeememmmmemeemmmmemmmmmmeememmmmeemmememeeemmemmeemmememmemmeeemeeemmeemmemmmmmmeeeemmemeemeeeemeeemememeeemmemmmemmmmmmeemmeeemeememmemeemmeemmeeememmmmmmememmeemmeemmeemeemmmeemmmmemeemmeeemmemmmmmmmeeememmmemmmmmmeeeemeemememmeemeeemeeemmeemmeemmeemmeemeeememmmemeeeeemeemeemmmmeemmmemeeememmmeeememmmeemeemmmeemmemememeeeeemeememeemmeemmmemeeememmmeeememmmmeemmeemeemeeemmeeeeeme' enc = enc.replace('m', '0').replace('e', '1') msg = '' for i in range(0, len(enc), 8): msg += chr(int(enc[i:i+8], 2)) print(msg)
実行結果は以下の通り。
Hello my kitty, Finally you found her. I am delighted. Take it, here's your gift flag : {Sw33t_l1ttle_k1tt3n}
使用したツールと合わせ、フラグとする。stegseekを使っているが、steghideでブルートフォースしているので、ツール名はsteghide。
GREP{steghide,Sw33t_l1ttle_k1tt3n}
CaeX0R (CRYPTOGRAPHY)
1~1000のランダムな値1つと、フラグの各文字をXORして暗号化している。このランダムな値をブルートフォースして復号する。
#!/usr/bin/env python3 def is_printable(s): for c in s: if ord(c) < 32 or ord(c) > 126: return False return True c = ['162', '177', '188', '169', '136', '187', '138', '145', '172', '187', '138', '145', '172', '190', '152', '156', '187', '195', '177', '142'] for a in range(1, 1001): flag = '' for code in c: flag += chr(int(code) ^ a) if is_printable(flag) and flag.endswith('}'): print(flag) break
復号結果は以下の通り。
QBOZ{Hyb_Hyb_MkoH0B}
さらにシーザー暗号になっている。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
Rotation 10: GREP{Xor_Xor_CaeX0R}
GREP{Xor_Xor_CaeX0R}
NOT 13 (CRYPTOGRAPHY)
quipqiupで復号する。
LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT. MORBI SCELERISQUE, NULLA VITAE LUCTUS TINCIDUNT, MI TURPIS VESTIBULUM TELLUS, UT CONGUE TURPIS QUAM QUIS AUGUE. PROIN ULTRICIES LUCTUS RISUS, EGET VARIUS RISUS INTERDUM SED. NUNC ID TINCIDUNT IPSUM. THE FLAG IS ITS NOT ALWAYS ROT, IN LOWER CASE, WITH UNDERSCORES INSTEAD OF SPACES. FUSCE DICTUM NULLA ERAT, TINCIDUNT TEMPUS LECTUS ULTRICIES VEL.
この文章に従い、フラグを構成する。
grepCTF{its_not_always_rot}
Birdseed (CRYPTOGRAPHY)
ランダムの値とXORして暗号化しているが、seedの範囲が0~999のため、ブルートフォースで復号する。
#!/usr/bin/env python3 import random with open('out.txt', 'r') as f: encrypted = bytes.fromhex(f.read()) for seed in range(1000): random.seed(seed) flag = '' for c in encrypted: flag += chr(c ^ random.randint(0, 255)) if flag.startswith('grepCTF{'): print(flag) break
grepCTF{n3v3r_tru1y_r4nd0m}
Derailed (CRYPTOGRAPHY)
user IDは75番目の素数から1引いたもので、passwordのリストで行数のところに該当するパスワードがあると推測できる。
#!/usr/bin/env python3 from Crypto.Util.number import * with open('password.txt', 'r') as f: words = f.read().splitlines() count = 0 n = 1 while True: if isPrime(n): count += 1 if count == 75: userID = n - 1 break n += 1 password = words[userID - 1] print(password)
該当するパスワードは以下であることがわかる。
T_OF}ENI_NNfqR{rlQcjeCe_B
Rail Fence Cipherのようなので、https://www.dcode.fr/rail-fence-cipherでレール数に4を指定して復号する。
TERC{N_Irel_ONQ_cNFfjBeq}
さらにシーザー暗号として、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号すると、フラグになった。
GREP{A_Very_BAD_pASswOrd}
Uneasy Alliance (CRYPTOGRAPHY)
UNIXTIMEからp, qを計算しているので、UNIXTIMEのブルートフォースで復号する。
#!/usr/bin/env python3 from Crypto.Util.number import * from random import Random rand_fn = lambda n: long_to_bytes(rnd.getrandbits(n)) e = 65537 ct = 9898717456951148133749957106576029659879736707349710770560950848503614119828 for seed in range(1677596400, 1680427000): rnd = Random(seed) p = getPrime(128, randfunc=rand_fn) q = getPrime(128, randfunc=rand_fn) n = p * q phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(ct, d, n) flag = long_to_bytes(m) if flag.startswith(b'GREP{'): flag = flag.decode() print(flag) break
GREP{Brut3D_M3!_f0r_l1f3}
CaeX0R 2 (CRYPTOGRAPHY)
CaeX0Rと暗号データのみ異なるので、同じ方法で復号する。
#!/usr/bin/env python3 def is_printable(s): for c in s: if ord(c) < 32 or ord(c) > 126: return False return True c = ['313', '296', '295', '304', '274', '280', '263', '280', '263', '310', '315', '310', '316', '345', '268', '263', '310', '302', '345', '296', '276'] for a in range(1, 1001): flag = '' for code in c: flag += chr(int(code) ^ a) if is_printable(flag) and flag.endswith('}'): print(flag) break
復号結果は以下の通り。
PANY{qnqn_R_U0en_G0A}
さらにシーザー暗号となっているので、https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
Rotation 9: GREP{hehe_I_L0ve_X0R}
GREP{hehe_I_L0ve_X0R}
DOGE DOGE DOGE (CRYPTOGRAPHY)
フラグが"grepCTF{"から始まることを前提にわかっている範囲でXORの鍵を求める。そこから全体の鍵を推測し、復号する。
#!/usr/bin/env python3 enc = b'#="5\x07\x1b\x01>4#s<u! \x1a3~3-\x1b7w7\x1b&4\x1a":)8' flag_head = b'grepCTF{' key = b'' for i in range(len(flag_head)): key += bytes([flag_head[i] ^ enc[i]]) assert key == b'DOGEDOGE' key = key[:4] flag = '' for i in range(len(enc)): flag += chr(enc[i] ^ key[i % len(key)]) print(flag)
grepCTF{pl4y1ng_w1th_x0r_is_fun}