CSAW CTF Qualification Round 2022 Writeup

この大会は2022/9/10 1:00(JST)~2022/9/12 1:00(JST)に開催されました。
今回もチームで参戦。結果は1787点で884チーム中128位でした。
自分で解けた問題をWriteupとして書いておきます。

Welcome (misc)

Discordに入り、#rulesチャネルでリアクションすると、新しいチャネルが現れる。#announcementsチャネルのトピックを見ると、フラグが書いてあった。

flag{c54w_f1n4l5_15_1n_p3r50n_y4y}

Quantum Leap (misc)

フラグは"flag{"から始まり、"}"で終わるので、該当する文字の2進数を比較する。

>>> bin(ord('f'))
'0b1100110'
>>> bin(ord('w'))
'0b1110111'

下0, 4bit反転
下1, 3, 5bit -> 1, 0, 1

>>> bin(ord('l'))
'0b1101100'
>>> bin(ord('x'))
'0b1111000'

下2, 4bit反転
下1, 3, 5bit -> 0, 1, 1

>>> bin(ord('a'))
'0b1100001'
>>> bin(ord('q'))
'0b1110001'

下4bit反転
下1, 3, 5bit -> 0, 0, 1

>>> bin(ord('g'))
'0b1100111'
>>> bin(ord('v'))
'0b1110110'

下0, 4bit反転
下1, 3, 5bit -> 1, 0, 1

>>> bin(ord('{'))
'0b1111011'
>>> bin(ord('n'))
'0b1101110'

下0, 2, 4bit反転
下1, 3, 5bit -> 1, 1, 1

>>> bin(ord('}'))
'0b1111101'
>>> bin(ord('i'))
'0b1101001'

下2, 4bit反転
下1, 3, 5bit -> 0, 1, 1

以下の法則で変換されていると推測できる。

・下1bitが1の場合、下0ビットを反転
・下3bitが1の場合、下2ビットを反転
・下5bitが1の場合、下4ビットを反転

これを元に復元する。

#!/usr/bin/env python3
with open('output', 'r') as f:
    data = f.read().rstrip()

flag = ''
for d in data:
    b = bin(ord(d))[2:].zfill(8)
    b = list(b)
    if b[2] == '1':
        b[3] = str(int(b[3]) ^ 1)
    if b[4] == '1':
        b[5] = str(int(b[5]) ^ 1)
    if b[6] == '1':
        b[7] = str(int(b[7]) ^ 1)
    b = ''.join(b)
    flag += chr(int(b, 2))
print(flag)
flag{4_qu4ntum_g4t3}

DockREleakage (rev)

docker imageのtar.gzを展開し、確認する。repositoriesを見る。

{"dockre-chal":{"latest":"928ab519cd995aeae5eced3dbe4b7e86c8bc7f7662ef0f73e59c2f30b2b3b8e4"}}

928ab519cd995aeae5eced3dbe4b7e86c8bc7f7662ef0f73e59c2f30b2b3b8e4のlayer.tarを展開し、chal/flag.txtを見る。

73c73d_w17h1n_7h3_d0ck3rf1l3}
Find the rest of the flag by yourself!

フラグの後半が見つかった。次にacbb216b17482071caca135101282177f6ffed7b8ee0bfc5323aae103c216d74.jsonを見る。この中のデータの一つに以下のデータがある。

{"created":"2022-09-03T07:46:12.680399343Z","created_by":"/bin/sh -c echo \"ZmxhZ3tuM3Yzcl9sMzR2M181M241MTcxdjNfMW5mMHJtNDcxMG5fdW5wcjA=\" \u003e /dev/null","empty_layer":true}

base64文字列をデコードする。

