0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

IJCTF (2021) Writeup

Last updated at Posted at 2021-07-25

1. はじめに

 2021/07/24(土) 16:30 JST ~ 2021/07/25(日) 16:30: JST で、IJCTF 2021 にチーム「N30Z30N」としてソロ参加し、2823 点(得点を得た 132 チーム中 14 位)を獲得しました。
image.png
 途中温度管理と体調管理に失敗してヘロヘロになりましたが、すぐに回復して最後には楽しくプレイできたのでヨシ!としましょう。
 以下、解けた問題の Writeup です。
※2021/07/26 19:30 一部修正

2. Writeup

2-0. Sanity (Reverse, 100点)

Just testing your sanity

 ReverseEngineering にカテゴライズされていますが、いわゆる Sanity Check 問題です。
 表層解析(strings)で得られる「IJCTF{simple_sanity_check_for_babies}」はフェイク。
 IDA Free 版で正当ルートと誤答ルートのアドレス(0x127E、0x128c)を把握し、以下のように「angrで殴って」フラグを得ました。

import angr
p = angr.Project("./sanity")
state = p.factory.entry_state()
sim = p.factory.simulation_manager(state)
sim.explore(find=(0x400000+0x127E,),avoid=(0x400000+0x128c,))
if len(sim.found) > 0:
  print(sim.found[0].posix.dumps(0))

IJCTF{you_did_not_fall_for_it_right?}

2-1. Discrete Log (Crypto, 775点)

This is unique discrete log answering service.

 指定されたサーバに接続すると、RSA の公開鍵と暗号文(これらは動的に変わる)が与えられた後、離散対数計算をしてくれるサービスに移ります。
 このサービスを使い、公開鍵の Mod として得られた数($n$)と 適当な $a$ に対し、 $a^{e}$ mod $n$ を base、$a$ を number に指定すれば秘密鍵 $d$ をリークできます。(以下の例では $a=2$)

This is service for answering discrete log.
Mod: 5272879985315806480750829848227667078673597426524903989629867291289837202205895105729911030263049767652853040507336730464666448862971166047831422338422763836267673154819009497111448320964677269956421026510801870691884038250252644717112426369042214937218765432467001593126986475212643411723960337823410974583878084993
Here is RSA encrypted flag.(e=65537)
2078051323742198497947676697897248201281731730453580916706850881187365450454406000736694844163879816588323719439348211479031979071841177838207999417896928702373445832007257730209054684989592862856081522430104659043389221295572865603425821572477337892146178569516241993151183556529368415825210776927087538827611890377

base: 2412068478897303550419994071388717578085134969863519524730943382066607030110028947817763899368509231398921628181846186324277555833025734508354445443090803702908835610624160278401860781574732697766646417959891374052983950662501351935914065877522992455234939026057391008959538840762309629376533377691087166357625671731
number: 2
Here is the discrete log: 541190887914106112467315516030691099755831867334784422330062443121612065560491835584360690069188462427589239314777919609847549754745800821226758355728835939062912951366312686162569171537159591324783308378689985888601000205620379990180392507159230270191232229079466226169658085227167865177579928853810207348416772899

あとは RSA の復号処理をすれば Ok。

solve.py
from Crypto.Util.number import *

n = 5272879985315806480750829848227667078673597426524903989629867291289837202205895105729911030263049767652853040507336730464666448862971166047831422338422763836267673154819009497111448320964677269956421026510801870691884038250252644717112426369042214937218765432467001593126986475212643411723960337823410974583878084993
e = 65537
c = 2078051323742198497947676697897248201281731730453580916706850881187365450454406000736694844163879816588323719439348211479031979071841177838207999417896928702373445832007257730209054684989592862856081522430104659043389221295572865603425821572477337892146178569516241993151183556529368415825210776927087538827611890377

# discrete_log(base=2**65537, number=2)
d = 541190887914106112467315516030691099755831867334784422330062443121612065560491835584360690069188462427589239314777919609847549754745800821226758355728835939062912951366312686162569171537159591324783308378689985888601000205620379990180392507159230270191232229079466226169658085227167865177579928853810207348416772899

