ångstromCTF 2019 Writeup

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

IRC (MISC 10)

freenodeで#angstromctfチャネルに入ると、フラグが書かれていた。

ångstromCTF 2019 | 4/20 - 4/27 @ 00:00 UTC | Ops and Voiced are authors | actf{like_discord_but_worse}
actf{like_discord_but_worse}

Blank Paper (MISC 30)

PDFの先頭4バイトが壊れているので、"%PDF"にして開くと、文章の中にフラグが含まれていた。

actf{knot_very_interesting}

Classy Cipher (CRYPTO 20)

シーザー暗号の拡張版。

def decrypt(e, s):
    d = ''
    for c in e:
        d += chr((ord(c) - s) % 0xff)
    return d

enc = ':<M?TLH8<A:KFBG@V'

s = ord(':') - ord('a')
flag = decrypt(enc, s)
print flag
actf{so_charming}

Really Secure Algorithm (CRYPTO 30)

RSA暗号。p, qがわかっているので、そのまま復号する。

from Crypto.Util.number import *

p = 8337989838551614633430029371803892077156162494012474856684174381868510024755832450406936717727195184311114937042673575494843631977970586746618123352329889
q = 7755060911995462151580541927524289685569492828780752345560845093073545403776129013139174889414744570087561926915046519199304042166351530778365529171009493
e = 65537
c = 7022848098469230958320047471938217952907600532361296142412318653611729265921488278588086423574875352145477376594391159805651080223698576708934993951618464460109422377329972737876060167903857613763294932326619266281725900497427458047861973153012506595691389361443123047595975834017549312356282859235890330349
n = p * q

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)

flag = long_to_bytes(m)
print flag
actf{really_securent_algorithm}

Runes (CRYPTO 70)

Paillier暗号。factordbでnを素因数分解する。

n = 310013024566643256138761337388255591613 * 319848228152346890121384041219876391791

素因数分解できたので、あとはそのまま復号する。

from Crypto.Util.number import *
import gmpy2

def lcm(a, b):
    return (a * b) / gmpy2.gcd(a, b)

def L(u, n):
    return (u - 1) / n

n = 99157116611790833573985267443453374677300242114595736901854871276546481648883
g = 99157116611790833573985267443453374677300242114595736901854871276546481648884
c = 2433283484328067719826123652791700922735828879195114568755579061061723786565164234075183183699826399799223318790711772573290060335232568738641793425546869
p = 310013024566643256138761337388255591613
q = 319848228152346890121384041219876391791

lamda = lcm(p-1, q-1)
mu = L(pow(g, lamda, n**2), n) % n

m = L(pow(c, lamda, n**2), n) * inverse(mu, n) % n
flag = long_to_bytes(m)
print flag
actf{crypto_lives}

Paint (CRYPTO 100)

与えられたスクリプトの概要は以下の通り。

image = flagの数値
palette = 1 << 2048
base = random.randint(0, palette) | 1
secret = random.randint(0, palette)
my_mix = pow(base, secret, palette)

shared = pow(your_mix, secret, palette)
painting = image ^ shared

paletteが2の2048乗なので、2の1乗から順にmodulusを少しずつ増やし、近づいていくような方式でsecretを求める。secretが判明したら、あとはそのままflagを算出して、割り出す。

from Crypto.Util.number import long_to_bytes

palette = 32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656
base = 13489305024865487703110255658234329747698118206959778644688156332043783846078839120693894255527894489531905012244713117142764166452312133019772171674466933769775907460046497284522592167536594047800489828714315435570429416637425443402332599055774982796405757075108551322778712959943658831605397635195107786224617525627358659165255604556424206194207190437525742567525338826878962081515333896433312311548844614323540250054093970082337500580573165008440265840792908334486258260848163001490152587781983042546491301026074736907693887630347258892882871059741621049169714319440564952700454580681894452760215968494428411686329
my_mix = 6870295205307030503255600311283969014496436297715066273709495591567561187646528069669895230912327862244474990612611625088862250315706633708998214109152824455738719595737772769297517386968692628228327225922261219083693899105983726637012353264168761696183327692619506267951701511870035935612090359086376808592001973358166067468618577312983514388332736591060901174314042634365304017788649960016991442596975922402288221898367955532116456798868804571091463566329706023967280838744359633963847966790121312196824818606244189274966061324393424041211903396020720341163472399763951106703068172772579049891895580785347369093113
your_mix = 14317253516668543276504878316838097235650210449758621543536146016892160048656997634541093315774403078357942150970695487937570449270120625898199254439189104072891595263513437420116930684308702803055295267600790477195902538538739117809573391251939794413361184343367694928615752045687223262368136262534778688889202144260002584306527206705616186699377315031757095455954292951059462279988296369935635246644221722025457496936215039008069820514166063271894671978845634968761626636993374291118230179892722513818307254406450607168911057458141649111515924404215975886422961651958216688209696158879621701708955382424640000048217
painting = 17665922529512695488143524113273224470194093921285273353477875204196603230641896039854934719468650093602325707751566466034447988065494130102242572713515917910688574332104680867377750329904425039785453961697828887505197701127086732126907914324992806733394244034438537271953062873710421922341053639880387051921552573241651939698279628619278357238684137922164483956735128373164911380749908774512869223017256152942356111845682044048514917460601214157119487675633689081081818805777951203838578632029105960085810547586385599419736400861419214277678792284994133722491622512615732083564207280344459191773058670866354126043620