$ echo ZmxhZ3tuM3Yzcl9sMzR2M181M241MTcxdjNfMW5mMHJtNDcxMG5fdW5wcjA= | base64 -d
flag{n3v3r_l34v3_53n5171v3_1nf0rm4710n_unpr0

先ほどのフラグの後半部と結合すると、フラグになる。

flag{n3v3r_l34v3_53n5171v3_1nf0rm4710n_unpr073c73d_w17h1n_7h3_d0ck3rf1l3}

Word Wide Web (web)

http://web.chal.csaw.io:5010/stuffにアクセスすると、たくさんの単語が表示される。その中に一つの単語だけリンクがあるので、アクセスしてみる。すると、またたくさんの単語が表示され、その中に一つの単語だけリンクがある。繰り返し、リンクをたどる処理をスクリプトにし、実行する。

#!/usr/bin/env python3
import requests
import re

s = requests.Session()

url = 'http://web.chal.csaw.io:5010'
path = ''

while True:
    r = s.get(url + path)
    body = r.text

    pattern = '\<a href="(\/.+)"\>'
    m = re.search(pattern, body)
    if m is None:
        print(body)
        break
    path = m.group(1)

行きついたところにフラグがあった。

CTF{w0rdS_4R3_4mAz1nG_r1ght}

Gotta Crack Them All (crypto)

keyとXORを取っている。パスワードの一つが"Cacturne-Grass-Dark"とわかっている。

$ nc crypto.chal.csaw.io 5002
You can encrypt a pre-approved password using this service.

What is the password you would like to encrypt?

>> Cacturne-Grass-Dark
The encrypted password is: b'kz\xc6\xb9\xd9Du\xcb\x8a\x9e\xe0\x9d\xbeo\xee\x03\xcf\xddd'
Would you like to go again? (Y/N)

暗号化リストの6行目が該当の暗号だとわかるので、XORでキーを割り出し、復号する。鍵の長さが不足しているので、推測して復号する。37行目から以下のように推測し、XORでキーを割り出し、再度復号する。

b'Hakamo-o-Dragon-Fig\xd5\xe3wj0' → b'Hakamo-o-Dragon-Fighting'
#!/usr/bin/env python3
from Crypto.Util.strxor import strxor
from string import *

with open('encrypted_passwords.txt', 'rb') as f:
    words = f.read().splitlines()

with open('leaked_password.txt', 'r') as f:
    leaked = f.read()

key = strxor(words[5], leaked.encode())

for word in words:
    password = strxor((key * 2)[:len(word)], word)
    print('[-]', password)

# guess
key += strxor(words[36][len(key):], b'hting')

for word in words:
    password = strxor((key * 2)[:len(word)], word)
    print('[+]', password)

実行結果は以下の通り。

[-] b'Kingler-Water'
[-] b'Darkrai-Dark'
[-] b'Chingling-Psychic'
[-] b'Happiny-Normal'
[-] b'Clawitzer-Water'
[-] b'Cacturne-Grass-Dark'
[-] b'Slowking-Poison-Psy\xde\xffwg'
[-] b'Sneasel-Dark-Ice'
[-] b'Hoopa-Psychic-Ghost'
[-] b'Rhyperior-Ground-Ro\xde\xfc'
[-] b'Seedot-Grass'
[-] b'Chinchou-Water-Elec\xc9\xe5wg'
[-] b'Tsareena-Grass'
[-] b'Excadrill-Ground-St\xd8\xf2r'
[-] b'Gumshoos-Normal'
[-] b'Kricketune-Bug'
[-] b'Dartrix-Grass-Flyin\xda'
[-] b'Pikipek-Normal-Flyi\xd3\xf0'
[-] b'Dugtrio-Ground-Stee\xd1'
[-] b'Basculin-Water'
[-] b'Hippowdon-Ground'
[-] b'Togetic-Fairy-Flyin\xda'
[-] b'Finneon-Water'
[-] b'Riolu-Fighting'
[-] b'Entei-Fire'
[-] b'Spritzee-Fairy'
[-] b'Mantine-Water-Flyin\xda'
[-] b'Silvally-Normal'
[-] b'Bellsprout-Grass-Po\xd4\xe4qj'
[-] b'Wyrdeer-Normal-Psyc\xd5\xfe}'
[-] b'Marill-Water-Fairy'
[-] b'Herdier-Normal'
[-] b'Altaria-Dragon-Flyi\xd3\xf0'
[-] b'Thwackey-Grass'
[-] b'Spewpa-Bug'
[-] b'Bronzong-Steel-Psyc\xd5\xfe}'
[-] b'Hakamo-o-Dragon-Fig\xd5\xe3wj0'
[-] b'Chespin-Grass'
[-] b'Mr. Mime-Psychic-Fa\xd4\xe5g'
[-] b'Tornadus-Flying'
[-] b'Pupitar-Rock-Ground'
[-] b'Combusken-Fire-Figh\xc9\xfepc'
[-] b'Guzzlord-Dark-Drago\xd3'
[-] b'Carnivine-Grass'
[-] b'Growlithe-Fire'
[-] b'Grubbin-Bug'
[-] b'Gastrodon-Water-Gro\xc8\xf9z'
[-] b'Goomy-Dragon'
[-] b'Thievul-Dark'
[-] b'1n53cu2357234mc1ph3\x8f'
[-] b'Seadra-Water'
[+] b'Kingler-Water'
[+] b'Darkrai-Dark'
[+] b'Chingling-Psychic'
[+] b'Happiny-Normal'
[+] b'Clawitzer-Water'
[+] b'Cacturne-Grass-Dark'
[+] b'Slowking-Poison-Psychic'
[+] b'Sneasel-Dark-Ice'
[+] b'Hoopa-Psychic-Ghost'
[+] b'Rhyperior-Ground-Rock'
[+] b'Seedot-Grass'
[+] b'Chinchou-Water-Electric'
[+] b'Tsareena-Grass'
[+] b'Excadrill-Ground-Steel'
[+] b'Gumshoos-Normal'
[+] b'Kricketune-Bug'
[+] b'Dartrix-Grass-Flying'
[+] b'Pikipek-Normal-Flying'
[+] b'Dugtrio-Ground-Steel'
[+] b'Basculin-Water'
[+] b'Hippowdon-Ground'
[+] b'Togetic-Fairy-Flying'
[+] b'Finneon-Water'
[+] b'Riolu-Fighting'
[+] b'Entei-Fire'
[+] b'Spritzee-Fairy'
[+] b'Mantine-Water-Flying'
[+] b'Silvally-Normal'
[+] b'Bellsprout-Grass-Poison'
[+] b'Wyrdeer-Normal-Psychic'
[+] b'Marill-Water-Fairy'
[+] b'Herdier-Normal'
[+] b'Altaria-Dragon-Flying'
[+] b'Thwackey-Grass'
[+] b'Spewpa-Bug'
[+] b'Bronzong-Steel-Psychic'
[+] b'Hakamo-o-Dragon-Fighting'
[+] b'Chespin-Grass'
[+] b'Mr. Mime-Psychic-Fairy'
[+] b'Tornadus-Flying'
[+] b'Pupitar-Rock-Ground'
[+] b'Combusken-Fire-Fighting'
[+] b'Guzzlord-Dark-Dragon'
[+] b'Carnivine-Grass'
[+] b'Growlithe-Fire'
[+] b'Grubbin-Bug'
[+] b'Gastrodon-Water-Ground'
[+] b'Goomy-Dragon'
[+] b'Thievul-Dark'
[+] b'1n53cu2357234mc1ph32'
[+] b'Seadra-Water'

この中からadminのパスワードと思われる、ポケモンのモンスター名でないものがフラグ。

1n53cu2357234mc1ph32

Phi Too Much In Common (crypto)

$ nc crypto.chal.csaw.io 5000
**********   TOO MUCH IN COMMON      **********

   Have at it!

/------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 1
N = 66230379529365020257079274492974065493126330971013187062230550667484536336498921190934189244362957312249586769969174687366278892047021464836842536529323843984917711253546999262427337059964746457287003004612989077687496386808318868155443955433495669933416861831960406309846051654977485404679018836950591207073
e = 3203033219058218846809870595889303749356759660802874921757919347285050689023
c = 16602634789029238804962653840385596228672793718393061759190379436477434589539439401344810124749337507914027486814367920958698864422367836842000865838169143446244202199945258912479805482529460471614475452210103481314126501905118652422131330354981855680471366776223771244856426005553912820240798175126871245542
> /------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 1
N = 97158515552243305887166842257349497196761944723849796333314570210192465467248058977973817520811466238434579242618470413011446370885671769783517122178886737136267378478851110781637707375501871752419723352747812903232025026662684439394479235396634760931569159089432041476376506570454407306279469567609447874133
e = 18408112354007983870269760119469358714794425138855862698209283717095203251527
c = 40597559051967819648988812970676624339815299554173974340185756834851649772498697096416499057158638069790189014561709885506503658509085168258788719816632907655273832559720389170846504255470221971178328064025401908633312901398161846240306537659732243916190526957713561623469784228736357566604931785270598213207
> /------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 1
N = 77552624443207359646089700308292520363775651928919510316405587244412029459793814355599651363969705936241997624014034446410130409040520904704222288741418451869075450281604530862820189902262119380780789529627644683074820698307434013253176331127640530460518445472446585030342275865032619556106417427541045657889
e = 8290673712481114196337956497649210842364305631913472623568114174185580849831
c = 62227187843422899487768810431178945148094010586999335663611835937007997481020063524535949914284336751562242177109249354250522379832825592726182607445470462086490320419744326295588542630205868022550363635863255848462379142157715139645979242744412831701661714996123243796658849718267858779823092830129715228493
> /------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 1
N = 66230379529365020257079274492974065493126330971013187062230550667484536336498921190934189244362957312249586769969174687366278892047021464836842536529323843984917711253546999262427337059964746457287003004612989077687496386808318868155443955433495669933416861831960406309846051654977485404679018836950591207073
e = 6461914606527340033058068371191892105920231183404124838916730227375848219991
c = 61747655950367328690845311363491999477172913254413763024768230345790552599866557902469338036025727322620488230742095151608347464525375842535363889225896691398096757040997363089905454473662909295617645290221572120790915100723370598403902217780692569984791434842099938496690776449770494327844731248133766400182
> /------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 2 12345678

Nope!

何回かciphertext_infoを取得すると、同じNで異なるeで暗号化したcが得られる。Common Modulus Attackで復号する。
これをクリアすると、phiを計算する問題が出題される。N, e, dがわかっているので、phiを算出し答える。

#!/usr/bin/env python3
import socket
import gmpy2
from Crypto.Util.number import *
from Crypto.PublicKey import RSA

def recvuntil(s, tail):
    data = b''
    while True:
        if tail in data:
            return data.decode()
        data += s.recv(1)

def commom_modulus_attack(c1, c2, e1, e2, n):
    gcd, s1, s2 = gmpy2.gcdext(e1, e2)
    if s1 < 0:
        s1 = -s1
        c1 = gmpy2.invert(c1, n)
    elif s2 < 0:
        s2 = -s2
        c2 = gmpy2.invert(c2, n)

    v = pow(c1, s1, n)
    w = pow(c2, s2, n)
    x = (v*w) % n
    return x

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('crypto.chal.csaw.io', 5000))