pt = long_to_bytes(pow(c,d,n)).decode()
print(pt[pt.find("IJCTF{"):pt.find("}")+1])

IJCTF{5257c399904eff4833fc2de85b778b8a}

2-2. Square Sum (Crypto, 949点)

Factoring problem is really hard. So I give a hint: square sum of half primes. Please find the flag.

problem.py
from Crypto.Util.number import getPrime, inverse, bytes_to_long
from Crypto.Random.random import randint
from secret import MSG, FLAG


p = getPrime(1024)
q = getPrime(1024)
n = p * q

n2 = n**2
halfpq_sqr_sum = (p//2)**2 + (q//2)**2

k = randint(1, n-1)
g = 1 + k*n
r = randint(2, n2-1)

cmsg = (pow(g, bytes_to_long(MSG), n2) * pow(r, n2, n2)) % n2
c = (bytes_to_long(FLAG) * pow(r, n, n2)) % n2

print('n2='+str(n2))
print('halfpq_sqr_sum='+str(halfpq_sqr_sum))
print('cmsg='+str(cmsg))
print('c='+str(c))
output.txt
n2=303520029249457721712088032629157671188675957478999814001107946377897435098262416944078032743694224547812111876508629629098790706348564683510627437608561667668817082232066690156773182947457415470637661555594387660242481075809061863866878134857767064271020275925358862373499666674028571177472256983693456175513694920187605044808723618380459789773493111510245511524263712883363099195516392023821523383273803607532258960986269523229557484356691226627296549458770890488302153036171491536959410916079515480053813267273641384674290430132960320985260531094724351210760834427441063392448845773608652498658579592730319172250637383076619273369963514086477217760274026803102575002211426718751927101891471494221058041179582203541771830433523526001577817599554252231872769532980390483401226639528096938277398399935682866324784993115812281877591865430215134442904551109441541036428784450911307551504003791948952027070850252232846043561327951083360866399333467139115782229855840846646026182204713128402629095939971393271042355316329494426040959059666926611479882720948473312355005677824274551575679913205670022022017139399175510998237850213737165974646339820009635040028632254754412047567426260382111848392850835481772030875035608845098368736718689
halfpq_sqr_sum=9472622129700199703245586196664170084403806786939405819609832782578112766576428480777927359491256761393588992261088804683630755557509473029147106633909601585974763553680654431838374713192695971152635135654980642968043058123569343691081887394840682085558030205578048546258148505272998984973693174088527063719679039240127298660482089717632821831294015315634267021396765367208053481029040950107066330391869209058343986685994748025768502912822369180563838868099092781901052919510720948179559741776416837917783632982451736331078296715767630096860173482291944176969440690386874499002706015031896162301715990323012706805065
cmsg=103078286643622880447473687471766764943564559811909431666893450054168818307349486985249638958378791065494084728451034371725501517686576282043339068765538253257675113627273797306564684365323509359273378294002601649860804748728163025522075672245295550880060720735463310260935029330499002606032562798724954111417118887527073957993508089754394967659543003785760047941879904675059060008485376318332423664374757995851806458605666638976841133899950278214115072866022244614110580100352950200691110948074551191407363470332793295773273747237585817004577206834980715049990997687288385068292724282066219625651831484886243730221023243574538535723396702634705817852703892641398427064494429830352214499320005058534474284577134432341253340519635081380378045892077000915627548861547061501696012768786270148594424943366660662587186418716670250433370555760184571143569773295737850383542649898270826382659942188410185848934468448568325496582525158048118061659983332451282069426273006110854670823360391164278996722155588697598627131803289512431866077055012911415395954770914537654639608929010542439756174537575923529600976400363945111089267192377586187238410504823191760221711696006651813981609780080767483863025507048248680195946925116086994713836313403
c=240819054139924192908276790404232331564539338066363967597355786532131090443403077278934264677782549578055871874639814497554138070603843470651529113660894021443057595391213699869840820555076709709812961373461750130612453785640822249347659164866017931889613080220570111764428471836538389725220257679376309813972857596742069449014122286177227320041653912468408726067139524231446702068292851152761999746103029142674572600075554585602575858028536948546991047817318676231622316199368535515883188272917775993105890398456057174036933873324756054332572569380343179450564438637126650504362820450286638292251182022086719737414761084603628504362175295748323721068857285078233340896776610403227874250072420794722246884387488198976386566266870301419765724246635936193987658699901284391726429640605754046472825878500017779396277376213443347956951870554421838859114868247676278966446582371758603658584495387466611294920606637857755221756257214625128936810241588259047215874829610442603034882068648615080256023583472836142327431962452607719206361748849195548395481842814703649891102211953579454296662259004591314057576449118621217086325140123543677458167848297604521746504718023238069208215956158087066756140657191702426263476717009016863723080567809

 上のように、問題のスクリプトと出力結果が与えられます。halfpq_sqr_sum から $p+q$ を計算できるので、$p$ と $q$ は容易にリークできます。ここで、$\phi(n) = (p-1)(q-1)$ を計算しておきます。
 次に、m := bytes_to_long(MSG) とおくと、pow(g, m, n2) = 1 + kmn (mod n2) であり、さらにそれを x 乗すると 1 + kmnx (mod n2) となることから、cmsg の式の両辺を $\phi(n)$ 乗して pow(r, n2, n2) を打ち消すことで km を求めることができます。
 k*m が判明すれば、 pow(r, n2, n2) から pow(r, n, n) を求めることができるので、その逆数を c に掛け算してフラグゲットとなります。

solve.py
from Crypto.Util.number import *
import gmpy2

n2 = 303520029249457721712088032629157671188675957478999814001107946377897435098262416944078032743694224547812111876508629629098790706348564683510627437608561667668817082232066690156773182947457415470637661555594387660242481075809061863866878134857767064271020275925358862373499666674028571177472256983693456175513694920187605044808723618380459789773493111510245511524263712883363099195516392023821523383273803607532258960986269523229557484356691226627296549458770890488302153036171491536959410916079515480053813267273641384674290430132960320985260531094724351210760834427441063392448845773608652498658579592730319172250637383076619273369963514086477217760274026803102575002211426718751927101891471494221058041179582203541771830433523526001577817599554252231872769532980390483401226639528096938277398399935682866324784993115812281877591865430215134442904551109441541036428784450911307551504003791948952027070850252232846043561327951083360866399333467139115782229855840846646026182204713128402629095939971393271042355316329494426040959059666926611479882720948473312355005677824274551575679913205670022022017139399175510998237850213737165974646339820009635040028632254754412047567426260382111848392850835481772030875035608845098368736718689
halfpq_sqr_sum = 9472622129700199703245586196664170084403806786939405819609832782578112766576428480777927359491256761393588992261088804683630755557509473029147106633909601585974763553680654431838374713192695971152635135654980642968043058123569343691081887394840682085558030205578048546258148505272998984973693174088527063719679039240127298660482089717632821831294015315634267021396765367208053481029040950107066330391869209058343986685994748025768502912822369180563838868099092781901052919510720948179559741776416837917783632982451736331078296715767630096860173482291944176969440690386874499002706015031896162301715990323012706805065
cmsg = 103078286643622880447473687471766764943564559811909431666893450054168818307349486985249638958378791065494084728451034371725501517686576282043339068765538253257675113627273797306564684365323509359273378294002601649860804748728163025522075672245295550880060720735463310260935029330499002606032562798724954111417118887527073957993508089754394967659543003785760047941879904675059060008485376318332423664374757995851806458605666638976841133899950278214115072866022244614110580100352950200691110948074551191407363470332793295773273747237585817004577206834980715049990997687288385068292724282066219625651831484886243730221023243574538535723396702634705817852703892641398427064494429830352214499320005058534474284577134432341253340519635081380378045892077000915627548861547061501696012768786270148594424943366660662587186418716670250433370555760184571143569773295737850383542649898270826382659942188410185848934468448568325496582525158048118061659983332451282069426273006110854670823360391164278996722155588697598627131803289512431866077055012911415395954770914537654639608929010542439756174537575923529600976400363945111089267192377586187238410504823191760221711696006651813981609780080767483863025507048248680195946925116086994713836313403
c = 240819054139924192908276790404232331564539338066363967597355786532131090443403077278934264677782549578055871874639814497554138070603843470651529113660894021443057595391213699869840820555076709709812961373461750130612453785640822249347659164866017931889613080220570111764428471836538389725220257679376309813972857596742069449014122286177227320041653912468408726067139524231446702068292851152761999746103029142674572600075554585602575858028536948546991047817318676231622316199368535515883188272917775993105890398456057174036933873324756054332572569380343179450564438637126650504362820450286638292251182022086719737414761084603628504362175295748323721068857285078233340896776610403227874250072420794722246884387488198976386566266870301419765724246635936193987658699901284391726429640605754046472825878500017779396277376213443347956951870554421838859114868247676278966446582371758603658584495387466611294920606637857755221756257214625128936810241588259047215874829610442603034882068648615080256023583472836142327431962452607719206361748849195548395481842814703649891102211953579454296662259004591314057576449118621217086325140123543677458167848297604521746504718023238069208215956158087066756140657191702426263476717009016863723080567809

# leak p and q
n = gmpy2.iroot(n2, 2)[0]
p_plus_q = gmpy2.iroot(4 * halfpq_sqr_sum + 2 * n + 1, 2)[0] + 1
p = (p_plus_q + gmpy2.iroot(p_plus_q**2 - 4*n, 2)[0]) // 2
q = n // p

# totiant of n
phi_n = (p-1)*(q-1)

# get k *m (mod n)
km =  (inverse(phi_n, n) * (pow(cmsg, phi_n, n2) - 1) // n) % n

# get pow(r, n2, n2)
R = (cmsg * inverse((1 + km * n), n2)) % n2

# get inverse of pow(r, n, n)
n_inv = inverse(n, phi_n)
rn = pow(R, n_inv, n)
rn_inv = inverse(rn, n)

# get flag
flag = (c * rn_inv) % n
print(long_to_bytes(flag).decode())

IJCTF{8c105018afda40bba64640e50e1da826}

2-3. Riddle Joker (Forensic, 999点)

Joker has returned from his imprisonment. Rumour says that he's scheming a new evil operation by implanting several bombs at a local bank. Each of bomb has a tag information that might be a clue for finding Joker's secret

 このほか、「secret.pdf」という 86,426 バイトの PDF ファイルが提供されます。
 この PDF ファイルを「PDFStreamDumper」というツール( https://github.com/dzzie/pdfstreamdumper ) を使って解析すると、「flag.txt」が入っているパスワード付 zip が末尾近くに隠されていることと、そのパスワードを記した QR コードを示す画像が 49 分割して保持されていることが分かります。
 この画像を特定し、そのデータ部と思われるバイナリと.pgm 形式ファイルのヘッダを合成し、画像ファイルとして出力します。扱いづらいのでビットマップファイルに変換後、手作業で組み立てていくと以下のような QR コードを得ることができました。
 なお、画像データと思しきバイナリデータは過剰に存在しておりましたが、後半部に書かれている座標情報にある stream の番号と照合し目視で特定していきました(おそらく、強い人ならプログラムを書くところなのでしょう)。
 また、座標が一部誤っているようでしたので、目視で適宜(テキトーに)修正しています。
image.png
 これを読み取ると
「The passcode is sup3r__cred3nti4l_p4sscode_3da748」
と出てきましたので、このパスワードで zip を解凍してフラグをゲットできました。

IJCTF{4bbffb87ecc31ba242772ab1f14f569c}
※この QR は組み立てが若干間違っていて、誤り訂正が効いて救われている可能性大です。

3. おわりに

 Forensic問 の「Black Letter」ができなかったのが悔しいっす!PNG の修復まではうまくいっていたのに。。。orz

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?