secret = 0
for i in range(1, 2049):
    for j in range(2):
        if pow(base, secret, 2**i) == (my_mix % (2**i)):
            break
        secret += 2 ** (i - 4)

shared_mix = pow(your_mix, secret, palette)
image = painting ^ shared_mix
flag = long_to_bytes(image)
print flag
actf{powers_of_two_are_not_two_powerful}

Secret Sheep Society (120)

与えられたスクリプトの概要は以下の通り。

■index()
・token = cookieのtoken
・session = tokenのAES復号(JSON)
 -> 'admin'がTrueの場合、フラグ表示

■enter()
・handle = 入力したハンドル名
・session = {'admin': False, 'handle': 'XX'}という形式
 -> JSONダンプ:{"admin": false, "handle": "XX"}
・token = sessionのAES暗号

通常このようなデータが暗号化される。

0123456789abcdef
{"admin": false,
 "handle": "XX"}
PPPPPPPPPPPPPPPP

下記のようにすればよい。暗号本体は変えずに、XORで算出してIVだけ変えれば、この暗号文を作れる。

{"admin": true, 
 "handle": "XX"}
PPPPPPPPPPPPPPPP
import requests
from base64 import b64encode, b64decode

def pad(s):
    c = 16 - len(s) % 16
    return s + chr(c) * c

def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

url = 'https://secretsheepsociety.2019.chall.actf.co/'

payload = {'handle': 'XX'}
r = requests.post(url + 'enter', data=payload, allow_redirects=False)
token = r.cookies['token']

iv = b64decode(token)[:16]
enc = b64decode(token)[16:]

plain = '{"admin": false,'
new_plain = '{"admin": true, '
new_iv = str_xor(str_xor(plain, iv), new_plain)

new_token = b64encode(new_iv + enc)

cookies = {'token': new_token}
r = requests.get(url, cookies=cookies)
print r.text

実行結果は以下の通り。