ns = []
es = []
cs = []
while True:
    data = recvuntil(s, b'\n> ')
    print(data + '1')
    s.sendall(b'1\n')
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    N = int(data.split(' ')[-1])
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    e = int(data.split(' ')[-1])
    data = recvuntil(s, b'\n').rstrip()
    print(data)
    c = int(data.split(' ')[-1])

    if N in ns:
        index = ns.index(N)
        e1 = es[index]
        e2 = e
        c1 = cs[index]
        c2 = c

        m = commom_modulus_attack(c1, c2, e1, e2, N)
        try:
            password = long_to_bytes(m).decode()
            break
        except:
            ns.append(N)
            es.append(e)
            cs.append(c)
    else:
        ns.append(N)
        es.append(e)
        cs.append(c)

data = recvuntil(s, b'\n> ')
print(data + '2 ' + password)
s.sendall(b'2 '+ password.encode() + b'\n')

data = recvuntil(s, b'\n> ')

N = int(data.splitlines()[6].split(' ')[-1])
e = int(data.splitlines()[8].split(' ')[-1])
d = int(data.splitlines()[10].split(' ')[-1])

rsa = RSA.construct((N, e, d))
p = rsa.p
q = rsa.q
phi = (p - 1) * (q - 1)

print(data + '2 ' + str(phi))
s.sendall(b'2 '+ str(phi).encode() + b'\n')

