Google Capture The Flag 2022 Writeup

この大会は2022/7/2 3:00(JST)~2022/7/4 3:00(JST)に開催されました。
今回もチームで参戦。結果は405点で382チーム中112位でした。
自分で解けた問題をWriteupとして書いておきます。

ELECTRIC MAYHEM CLS (crypto)

AESの相関電力解析の問題。
9回同じ波形があり、最後の1回が似ているが特に後半が異なる波形になっている。このことからラウンド数が10のAES-128であると推測できる。平文と暗号文のペアと対応するトレースデータが50個ずつある。Square CTF 2018 の「leaky power」の問題で使ったスクリプトを流用して、秘密鍵を推測する。

#!/usr/bin/env python3
import numpy as np
import json

HW = [bin(n).count('1') for n in range(256)]

sbox=(
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16)

def intermediate(pt, keyguess):
    return sbox[pt ^ keyguess]

with open('traces.json', 'r') as f:
    input = json.load(f)

NUM_TRACES = len(input)

pt_list = [input[x]['pt'] for x in range(NUM_TRACES)]
ct_list = [input[x]['ct'] for x in range(NUM_TRACES)]
tr_list = [input[x]['pm'] for x in range(NUM_TRACES)]

pt = np.array(pt_list)
traces = np.array(tr_list)

numtraces = np.shape(traces)[0] - 1
numpoint = np.shape(traces)[1]

bestguess = [0] * 16
# Set 16 to something lower (like 1) to only go through a single subkey & save time!
for bnum in range(0, 16):
    cpaoutput = [0] * 256
    maxcpa = [0] * 256
    for kguess in range(256):
        print('Subkey %2d, hyp = %02x: ' % (bnum, kguess), end='')

        # Initialize arrays & variables to zero
        sumnum = np.zeros(numpoint)
        sumden1 = np.zeros(numpoint)
        sumden2 = np.zeros(numpoint)

        hyp = np.zeros(numtraces)
        for tnum in range(0, numtraces):
            hyp[tnum] = HW[intermediate(pt[tnum][bnum], kguess)]

        # Mean of hypothesis
        meanh = np.mean(hyp, dtype=np.float64)

        # Mean of all points in trace
        meant = np.mean(traces, axis=0, dtype=np.float64)

        # For each trace, do the following
        for tnum in range(0, numtraces):
            hdiff = (hyp[tnum] - meanh)
            tdiff = traces[tnum,:] - meant

            sumnum = sumnum + (hdiff*tdiff)
            sumden1 = sumden1 + hdiff*hdiff 
            sumden2 = sumden2 + tdiff*tdiff

        cpaoutput[kguess] = sumnum / np.sqrt( sumden1 * sumden2 )
        maxcpa[kguess] = max(abs(cpaoutput[kguess]))

        print(maxcpa[kguess])

    # Find maximum value of key
    bestguess[bnum] = np.argmax(maxcpa)

print('Best Key Guess: ', end='')
key = ''.join([chr(b) for b in bestguess])
print(key)

flag = 'CTF{%s}' % key
print('flag:', flag)

実行結果は以下の通り。

        :
Subkey 15, hyp = cc: 0.45401490813444945
Subkey 15, hyp = cd: 0.49010720993093665
Subkey 15, hyp = ce: 0.47146697427617695
Subkey 15, hyp = cf: 0.5068288359807762
Subkey 15, hyp = d0: 0.4838081788859939
Subkey 15, hyp = d1: 0.4854645790828718
Subkey 15, hyp = d2: 0.47397043847121284
Subkey 15, hyp = d3: 0.5154221414830332
Subkey 15, hyp = d4: 0.4634683845549041
Subkey 15, hyp = d5: 0.49131398564000117
Subkey 15, hyp = d6: 0.4967844598971371
Subkey 15, hyp = d7: 0.5193739416317773
Subkey 15, hyp = d8: 0.4572149886848642
Subkey 15, hyp = d9: 0.43301119798033977
Subkey 15, hyp = da: 0.44607019995923475
Subkey 15, hyp = db: 0.48586908211596014
Subkey 15, hyp = dc: 0.4547689799953138
Subkey 15, hyp = dd: 0.49644633789896647
Subkey 15, hyp = de: 0.48473268198250025
Subkey 15, hyp = df: 0.5015684523731578
Subkey 15, hyp = e0: 0.4855245154529038
Subkey 15, hyp = e1: 0.4773965708833608
Subkey 15, hyp = e2: 0.483905093869241
Subkey 15, hyp = e3: 0.5556550860760712
Subkey 15, hyp = e4: 0.4352508778926109
Subkey 15, hyp = e5: 0.5122373178124859
Subkey 15, hyp = e6: 0.48014300426842854
Subkey 15, hyp = e7: 0.4643384099621492
Subkey 15, hyp = e8: 0.4744772626333159
Subkey 15, hyp = e9: 0.45481497339412846
Subkey 15, hyp = ea: 0.5126674568619023
Subkey 15, hyp = eb: 0.43493989441426584
Subkey 15, hyp = ec: 0.43329852870845154
Subkey 15, hyp = ed: 0.45082959861817234
Subkey 15, hyp = ee: 0.4564515034569882
Subkey 15, hyp = ef: 0.5165751160466153
Subkey 15, hyp = f0: 0.4382706117302386
Subkey 15, hyp = f1: 0.4925133649778097
Subkey 15, hyp = f2: 0.49171365134234124
Subkey 15, hyp = f3: 0.5044774672690812
Subkey 15, hyp = f4: 0.4813231064962157
Subkey 15, hyp = f5: 0.4651318090071481
Subkey 15, hyp = f6: 0.5422507667075844
Subkey 15, hyp = f7: 0.6048876171470424
Subkey 15, hyp = f8: 0.4727434836122757
Subkey 15, hyp = f9: 0.5027699323037813
Subkey 15, hyp = fa: 0.508061038753254
Subkey 15, hyp = fb: 0.4922862591756897
Subkey 15, hyp = fc: 0.5668312430143295
Subkey 15, hyp = fd: 0.46998646853436393
Subkey 15, hyp = fe: 0.45107537517359675
Subkey 15, hyp = ff: 0.4851766132658818
Best Key Guess: W0ckAwocKaWoCka1
flag: CTF{W0ckAwocKaWoCka1}
CTF{W0ckAwocKaWoCka1}