nullcon HackIM 2022 Writeup

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

Sanity Check (baby)

base64デコードする。

$ echo RU5Pe1dFTENPTUVfVE9fTlVMTENPTl9DVEZ9Cg== | base64 -d
ENO{WELCOME_TO_NULLCON_CTF}
ENO{WELCOME_TO_NULLCON_CTF}

cookie lover (cry)

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

・msg = 'I love cookies.'
・key: RSA秘密鍵
・以下繰り返し
 ・cmd: メニュー選択
 ・cmd[:2] == b'1:'の場合
  ・sign(cmd[2:])の結果を表示
   ・cmd[2:]にb'cookie'が含まれていたら、0を返す。
   ・cmd[2:]に各バイト文字のコードが32より小さいものがあったら、0を返す。
   ・pow(bytes_to_long(cmd[2:]), d, n)
 ・cmd[:2] == b'2:'の場合
  ・verify(int(cmd[2:].decode()))の結果を表示
   ・bytes_to_long(msg) == pow(int(cmd[2:].decode()), e, n)の場合、フラグを返す。

msgの数値化した値を素因数分解し、それぞれの値の復号値の掛け算をしたものがmsgの復号値になる。このことをスクリプトにして、フラグを取得する。

msgの数値 = 2 * 5 * 6673 * 189344417922901 * 30051184098398543

1バイトごとの数値が32より小さいものがないようにすることを考慮して、以下のように組み合わせる。

>>> '%x' % (2 * 5 * 6673 * 189344417922901)
'af585ed1634e6272'

>>> '%x' % 30051184098398543
'6ac3648943d14f'

msgの数値 = (2 * 5 * 6673 * 189344417922901) * 30051184098398543
#!/usr/bin/env python3
import socket
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)

msg = 'I love cookies.'
i_msg = bytes_to_long(msg.encode())
assert i_msg % 2 == 0

with open('pubkey.pem', 'r') as f:
    pub_pem = f.read()

pubkey = RSA.importKey(pub_pem)
n = pubkey.n

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('52.59.124.14', 10301))

i_msg1 = 2 * 5 * 6673 * 189344417922901
i_msg2 = 30051184098398543
assert i_msg == i_msg1 * i_msg2

msg1 = long_to_bytes(i_msg1)
msg2 = long_to_bytes(i_msg2)

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