for _ in range(5):
    data = recvuntil(s, b'\n').rstrip()
    print(data)

実行結果は以下の通り。

**********   TOO MUCH IN COMMON      **********

   Have at it!

/------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 1
N = 92599299343232292261834178543890237387484921511614229653415119150379025672557158079593766699105621305545121399064580043995019950138120667405208241479131905776463986888676538577986955456126293031500188512827288084619006072487276385597167984228025224038068741864645024709036190735173856986216816635205617371251
e = 16928907533626229902284227783657840508336857921783474343555553116847230865331
c = 79570849854711607139417345642388677273839703713555430342321535812820875952624829829228862056309772593548873488031858181225277251937313263316901165117441026524819040228237484837355605354466786258483584226934759111747348086470492341818531267980922417619965568267394278979803974326580670936121922143960459533798
> /------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 1
N = 66841426676804688396771769972561843618412039028675927408156721898462403499632272355018096006084265389119375725460220991982054036255312770486606664268573532988575589293219994782714415202818755031344902659461329975751170875314154800578771280759291227371080846206675137271359015081773571314195744455213154427597
e = 20643168474203546933298257670240213719583351519179679768833156342247300184973
c = 34367729465156325930897062256052971848240755538905676844390968630323096992780138935126484933870426892930284766886879143689207515309637107833249240440322560307519059504083894525893593615656325221819307854771539020472177848929350810915040438732916654817910816704684422906316608152236582001686917198854494566951
> /------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 1
N = 93150654553270961014768164130409121928813076081873161613053090131547851938714893597908885804564067189857086242620569580714858215239915642529188260181294150027598408092318846514660800971605904900252102919761867835829741604253924392570808908720438859080776198561197727644262517765355363081126013877332755639777
e = 14564002562960621555096419172124728966001366362068142277514940102932321715587
c = 26823373791497368615326329325823097570538826785067685635320982007306494352439874098201830966485969176118141362926620739747065514898346909721894614163220613190756377329935483173296178692954012036304274932386801987084207773762448597756225917999340679191286184229577288745713915917134865116686254096088755399915
> /------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 1
N = 97934790717457816377408047595808017336290557647132874078463273042273480783693965913047913569035873972956920187266096013384149733128634153495383667005783367406343085928898092741457647502175596143302932648245342944432576818196386356562830861689797976228719838904175290216793497280116034968767061763793356427923
e = 17026356965343564383006139344035147997574236973261931055393297574271594721947
c = 36422996639906934280791759292864646158321891131994714018774979799204112699817423912629655615142621165865073844048825501188197026566655735034651136290222437988406549568072738686120589143594127844448881834208636747038354360336434517062474426581430949103364256096272027091751555393826040970441807243209096030970
> /------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 1
N = 93150654553270961014768164130409121928813076081873161613053090131547851938714893597908885804564067189857086242620569580714858215239915642529188260181294150027598408092318846514660800971605904900252102919761867835829741604253924392570808908720438859080776198561197727644262517765355363081126013877332755639777
e = 3903045633246461963443996627866752963174454790408982275112905147194381029419
c = 63047438524847764130018540464138007337825148721616052463529587222913141611318978605784743617174005749795694486145667033309999978142171791961527324895350873542614181199261564385165826366721405368299699380566756406246665364100155930677775564053779176118898354227653325334993025828331467179165827916940801675049
> /------------------------------\
|           COMMANDS              |
|                                 |
|   1) ciphertext_info            |
|   2) solve_challenge <password> |
|   3) exit                       |
\------------------------------/

