BSides Delhi CTF 2019 Writeup

この大会は2019/9/28 20:30(JST)~2019/9/29 20:30(JST)の予定で
開催されましたが、スコアサーバのスペックが悪く、
途中全くつながらくあきらめていました。
ただ12時間以上遅れて接続できるようになり、
開催時間は2019/9/30 13:30(JST)までに延長となりました。
実質自分一人でしたが、一応今回もチームで参戦。
結果は1364点で214チーム中47位でした。
自分で解けた問題をWriteupとして書いておきます。

Sanity Check (Misc)

freenodeで#bi0s-ctfチャネルに入る。

13:52 *R3x topic : Bsides CTF 2019 is live | CTF ends at Sep 30 - 10:00 am IST | Sorry for the initial glitches | Hosted by team bi0s | flag for sanity check - bsides_delhi{Welcome_to_BSides_Delhi}
bsides_delhi{Welcome_to_BSides_Delhi}

Business Planning Group (Forensics)

PNGの後ろにBPGが結合している。BPGファイルとして保存して開く。
f:id:satou-y:20191005054907p:plain
画像にはBase64文字列らしきものが書いてある。

YnNpZGVzX2RlbGhpe0JQR19pNV9iM3R0M3JfN2g0bl9KUEd9Cg==
$ echo YnNpZGVzX2RlbGhpe0JQR19pNV9iM3R0M3JfN2g0bl9KUEd9Cg== | base64 -d
bsides_delhi{BPG_i5_b3tt3r_7h4n_JPG}
bsides_delhi{BPG_i5_b3tt3r_7h4n_JPG}

SecureMAC (Crypto)

$ nc 35.188.170.152 1337
Are you sure you can break this??
n: 76472690532697166311269021454388689177337819643898942045648649751867098781025415467064983001826060648346992859933469733190992062552395519434685758658645859158524388939443193604668303015205778680402868870148158899016610276865820550344580001676405530950799291096238491161167924019305657802321745641587719633477
c: 5060590115074907654157443115269670455121700680545650614494579684796385803395442389488861108153558630937635714769114387689782977672747430779513838275172557831752831951447443618690425918742977709246496433951771110941509826730106825295859667024423110580931497154133833035641682463733419604392926160060406026250
enc_key: 7733309070852195143193103111258144016916408733264353946637309551910328067745900310261254365736406633900851633670377795196341452584950259992008214772252578322739868793109441848870933117722212871194379223725084763257441154198893337036520889792871193187008232406741375995946784104058637357478778159030583979776

Message 1: 
1234567890abcdef1234567890abcdef

Message 2: 
1234567890abcdef1234567890abcdee

Tags don't match

サーバ処理の概要は以下の通り。

・p, q: 512bit素数
・n = p * q
・e = 65537
・enc_key: keyをRSA暗号化
・c1 = pow(bytes_to_long("fake_flag"),e,p)
・c2 = (pow(bytes_to_long("fake_flag"),e,q) ^ 1337)%q
・qinv = inverse(q,p)
・h = (qinv * (c1 - c2)) % p
・c = c2 + h*q
・n, c, enc_keyを表示
・msg1, msg2: 16進数で入力
・msg1とmsg2は異なるが、signの結果が同じものを指定できていれば、フラグを表示
 さらにsignは以下のような処理。
 ・tag: 最初のブロック(16バイト)をAES-ECB暗号化
 ・2ブロック目以降、tagとのxorをAES-ECB暗号化し、tagとする。
 ・最後のブロックで生成されたtagがsignの結果となる。

AESの鍵がわからないとどうしようもないと考え、まずはRSA CRT Fault Attackでnを素因数分解し、keyを復号する。
またsignの仕組みはこうなっている。

pt0       --[ECB]--> ct0 -> tag
pt1 ^ ct0 --[ECB]--> ct1 -> tag

keyがわかっていれば、ct0を算出できる。
pt1 ^ ct0 が pt0 と同じであれば、pt0とpt0+pt1は同じタグになる。
以上のことからスクリプトを作成し、実行する。

import socket
from Crypto.Cipher import AES
from Crypto.Util.strxor import strxor
from binascii import hexlify, unhexlify
from Crypto.Util.number import *

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(('35.188.170.152', 1337))

data = recvuntil(s, '1: \n').rstrip()
print data

#### decrypt key ####
e = 65537
n = int(data.split('\n')[1].split(': ')[1])
c2 = int(data.split('\n')[2].split(': ')[1])
enc_key = int(data.split('\n')[3].split(': ')[1])

c1 = pow(bytes_to_long('fake_flag'), e, n)

p = GCD(pow(c2 - c1, e, n), n)
q = n / p

assert p != 1
assert q != 1
assert p * q == n

phi = (p - 1) * (q - 1)
d = inverse(e, phi)

m = pow(enc_key, d, n)
key = long_to_bytes(m)

#### get two messages of same tag ####
message1 = '1234567890abcdef'

ECB = AES.new(key, AES.MODE_ECB)
ct0 = ECB.encrypt(message1)
pt1 = strxor(ct0, message1)
message2 = message1 + pt1

print hexlify(message1)
s.sendall(hexlify(message1) + '\n')

data = recvuntil(s, '2: \n').rstrip()
print data
print hexlify(message2)
s.sendall(hexlify(message2) + '\n')

data = recvuntil(s, '\n').rstrip()
print data

実行結果は以下の通り。

Are you sure you can break this??
n: 96838342337680682267171353265971032237294032508607201668491854263806820935507257020201554846768513785404162079515501788400272077902712859497750615959372529710074478533346415132895418900920925481934023577162607998710600750844042283691554771890081265167715533011289202032266831085194379168012745303876230348851
c: 82762930788866114361331566298418425441585658626734423801814167222017604637666030995313339402634457996150039514884707129717640360539671058514618015163522150561065740292930025759757930922296543751118585326474171749561856217047797331536806343472353171750630801533548282910733579338095878235075315491055574613409
enc_key: 14743966201402632176241257754478447951884458275889406290398299871117647370111201648871152565564132669215895939963962458134242560765314666733237935548142137268658802877923637914124409873842520516723143875066036678122285678465889900193241891892818857332242153876041392988564040068011322655396344645399722254759

Message 1:
31323334353637383930616263646566

Message 2:
313233343536373839306162636465665c2d32312074d2f6f28f5db87f022318
Congrats!! Here's your flag: bsides_delhi{F4ult'n'F0rg3_1s_@_b4d_c0mb1n4ti0n}
bsides_delhi{F4ult'n'F0rg3_1s_@_b4d_c0mb1n4ti0n}

Feedback(?) (Misc)

※正確な問題タイトルは自信がありませんが、こんな感じの名前だったかと...。スマホでアンケートの問題に答えたのですが、記録するのを忘れていました。

アンケートに答えたら、フラグが表示された。

bsides_delhi{Thanks_f0r_PlaYing_th1s_CTF_!!}}