cmd1 = b'1:' + msg1
print(cmd1)
s.sendall(cmd1 + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
c1 = int(data)

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

cmd2 = b'1:' + msg2
print(cmd2)
s.sendall(cmd2 + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
c2 = int(data)

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

c = (c1 * c2) % n
cmd3 = b'2:' + str(c).encode()
print(cmd3)
s.sendall(cmd3 + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

Choose an option:
1:[text to sign]
2:[number - signature to check]

b'1:\xafX^\xd1cNbr'
325690727908661499309661798673134746525549931298526474206266022934601444708113047135538801328273183297655028939045499865404394823941240366064657468406941255955543239155462791788307004634331285485892917231231535154491480414174110780962202278496000932209175962032151237886596020914761101015606206893177904134713327192496525522973087334386489302244577603477091685695503257531016400169510979360508202905327850786665513071465331459167490648236114913457458979699134001127868136528012905568773910174487093556652184961183575109149941636305890323261605585481820984155205006869910027003730275723789202922476747279116413984761934121175634970596086140934920675738310431675513690082442869526858283838789816550273821346287346247705433357168442589963315788300739505889843762042516200701415034107391026223313860185806426680409603345861645877984652881067094328071044764946477238165736269428785099544134006057946961512482428478101621396328497532118337656844687710248423996821791509608367788650054029785629740188415994398629373996648229976382156946666818287824516294737657584859221147520073523918224710415748442782455079832736893891124063604720331349499371222833636810873692673580690201751213383218525203673097913454100392088940949006666473385795391405
Choose an option:
1:[text to sign]
2:[number - signature to check]

b'1:j\xc3d\x89C\xd1O'
208198990457945937850609311949608176940012395585539332857257067088968427765226163105859687045659561311594400130540516324714817279401844231629262039240904353095416052352496624630113295859222371130952078485039577334384466107875635753028354946420390098694645736246658854182214672336881314799660743325422065712289502420823736493097608814506814256224506757621157275144001135782191054555391441361338471890537154199941992003533221046511955718140429145618768496493555842382918609047753848418895683548356612656999584272503410562983108698153938413445184343582536960274615998732804465232528115930925501201440604777047927165194658124970222970361692822147825046137833768228200005349167259989260725956184603808237558364594206141109794131971913136203898250335845941797424497471833855184786585773711086768133398324593827071277030767872613010460903209647660757178737420249612734974747196142519046313575163675183695056555150040849643757943916749567302853548309927504155366045862962743975056260881143894235457241631234157217478981887233851117304041174464144889252285820478077989914532082803208048645316784315822729581069884421571288419110534658097386516170522447330669376592657176021898629534711821414172468669721575071589053669414994342633548028257950
Choose an option:
1:[text to sign]
2:[number - signature to check]

b'2:183715238308397635712374225419373805798742055683226422934080084912571963507041250961583553698774247650805521395803139201549546155514498290541895722622526436040024047904660300222065763712351217474928376850784630905553758858190205688837414953457818339523500001823621579084464449491627975153761076059266019332369948606754941770662639776141799102648900815323489156665596632472281491282410300991241823300454592922461162611382496591490550609271962691628152420985917389235738904080699097528791626413835794266350039503418910923006851517224066201307979706970243349020918755341888204032054136258892397118667475213806953074733426062459693660535346560600965189675021611731714089952250296904007231831487782499114621964969534223196097402736888347041882248897823173621222165816190410685609663388419181176124525018049553548103350701273672763892456783772563315621245515637664525782066737220163193595126710707023308068862240772717620707827931707550917607230887757066852815460593413638711731996164882740050526557868528818690008964887793725631236051966033348445245363394748484485498232392925138253100412827331658375963317501552336516833908072999587184562493624756880635192816071931270468844048857769080593876604160303151379277321929506581398341475479232'
ENO{F4ct0r_and_Conqu3r!}
ENO{F4ct0r_and_Conqu3r!}

Magic words (cry)

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

・srand(time(0));
・12回以下を繰り返す。
 ・index: ランダム12未満数値
 ・targetにmagic_words[index]を結合
 ・targetに" "を結合
・targetに"egg -- give me the flag!"を結合
・targetを表示
・inputStr: 入力(1024バイト以下)
・n: 固定値(既知)
・signature: inputStrの数値
・m = pow(signature, 3, n)
・char* message = int_to_str(m); 
・strcmp(message, target) == 0の場合、フラグを表示

mの文字列化が\x00<任意>の形式になればよい。任意の長さが長いほど3乗の値を探しやすいので、\x00*256を後続に着け、3乗根を求める。

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

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

n = 618017787734894212297412626251373685391953260761562256145235489725638613940584232503544382290786003502312209667905128601350448023284044287685001349014156076694914712630398005605673740514051697161500094865589407030636020089742702469018865286861672757837780829537694996694833864683805379159042221723723842387709902526369317372614408759564577441603426577837263761362135010436432956275299236838020581149454958441919492440492313018496438498099089383812883390835577605611991978206224568903757920872823293843687276713402767349404190867763874002307512458092607168715450826196573891917818134117192644486518276385456954928916750027466243826789735151096947239658610479195958319740857188397997117968901882796441973903340603204071299778964190138301428335590196310114790206400024512865068139543501059161433303153043130579702895687471084173256419199357102357728600646492441278405167508180066175830384059728198376079159561904247074846373084032155551839906550032001949969880315993549738076287202881585332049659570380033962200120029734918281696120171036422629762465758525540626266912510777059762980057239483211399081674862153728665111324068484754456207003656318475064950873727298779453666117120500091671142668282469471670064268176600698035510728754159

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('52.59.124.14', 10302))

data = recvuntil(s, b'number.\n').rstrip()
print(data)
pattern = b'"(.+)"'
m = re.search(pattern, data.encode())
target = m.group(1)

target += b'\x00' * 256
c = bytes_to_long(target)

m = int(gmpy2.iroot(c, 3)[0]) + 1
print(m)
s.sendall(str(m).encode() + b'\n')
data = recvuntil(s, b'\n').rstrip()
print(data)
data = recvuntil(s, b'\n').rstrip()
print(data)

実行結果は以下の通り。

You think you can give me orders? Prove your authority by signing:
"spam ham mike holy pteng mike pteng moly moly pteng ene moly egg -- give me the flag!"
I want the signature as decimal number.
4181321586603772534793357116592013204177518788955354274759485758634250435240920991719187402108130848481412597142621465930044162030324501918549598769751084475013644442314335348243421214275827733923475024407420375384957499465874028037432088251822128037165226893884317905590494
You were right. Here is your flag:
ENO{c0mp4re_th3_whol3_length}
ENO{c0mp4re_th3_whol3_length}