> 2 d0nt_reUs3_c0mm0n_m0duLus_iN_RSA

********** What the Phi?  **********

Give me phi and I'll give you a flag


N = 97934790717457816377408047595808017336290557647132874078463273042273480783693965913047913569035873972956920187266096013384149733128634153495383667005783367406343085928898092741457647502175596143302932648245342944432576818196386356562830861689797976228719838904175290216793497280116034968767061763793356427923

e = 24246426518850857132122259750361730417342755259639998613040824532553721920239

d = 38550551838267677901600814308974794800412771782528865753556316459464100087576689630219459038572918207153521275050604895720746906733498267505162038809151851814433919954002172458165034676258879934994139534497931839686512983498911853904726892873467416261172268490783160334649530502171196793008921183132824046351
/------------------------------\
|           COMMANDS              |
|                                 |
|   1) try_again                  |
|   2) phi <phi_value>            |
|   3) exit                       |
\------------------------------/

> 2 97934790717457816377408047595808017336290557647132874078463273042273480783693965913047913569035873972956920187266096013384149733128634153495383667005783347591848586542793419570625418124529900910744439497817365571012710187525969553709932015969835839230374334244769937204684915684471122778808735239720897227128

What?! How did you do that??

flag{aR3nT_U_tH3_RSA_ninJA}
flag{aR3nT_U_tH3_RSA_ninJA}

Not Too Taxing (crypto)

bkcrackで既知平文攻撃を行う。

$ ./bkcrack -C Tax_Ret_Form_Nov_2021.zip -c Tax_Ret_Form_Nov_2021.pdf -p Tax_Ret_Form_Blank.pdf
bkcrack 1.0.0 - 2020-11-11
Generated 4194304 Z values.
[11:07:32] Z reduction using 150176 bytes of known plaintext
22.6 % (33917 / 150176)
255 values remaining.
[11:07:36] Attack on 255 Z values at index 117285
Keys: b2c4a24e f036ff3a 998f6727 
47.5 % (121 / 255)
[11:07:37] Keys
b2c4a24e f036ff3a 998f6727

$ ./bkcrack -C Tax_Ret_Form_Nov_2021.zip -c Tax_Ret_Form_Nov_2021.pdf -k b2c4a24e f036ff3a 998f6727 -d Tax_Ret_Form_Nov_2021.pdf
bkcrack 1.5.0 - 2022-07-07
[13:41:06] Writing deciphered data Tax_Ret_Form_Nov_2021.pdf (maybe compressed)
Wrote deciphered data.

抽出したTax_Ret_Form_Nov_2021.pdfをPDFStreamDumperで開き、各オブジェクトを見ていく。オブジェクト1268にフラグが書いてあった。

flag{1f_y0u_u53_z1pcryp70_4ny0n3_c4n_aud17_y0u}