<!DOCTYPE html>
<html lang="en">
  <title>Secret Sheep Society</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://unpkg.com/tachyons/css/tachyons.min.css">
  <body>
    <div class="courier mh4 mh5-m mh7-l mv4">
      <p class="f3">Secret Sheep Society Portal</p>
      <pre>
           __  _
       .-.'  `; `-._  __  _
      (_,         .-:'  `; `-._
    ,'o"(        (_,           )
   (__,-'      ,'o"(            )
      (       (__,-'            )
       `-'._.--._(             )
          |||  |||`-'._.--._.-'
                     |||  |||
      </pre>

        <p style="line-height: 2">
          Welcome, <span class="f6 light-red bg-light-gray ph2 pv1 br2">XX</span>.

            The flag is <span class="f6 mid-gray bg-light-gray ph2 pv1 br2">actf{shep_the_conqueror_slumbers}</span>.

        </p>
        <div class="mv3">
          <form method="post" action="/exit">
            <input class="courier b ph3 pv2 input-reset ba b--black pointer" type="submit" value="Exit">
          </form>
        </div>

    </div>
  </body>
</html>
actf{shep_the_conqueror_slumbers}

WALL-E (CRYPTO 130)

最低でもフラグの後ろに\x00が84バイトある。

aa...aa\x00\x00\x00\x00..\x00
  86           84

以下のように式を変えて計算しやすくする。

m = flag * (256**84)
⇒pow(m, e, n) = pow(flag, e, n) * pow(256**84, e, n)
⇒pow(flag, e, n) = c * inverse(pow(256**84, e, n), n)

pow(flag, e, n) がわかるので、この値にnを足しながら、e乗根を確認し、該当するものを探す。

from Crypto.Util.number import long_to_bytes, inverse
import gmpy

n = 16930533490098193592341875268338741038205464836112117606904075086009220456281348541825239348922340771982668304609839919714900815429989903238980995651506801223966153299092163805895061846586943843402382398048697158458017696120659704031304155071717980681280735059759239823752407134078600922884956042774012460082427687595370305553669279649079979451317522908818275946004224509637278839696644435502488800296253302309479834551923862247827826150368412526870932677430329200284984145938907415715817446807045958350179492654072137889859861558737138356897740471740801040559205563042789209526133114839452676031855075611266153108409
e = 3
c = 11517346521350511968078082236628354270939363562359338628104189053516869171468429130280219507678669249746227256625771360798579618712012428887882896227522052222656646536694635021145269394726332158046739239080891813226092060005024523599517854343024406506186025829868533799026231811239816891319566880015622494533461653189752596749235331065273556793035000698955959016688177480102004337980417906733597189524580640648702223430440368954613314994218791688337730722144627325417358973332458080507250983131615055175113690064940592354460257487958530863702022217749857014952140922260404696268641696045086730674980684704510707326989

c2 = c * inverse(pow(256**84, e, n), n) % n

i = 1
while True:
    m = gmpy.root(c2, e)[0]
    if pow(m, e) == c2:
        break
    c2 += n
    i += 1

flag = long_to_bytes(m).rstrip('\x00')
print flag
actf{bad_padding_makes_u_very_sadding_even_if_u_add_words_just_for_the_sake_of_adding}

Lattice ZKP (CRYPTO 150)

$ nc 54.159.113.26 19003

    __      __  __  _                    __       
   / /___ _/ /_/ /_(_)_______     ____  / /______ 
  / / __ `/ __/ __/ / ___/ _ \   /_  / / //_/ __ \
 / / /_/ / /_/ /_/ / /__/  __/    / /_/ ,< / /_/ /
/_/\__,_/\__/\__/_/\___/\___/    /___/_/|_/ .___/ 
                                         /_/      
Query until you are convinced that I know s, where
b = A*s + e

A*r + e: 308209fe0202676402024d3b020219ae02027a860202599c020223ef02020e700202067b02025f3802021d9a02025e6f02025bdb02026a3102026a0102025f5f0202247502022d1e020201b30202233b0202229e02024aed02024cca02020fdb02021273020273dd0202399f0202767402023f0b0202468b020245bf020206ad0202557002022a4402022e340202678d02027548020221690202508e02026a75020216180202107102021d17020213a802026728020225e20202396202024ad202023f8e02025a7d02025f7b02022f6902020b3a020252d002023e9602023aaa02026e4502022d0f0202045502024d40020246ef020231860202767702027cba0202461602024b8402021c33020219c802022947020248c80202643e020232960202034b02024e210202343402027827020244b7020274f902026782020232290202203d020214b702025e9f020242e102022939020202d802027a31020244ed02025f1702025b9102025a0c02022da702027aa1020254de02020bb202024bf60202085c0202647b0202538d02025ea2020238a30202746802024aba0202059702026893020216190202703d0202199f02023d26020223a002023b4c0202109c020267ec02027670020245d1020268770202423b0202634f0202793e02021884020238a00202451f02022635020243e302020eb30202182b02022be30202550d02021b7d02025e8b020258a40202190a020205f802026e8902021df0020221e902022a900202717b02025040020212a802022f12020256a502021543020213de02022cff0202198a02026d560202176502027f24020231e5020236400202617e020252e60202770b0202237e02015a020207cc02020fef02023ff902020b71020203b10202449302025e1e020224b5020233e702024a4502024f9f02025aab0202433b02025b2c0202207b0202166d02023c52020275f902022f420202791d020247c20202100d0202162c02022b5b02020c2f0202055502027078020267210202213902020d7b02023a0a020224410202307c020271f202023be402022fae0202691e02023ac00202455a02024ac002025a300202509b020251af020241d202027c660202483102025c4002021076020203a10202758c02027ebf02024aaa0202176a020264100202369902026d89020262540202180c02025577020276260202240202027fb802020f0d02027375020253f70202438702024dce020276c502027f8a0202258502025ec102024ce502027022020255a30202409502024e1902025f4402023c4a020266d90202134c02021bfd0202642102026678020214d9020202a90202039602022c50020239aa020259de02026ef2020265430202377602020959020276c702021d2202021fbd0202674e020269dd020257d702025feb0202551c02020b88020201f70202686702022c0302026364020215e402024a7002020ed6020275a40202546a02027c450202776b02021c54020224c90202062a020213a202026b740202012102025c89020230b5020201aa020213b4020217bb02024f0202023ff402024c40020233950202270102023b6702027e2a02025f780202100602026140020221f90202118902025901020229f60202334102022b0d02022c7c020240cf02023d5202023a89020254c302021d6602022b20020254ed020250b10202644d02026aa602026b92020219a302025f6902026e1b0202083402020a3802025c9902023dc702025bea02026fb802023bae020248b7020206e8020279470202796c02023c9002021a7402022aee020267ea020267f502023d68020211c70202370802021020020261e802021e9c020257ca02024cf202021fac0202028c02021c9402026eb9020244f302026a2202026b9f0202224d02023d260202529002021c310202408002021b21020243d80202186c0202029c02021d6c020238400202678b0202456302022c19020211d402024e6f020253e6020276480202640e02020265020231f50202341202024bf20202787f02023dc002027919020202ee02023f9402023c9202023ada020221a9020272e302025023020277a2020256b6020269dc020204ac0202643f020232c602020fc80202475f02023d73020215e402023c8602026fbc02024345020201c90202322d020229230202522302022b260202164402022af702025b9402020b2602020cf00202532802026c820202209f02022a2f02021cb002024f1202027acb020215c9020266cb020266d3020245be0202099b02027deb02027a5302020b7b02023d4e02025cfc0202438702022fbd0202540d0202312102027e5f020140020262ec020223cd020202c602027f77020221600202332302027ec802021e3602025c600202594302022fef020236f802023cf80202291602026b6a02023b39020237a5020232b2020257170202414102023d520202283b020274a502025f3a0202388f02022c7b020207290202723f0202454c0202289e02027ef0020226e00202251502027ab6020222170202090302022d8d020259fa02025c38020279c302021ffe020255af02027dd1020212b40202398e0202373202023c6b020261be020278e302020af1020222470202738302027eb00202178202025f010202752002021ee3020206e402027a5402020325020218cd02021d8502022e7e020227790202589002026c71020242a8020204e202025cad0202512d020271c202027f6802026d3002024196020223df020248560202667902024c58020260e4020207db02024a3e020209be020248be02024c6c0202169a020247280202528a0202774602027d1b02026944020246e2020239f40202226e02027e4e02022fe50202653502022ca702021afd02021f0702021221020276fc0202453002021bc6020232a2020245df020253c002021c4c020240780202197c02021411020250ba02022cd202020e5d0202080002027e3f020234a0020228760202786c020256db0202053f02020ae70202453e02022d2802024ea50202202002020ed7020253100202074b02023b3402025bd502021f7d02024857020215540202202102026db402027a4c020238560202265f020271d002027dcb0202294d02026a64020210950202697f020242a602022edf02023873020265bd02027e4c020213a9020234cf020232ad020226b602025fe002025d5f020256b2020253a60202336d02024dd602023da7020260c702021aa1020264b2020254110202221902021c900202384e02024eab020259990202642f0202292e0202026b0202029e0202138f02027bc10202178e0202757902027c6c02025ef40202195102022dd1020231870202592b020270e00202057402023a14020218490202487f02027d4902022dd7020221f402021a4d0202753e02022c15020238d202021e3f02023b9202020c3702020a7902022651020273a902026b5602024573020277350202042302027ab6020239100202609f020264450202552002021da7020242a50202769202022311020271b8020249d702026f87020271dd02023ab2020239d2
[0] r
[1] r+s
Choice: 0
r: 308209fe02022f35020273a602022c9b0202265102026d6702022212020273ab0202784502024c25020229b5020237c602027c20020238330202025b02021f3c02022f990202566702021c7602025886020237190202405902024ffa02024c5c02023e1702027b640202501d02022cd902027d3702023a2f020266bc0202669c020259570202624f020231230202679f02021bc6020222cd02026b8d02021ac60202714d020223f60202256f020276170202565e02025bb90202761e020210de02025e0a0202623002022d8902020884020237f4020226f1020207a8020207c702022f940202149802027ad302020485020244320202792b020257a202025b3f02024dd202023214020231da0202320602023ecb02027f4102027d53020221df020244eb02021a2b020232da020244a402021fcc020273960202337e02026dc80202599502025249020258b8020273a1020247f002025c890202785e020227dc02022f5a02020d9102023cae02022f93020244d202025c3202020da602020e8702020b0e020237b202022bc402023dc002026b89020225060202642e0202120f020225ff0202097d0202124402024fd6020239a502021b75020231ae02026dd00202225702027326020204f80202587b020231d502026e0002024ddb020219450202138602021b9f020247cc02022ff402026052020256a5020236ca020279d40202405a0202141902027b2b0202577b020247a902022cd302027fd702020b9b02027ffe02025c3b02023ccb02024f3e02023afa0202686002020f6102026c3702027ee60202330d020278ea0202232702021db10202184c0202194702023f5902027ab40202128602023ba00202780b02025345020238690202651d0202493102022879020252cc02025de1020228e702022c2c020274cd020277ab0202393902026f4f020273d90202664402026b7e020220260202531c0202718d020236dd02023f3c02022037020217d7020216840202128b0202553602025b780202743602024c71020229e102025fc202026a3802021c6f0202637402026e1c02020b34020213f2020212ee02022852020215a902024d8102021b02020208a102022c3b0202690502020a5902020227020231430202349d02020ee7020231f002020d7b0202181402026e1d02024e360202163902023e050202230102025ded0202319f02022b85020211aa02027692020226c302021a3702020273020210dd020258b50202028c02024be6020272ef02020741020231e50202455b02022fdf02026d9002023de3020216c502026a840202282f020239b0020227f1020240a70202165e02023d3c0202162902022e4c02022f7702021b6602026df7020252bf02021d1a0202212c02027f78020207ab02024279020259e702021aac02022dc702024f010202542e020239d6020270ef0202579d0202291202023b9502022e28020222c502024b71020255ea02022c5f020206f602026cca020279f202022e490202226502022df9020254940202410302027ac80202398d020243a502025fde0202062102024d6102023b100202668402021c9b0202147602024ad002024d7002026948020271f602023e2502026ce902027c43020257be02025a9d02023ba6020257e40202523a020241760202300c020234f202020ab30202310b0202372402022450020202dd0202764d02027c6b0202287e02022a67020207810202371602025ff8020213670202466c02020d62020265e0020271b2020262f7020256a60201080202393802020756020237df0202060d02022aaa02027cdf0202151d020252e302024fab02020b240202457e020263ea020278b1020220c90202328e020249440202759602022fb70202729e02026ef702020efc0202553f02020d290202555202023819020265e602026635020216e3020238de02021fa4020254a8020244890202265e020200ae02021d1a020221c50202637d0202657802024c2602022a4a0202566402027baa0202139202025916020226a402022c830202351202024d9202022da602026ca102022f250202766c02027eda020253300202665702021d1802021454020265e9020232740202115302027cc0020221960202187302020428020254f1020246a302027032020209c7020245ff0202594702020fca02022d8102021309020213bf02023e1902020a4a02023fd502020d360202377d02020e7c0202084d0202563602021ad30202330b020259ef0202108f0202336d020238370202670e02022e39020276d402021acf0202470702022c1502024b830202680c020224a2020269db0202565d02025d0d020273030202273402022a970202117902027ffa02026c85020257510202738e02021e47020256c002025c6c020240eb02027902020211b902026264020238f7020232f302021ceb020264720202477c020234750202388a02025b2e0202337502025fc302022110020269140202319602024ea8020264300202468f02022dac02026562020230ce02021ef402024a3802027db002026e680202253a020219c5020249280202760e020247bb0202342d020245530202798c02024b8d02021bb90202703f02026c0b02024e0a020262ae0202689b02021b2a02022d3b02024c9002027574020228ae02021ccb0202160502023e040202441e02025d2b0202149f0202297802024017020171020268b002026b8f020211ba02020a960202125f0202290a020242c40202114002023e850202790902026b7d0202461f0202406e02024016020224bb0202012f020217c9020235ef0202469a0202149302023c8c0202759d02023cff0202571202020e8702020cb10202520e02020f7202021d47020213bd0202257902025f5f020252f9020239a8020279cc020278840202234402021ac002024c50020236d502027778020276a10202286302027d5702022f510202472602022dd10202624102021eb902020e4a02024aba020270fe0202600202025408020279df02025dff02025f3f02022da202023c4e02027be302021140020232a102023c3f0202530e02021b26020212f90202282b020248e002022b16020237ac020275bc02026cd902024547020227ba020229ca02025be802022bb9020259640202675c0202732c020201210202042f020232470202391a02024ad4020209d402020f4f02021b9202025014020258460202050e020204bc02027d9202020c6f020217320202168402020e3402022b7902024d24020212ed02023156020204f402021b8502020d83020240c90202563f02027c1f02023f20020215f5020267f802020270020275bb0202137d0202048f0202343f02022f9502022d0402021d5d020258e102026ea60202429402023461020209d702024af4020274c5020251de020217720202419302021bfd0202608f0202698d0202156302025f9302022078020278360202205502024b8702027acd02024b87020209e1020222fd020204e40202117e02027edb020244a6020215b3020208020202288d0202262b020235f802024c1b02020ebc02023a270202381e

$ nc 54.159.113.26 19003

    __      __  __  _                    __       
   / /___ _/ /_/ /_(_)_______     ____  / /______ 
  / / __ `/ __/ __/ / ___/ _ \   /_  / / //_/ __ \
 / / /_/ / /_/ /_/ / /__/  __/    / /_/ ,< / /_/ /
/_/\__,_/\__/\__/_/\___/\___/    /___/_/|_/ .___/ 
                                         /_/      
Query until you are convinced that I know s, where
b = A*s + e

A*r + e: 308209fe0202676402024d3b020219ae02027a860202599c020223ef02020e700202067b02025f3802021d9a02025e6f02025bdb02026a3102026a0102025f5f0202247502022d1e020201b30202233b0202229e02024aed02024cca02020fdb02021273020273dd0202399f0202767402023f0b0202468b020245bf020206ad0202557002022a4402022e340202678d02027548020221690202508e02026a75020216180202107102021d17020213a802026728020225e20202396202024ad202023f8e02025a7d02025f7b02022f6902020b3a020252d002023e9602023aaa02026e4502022d0f0202045502024d40020246ef020231860202767702027cba0202461602024b8402021c33020219c802022947020248c80202643e020232960202034b02024e210202343402027827020244b7020274f902026782020232290202203d020214b702025e9f020242e102022939020202d802027a31020244ed02025f1702025b9102025a0c02022da702027aa1020254de02020bb202024bf60202085c0202647b0202538d02025ea2020238a30202746802024aba0202059702026893020216190202703d0202199f02023d26020223a002023b4c0202109c020267ec02027670020245d1020268770202423b0202634f0202793e02021884020238a00202451f02022635020243e302020eb30202182b02022be30202550d02021b7d02025e8b020258a40202190a020205f802026e8902021df0020221e902022a900202717b02025040020212a802022f12020256a502021543020213de02022cff0202198a02026d560202176502027f24020231e5020236400202617e020252e60202770b0202237e02015a020207cc02020fef02023ff902020b71020203b10202449302025e1e020224b5020233e702024a4502024f9f02025aab0202433b02025b2c0202207b0202166d02023c52020275f902022f420202791d020247c20202100d0202162c02022b5b02020c2f0202055502027078020267210202213902020d7b02023a0a020224410202307c020271f202023be402022fae0202691e02023ac00202455a02024ac002025a300202509b020251af020241d202027c660202483102025c4002021076020203a10202758c02027ebf02024aaa0202176a020264100202369902026d89020262540202180c02025577020276260202240202027fb802020f0d02027375020253f70202438702024dce020276c502027f8a0202258502025ec102024ce502027022020255a30202409502024e1902025f4402023c4a020266d90202134c02021bfd0202642102026678020214d9020202a90202039602022c50020239aa020259de02026ef2020265430202377602020959020276c702021d2202021fbd0202674e020269dd020257d702025feb0202551c02020b88020201f70202686702022c0302026364020215e402024a7002020ed6020275a40202546a02027c450202776b02021c54020224c90202062a020213a202026b740202012102025c89020230b5020201aa020213b4020217bb02024f0202023ff402024c40020233950202270102023b6702027e2a02025f780202100602026140020221f90202118902025901020229f60202334102022b0d02022c7c020240cf02023d5202023a89020254c302021d6602022b20020254ed020250b10202644d02026aa602026b92020219a302025f6902026e1b0202083402020a3802025c9902023dc702025bea02026fb802023bae020248b7020206e8020279470202796c02023c9002021a7402022aee020267ea020267f502023d68020211c70202370802021020020261e802021e9c020257ca02024cf202021fac0202028c02021c9402026eb9020244f302026a2202026b9f0202224d02023d260202529002021c310202408002021b21020243d80202186c0202029c02021d6c020238400202678b0202456302022c19020211d402024e6f020253e6020276480202640e02020265020231f50202341202024bf20202787f02023dc002027919020202ee02023f9402023c9202023ada020221a9020272e302025023020277a2020256b6020269dc020204ac0202643f020232c602020fc80202475f02023d73020215e402023c8602026fbc02024345020201c90202322d020229230202522302022b260202164402022af702025b9402020b2602020cf00202532802026c820202209f02022a2f02021cb002024f1202027acb020215c9020266cb020266d3020245be0202099b02027deb02027a5302020b7b02023d4e02025cfc0202438702022fbd0202540d0202312102027e5f020140020262ec020223cd020202c602027f77020221600202332302027ec802021e3602025c600202594302022fef020236f802023cf80202291602026b6a02023b39020237a5020232b2020257170202414102023d520202283b020274a502025f3a0202388f02022c7b020207290202723f0202454c0202289e02027ef0020226e00202251502027ab6020222170202090302022d8d020259fa02025c38020279c302021ffe020255af02027dd1020212b40202398e0202373202023c6b020261be020278e302020af1020222470202738302027eb00202178202025f010202752002021ee3020206e402027a5402020325020218cd02021d8502022e7e020227790202589002026c71020242a8020204e202025cad0202512d020271c202027f6802026d3002024196020223df020248560202667902024c58020260e4020207db02024a3e020209be020248be02024c6c0202169a020247280202528a0202774602027d1b02026944020246e2020239f40202226e02027e4e02022fe50202653502022ca702021afd02021f0702021221020276fc0202453002021bc6020232a2020245df020253c002021c4c020240780202197c02021411020250ba02022cd202020e5d0202080002027e3f020234a0020228760202786c020256db0202053f02020ae70202453e02022d2802024ea50202202002020ed7020253100202074b02023b3402025bd502021f7d02024857020215540202202102026db402027a4c020238560202265f020271d002027dcb0202294d02026a64020210950202697f020242a602022edf02023873020265bd02027e4c020213a9020234cf020232ad020226b602025fe002025d5f020256b2020253a60202336d02024dd602023da7020260c702021aa1020264b2020254110202221902021c900202384e02024eab020259990202642f0202292e0202026b0202029e0202138f02027bc10202178e0202757902027c6c02025ef40202195102022dd1020231870202592b020270e00202057402023a14020218490202487f02027d4902022dd7020221f402021a4d0202753e02022c15020238d202021e3f02023b9202020c3702020a7902022651020273a902026b5602024573020277350202042302027ab6020239100202609f020264450202552002021da7020242a50202769202022311020271b8020249d702026f87020271dd02023ab2020239d2
[0] r
[1] r+s
Choice: 1
r+s: 308209fd020206d202020ef60202200502025ab302024fbc0202319402022906020255fc02025dd80202101602023f480202201202022ae002023c260202310a02021348020213060202123e020262aa02025d5c02027e46020243d60202213c0202165b0202560802020f17020270f402025fc102025b3602026a4b02027eac02024c7f02021484020217e8020237b402020860020246df020265620202513c02020cad0202517002022f0602023e7702022a4d020223680202184302023b640202453a02022428020244f702026cb7020220550202259c02024aa1020256c102025095020260cd0202796a02024a8502024979020236b00202355502027695020141020217ff02023022020223af0202248c020275a402020c8b020206be020202ff02023ec102027e4e020256f902020fbb02027dbe02027d6802021ec50202049f02021e280202570a0202613702023314020243f6020227950202448902022a0602023a18020218180202385602026958020242e20202305c020266f6020266fd02021dd80202446c0202751202025d1c020249d6020221a202026f90020279e7020246740202124d0202053f020214d702020cac020209300202206402025c400202241e02026a2802021d6b02026bd5020269da02027c790202016302026b7d02026ad8020242ae02023a9502023f1d020237770202636c020256aa020260020202351802022b9902025179020203ca02025cc4020265190202085d02027d5802027363020276640202198b0202113802026db3020237c702024cf302024c9a020231510202012a020252e302020b7b02020ac302020a5102021cdb02024a75020264b3020204980202506a02025ebf0202340102022d3902020cf0020273450202067c0202722802027c1802026c8e0202316e0202715e0202427602023d5f020255d2020268dd02023a1c020207c60202470a020242cd02027f4b02026d0d0202754e02023a82020271b9020260b902022044020249e502020c2202024f9a020221920202256802025b7c0202715802025d5a02021d4b02025067020258fe02023e1b02020978020262e00202047802022d7f020278460202436d02021f5702022ee702023e8b0202483a02022c8202027043020205f20202168c020267c90202262b02027cb202023680020250650202060b0202349602021f92020261700202693402026f9f020223ff02020ab6020221250202203b02023f1202026cc7020267cf020257d002023d8702020bb50202158b02021ec6020255fa02025cd702025abb02023ecf02023e4d02022bbe020221e0020258360202530402026cbc02024afa0202055102022dc402023fc902025ce60202343f020234f3020229af02027fea020272ee02025e6c02027b210202647102020867020270d00202166602023c1d02026eb602024bd302022a9602023cb30202371c02022be402024bac0202510302027d090202230c02026a2c02022a0b0202742302021d4b0202236202023c92020270c30202437302026b95020238b8020218de020228100202645e0202533e02026a3302022fde02021b8b02025f440202015902021487020215cb02024e5b02022d8b02023ed70202223202023d12020246f4020212be020276cc02022d87020235670202038102027b3202022ea0020214b4020202f40202488502023b3c020233d60202272b02020f970202272d0202231f0202546302026e0c020206dc020262ad020266f9020211de020205e50202507002022c87020207770202634202026a0302023b66020215d4020268d902024b1002021c4702025a2d02023cae0202666a02024c9b02025f7e02025f7a0202408702021e5e0202730702026a7302021edc02026860020267ce02027a9b02020f5b02026e7c02027c70020250480202458a0202340002022a5f0202149f020252f5020204e40202653002025331020240460202712002026302020242d802027daf0202019d02021c1c0202369b020269be020257d602026b7102022b030202790602025f6e02022b79020272d0020231f502021e9802021a230202308202022b6202027ed402027d250202420202026ffc020278e3020236c502027e360202207f020257980202635302026f4c02023e59020267ae02022b67020229cf02023384020221fe02023d5b02023300020242860202605202020eab020207bc020214a002020da102021c2402023f1f02024e5102023dab0202444202021bbf020218300202047f02027e200202647f020204830202501002022dd00202511902020b130202693f020256e502020b05020223f902025b9b0202610d0202338802021d6a02021a76020200ae02027ee002021a3d02020b0a02022d59020208e10202524d02020921020143020267d4020202ea02020bff02021d7202021c6f02024045020236120202595d020212610202446a020222e2020221fd0202061602024bbe02020b8102020f1502025b4702024af402020dd2020266fc020265d302022137020261dc020272ac020265dc0202637302027ff502025c9f020253b9020202d602027eaa020201e00202147502026fe0020250460202107502021be002023dce02022d7d0202586702024a030202678b02024b92020229ce020211bf02021bf90202714c02023b3d020226b6020211d2020238ba02020f420202502e02027c3c020225dd020261960202377802021f9f0202476f0202373a0202655702020f390202709c020251960202467a0202010f0202351f02020b8b02022e6f02023ffe0202419002027db602026f4702027bb40202107e02027c8802026764020227e2020233ec02023f1f02023e2602021552020236c7020240db02027ee102025c990202073f020256820202179f02027304020236da0202737602020e610202629002027b8202022cc402020fa5020277ea02026b0b0202244f0202129d0202177402027b44020225aa02026bdf0202253f020279ae02026aa5020238ed0202651d0202406d02022da2020274e8020266b20202056d0202562a02021b0f02021d3e020261bc020227a1020238e202024e2e02020560020266890202296e020237fe0202018c020209e70202196e02027b6702024ef20202233e02024a7b02023c0b02020ed60202250a02024f4002025cf302021ea202020bb6020273dd0202698c02025d8f02027b1b02024679020224970202334d02021fb402024c1902020f7902027678020263a80202698a020272af020276ce02021e5602021eb2020242d20202139d02027d690202039f020224ff0202042b020264b502022246020232130202132002026136020127020221b602020cb7020250a10202496d0202441502021088020218a30202253b0202222e02026b66020246040202087a02026cc0020267d7020279f002022bf402021d1c02022e91020272cd020240a802025bb702022a87020277b1020226550202040a0202108a020241de0202483202024af5020208670202768c020271d60202039b020203fc020276900202360902020e2c020225e402021f8f

A*r + eは何回アクセスしても変わらない。rとr+sの値がわかるので、引き算すればsの値がわかる。あとはencryptは実質XORなので、もう一度同じようなことをすれば、フラグが得られる。

#!/usr/bin/env python3
import socket
import binascii
import numpy as np
from Crypto.Util.asn1 import DerSequence
from Crypto.Hash import SHAKE256
from Crypto.Util.strxor import strxor

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

def decrypt(s, enc):
    raw = bytes(np.mod(s, 256).tolist())
    shake = SHAKE256.new()
    shake.update(raw)
    pad = shake.read(len(enc))
    return strxor(enc, pad)

der = DerSequence()

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('54.159.113.26', 19003))

data = recvuntil(s, b'Choice: ')
print((data + b'0').decode())
s.sendall(b'0\n')
data = recvuntil(s, b'\n').rstrip()
print(data.decode())

der.decode(binascii.unhexlify(data.decode().split(': ')[1]))
r = np.array([c for c in der])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('54.159.113.26', 19003))

data = recvuntil(s, b'Choice: ')
print((data + b'1').decode())
s.sendall(b'1\n')
data = recvuntil(s, b'\n').rstrip()
print(data.decode())

der.decode(binascii.unhexlify(data.decode().split(': ')[1]))
rs = np.array([c for c in der])

s = rs - r

with open('flag.enc', 'rb') as f:
    enc = f.read()

flag = decrypt(s, enc)
print(flag.decode())
actf{deep_into_that_darkness_learning_with_errors_goes}

Powerball (CRYPTO 200)

スクリプトの処理の概要は以下の通り。

balls: 0-4095のランダム整数値 6個
x    : 0-nのランダム整数値 6個
x表示
v入力

以下6回繰り返し
k = pow(v-x[i], d, n)
m.append((balls[i]+k) % n)
m表示

式を変えてballsを計算しやすくする。

m[i] = (balls[i] + pow(v-x[i], d, n)) % n
⇒m[i] - balls[i] = pow(v-x[i], d, n)
⇒pow(m[i] - balls[i], e, n) = v-x[i]

balls総当たりで上記が一致するものを探す。

import socket

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

with open('public.txt', 'r') as f:
    n = int(f.readline()[3:])
    e = int(f.readline()[3:])

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('54.159.113.26', 19001))

data = recvuntil(s, 'v: ')
x = eval(data.split('\n')[-3].split(': ')[1])
v = max(x)
print data + str(v)
s.sendall(str(v) + '\n')

data = recvuntil(s, '\n').rstrip()
print data
m = eval(data.split(': ')[1])

for i in range(6):
    data = recvuntil(s, ': ')
    for b in range(4096):
        if pow(m[i] - b, e, n) == v - x[i]:
            print data + str(b)
            s.sendall(str(b) + '\n')
            break

data = recvuntil(s, '\n').rstrip()
print data
data = recvuntil(s, '\n').rstrip()
print data
actf{no_more_free_oblivious_transfers}

MAC Forgery (CRYPTO 220)

サーバの処理の流れは以下の通り。

・IV生成、welcomeメッセージのMACを生成
・IV+MAC表示
・Message入力(16進数)
・MAC入力(16進数):32バイト
・MessageとMACが妥当だとフラグ表示

mac()
・m
 ・パディング
 ・16バイトずつの配列に変換
 ・配列の先頭に配列数のバイト列を挿入
・t
 ・ivを設定
 ・iv   ^ m[0] -> c[0]
  c[0] ^ m[1] -> c[1]
  c[1] ^ m[2] -> c[2]

得られるMACは以下のようなイメージで得られる。

m[0]:サイズをビッグエンディアンで16バイトにしている。

iv   ^ m[0] -> c[0]
c[0] ^ m[1] -> c[1]
c[1] ^ m[2] -> c[2]
c[2] ^ m[3] -> c[3]
c[3] ^ m[4] -> c[4]
c[4] ^ m[5] -> c[5]
c[5] ^ m[6] -> c[6]
c[6] ^ m[7] -> c[7] -> MAC

サイズを7から15にし、以下のようになるよう、IVと平文を調整する。

iv   ^ s15  -> c[0]
c[0] ^ m[1] -> c[1]
c[1] ^ m[2] -> c[2]
c[2] ^ m[3] -> c[3]
c[3] ^ m[4] -> c[4]
c[4] ^ m[5] -> c[5]
c[5] ^ m[6] -> c[6]
c[6] ^ m[7] -> c[7]
c[7] ^ m[8] -> c[0]
c[0] ^ m[1] -> c[1]
c[1] ^ m[2] -> c[2]
c[2] ^ m[3] -> c[3]
c[3] ^ m[4] -> c[4]
c[4] ^ m[5] -> c[5]
c[5] ^ m[6] -> c[6]
c[6] ^ m[7] -> c[7]
import socket
import binascii
from Crypto.Util.number import long_to_bytes

BLOCK_SIZE = 16

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

def str_xor(s1, s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a, b in zip(s1, s2))

def pad(s):
    c = BLOCK_SIZE - len(s) % BLOCK_SIZE
    return s + chr(c) * c

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('54.159.113.26', 19002))

welcome = b'''\
If you provide a message (besides this one) with
a valid message authentication code, I will give
you the flag.'''

data = recvuntil(s, 'Message: ')
mac = data.split('\n')[-2].split(': ')[1]
iv = binascii.unhexlify(mac)[:16]
t = binascii.unhexlify(mac)[16:]

#### calculate new iv ####
org_pt0 = long_to_bytes(7, BLOCK_SIZE)
new_pt0 = long_to_bytes(15, BLOCK_SIZE)
new_iv = str_xor(str_xor(org_pt0, iv), new_pt0)

#### calculate 8th block of plaintext (begin 0) ####
new_pt8 = str_xor(str_xor(org_pt0, iv), t)

#### create message ####
new_msg = binascii.hexlify(pad(welcome) + new_pt8 + welcome)
print data + new_msg
s.sendall(new_msg + '\n')

#### create mac ####
new_mac = binascii.hexlify(new_iv + t)
data = recvuntil(s, 'MAC: ')
print data + new_mac
s.sendall(new_mac + '\n')

data = recvuntil(s, '\n')
print data
actf{initialization_vectors_were_probably_a_bad_idea}