Teaser CONFidence CTF 2019 Writeup

この大会は2019/3/16 20:00(JST)~2019/3/17 20:00(JST)に開催されました。
今回もチームで参戦。結果は330点で546チーム中56位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity check (warmup)

freenodeで#p4teamチャネルに接続する。

20:01 *topic : Teaser lasts from March 16th 11:00UTC to March 17th 11:00UTC | https://confidence2019.p4.team | p4{thanks_for_playing_:)}
p4{thanks_for_playing_:)}

Count me in! (crypto, warmup)

AESのCTRもどき。各ブロックごとに並列で暗号化するが、カウンターを共有しており、61ブロックを32本の同時並行処理だと同じカウンターを使う確率が高い。フラグの部分以外は平文と暗号文が分かっている。
各ブロックの平文と暗号文のXORのどれかがフラグの各ブロックの平文と暗号文のXORになっていると推測し、復号する。

def chunk(input_data, size):
    return [input_data[i:i + size] for i in range(0, len(input_data), size)]

def xor(*t):
    from functools import reduce
    from operator import xor
    return [reduce(xor, x, 0) for x in zip(*t)]

def xor_string(t1, t2):
    t1 = map(ord, t1)
    t2 = map(ord, t2)
    return "".join(map(chr, xor(t1, t2)))

def is_printable(s):
    for c in s:
        if ord(c) < 32 or ord(c) > 126:
            return False
    return True

def unpad(s):
    return s[:-ord(s[-1])]

pt_head = """The Song of the Count

You know that I am called the Count
Because I really love to count
I could sit and count all day
Sometimes I get carried away
I count slowly, slowly, slowly getting faster
Once I've started counting it's really hard to stop
Faster, faster. It is so exciting!
I could count forever, count until I drop
1! 2! 3! 4!
1-2-3-4, 1-2-3-4,
1-2, i love couning whatever the ammount haha!
1-2-3-4, heyyayayay heyayayay that's the sound of the count
I count the spiders on the wall...
I count the cobwebs in the hall...
I count the candles on the shelf...
When I'm alone, I count myself!
I count slowly, slowly, slowly getting faster
Once I've started counting it's really hard to stop
Faster, faster. It is so exciting!
I could count forever, count until I drop
1! 2! 3! 4!
1-2-3-4, 1-2-3-4, 1,
2 I love counting whatever the
ammount! 1-2-3-4 heyayayay heayayay 1-2-3-4
That's the song of the Count!
"""

with open('output.txt', 'r') as f:
    enc = f.read().decode('hex')

enc_list = chunk(enc, 16)
pt_head_list = chunk(pt_head, 16)

flag = ''
for i in range(4):
    enc_flag_parts = enc_list[i - 4]
    for j in range(len(enc_list) - 4):
        key = xor_string(enc_list[j], pt_head_list[j])
        dec_flag_parts = xor_string(enc_flag_parts, key)
        if i == 3:
            dec_flag_parts = unpad(dec_flag_parts)
        if is_printable(dec_flag_parts):
            flag += dec_flag_parts

print flag
p4{at_the_end_of_the_day_you_can_only_count_on_yourself}

Bro, do you even lift? (crypto)

p(=35671)の1乗からpの100乗までmodulusを段階的に増やし、元のデータに近づいていくような方式でフラグを求める。

from Crypto.Util.number import *

p = 35671

a1 = 12172655049735206766902704703038559858384636896299329359049381021748
a2 = 11349632906292428218038992315252727065628405382223597973250830870345
a3 = 9188725924715231519926481580171897766710554662167067944757835186451
a4 = 8640134917502441100824547249422817926745071806483482930174015978801
a5 = 170423096151399242531943631075016082117474571389010646663163733960337669863762406085472678450206495375341400002076986312777537466715254543510453341546006440265217992449199424909061809647640636052570307868161063402607743165324091856116789213643943407874991700761651741114881108492638404942954408505222152223605412516092742190317989684590782541294253512675164049148557663016927886803673382663921583479090048005883115303905133335418178354255826423404513286728

pre_base = [0]
for i in range(1, 101):
    tmp_a1 = a1 % (p**i)
    tmp_a2 = a2 % (p**i)
    tmp_a3 = a3 % (p**i)
    tmp_a4 = a4 % (p**i)
    tmp_a5 = a5 % (p**i)

    base = []
    for b in pre_base:
        for x in range(p):
            y = b + x * (p**(i-1))
            f = (tmp_a1 * y**4 + tmp_a2 * y**3 + tmp_a3 * y**2 
                + tmp_a4 * y + tmp_a5) % (p**i)
            if f == 0:
                base.append(y)
    pre_base = base
    print '%03d' % i, pre_base

for b in pre_base:
    flag = long_to_bytes(b)
    if flag.startswith('p4{'):
        print flag
p4{Th4t5_50m3_h34vy_l1ft1n9}