CTF Beginners Contest 2021 参戦記
2021年5月22日〜2021年5月23日に開催されていた CTF Beginners Contest 2021のWrite-upを記載します。 今年は参加チーム数が多く、最終的な参加チーム数は計 1095 チームでした。 所感として、例年よりとっつきやすい問題が多かったです。(私を含む)初心者のために間口を広げてくれたのでしょうか。 私は個人で参加し、結果は 200位/1095チーム でした。個人ながら最終的に上位20%に入れたので、まあまずまずの成績といったところでしょうか。
問題一覧は、こちらから閲覧できます(スコアサーバへの登録が必要かも?)。
crypto
simple_RSA
問題
output.txt
とproblem.py
という2つのファイルから、flagを復元せよ。
output.txt
n = 17686671842400393574730512034200128521336919569735972791676605056286778473230718426958508878942631584704817342304959293060507614074800553670579033399679041334863156902030934895197677543142202110781629494451453351396962137377411477899492555830982701449692561594175162623580987453151328408850116454058162370273736356068319648567105512452893736866939200297071602994288258295231751117991408160569998347640357251625243671483903597718500241970108698224998200840245865354411520826506950733058870602392209113565367230443261205476636664049066621093558272244061778795051583920491406620090704660526753969180791952189324046618283 e = 3 c = 213791751530017111508691084168363024686878057337971319880256924185393737150704342725042841488547315925971960389230453332319371876092968032513149023976287158698990251640298360876589330810813199260879441426084508864252450551111064068694725939412142626401778628362399359107132506177231354040057205570428678822068599327926328920350319336256613
problem.py
from Crypto.Util.number import * from flag import flag flag = bytes_to_long(flag.encode("utf-8")) p = getPrime(1024) q = getPrime(1024) n = p * q e = 3 assert 2046 < n.bit_length() assert 375 == flag.bit_length() print("n =", n) print("e =", e) print("c =", pow(flag, e, n))
解答
典型的なRSA暗号の問題です。 注目すべきところは、
e=3
assert 2046 < n.bit_length()
assert 375 == flag.bit_length()
の3つです。 flagをとすると、上記の情報から、 が よりも小さいことが分かります。 つまり、が成立します。 あとは、を計算してやればOKです。
3乗根を求める部分に関して、私は二分探索で解の導出を行いました。以下、解答プログラムです。
from Crypto.Util.number import * # 3乗根を求めるプログラム (二分探索) def cubit_root(c): low = 0 high = c while True: half = (low + high) // 2 if (half**3 == c): return half elif (half**3 < c): low = half else: high = half flag = cubit_root(213791751530017111508691084168363024686878057337971319880256924185393737150704342725042841488547315925971960389230453332319371876092968032513149023976287158698990251640298360876589330810813199260879441426084508864252450551111064068694725939412142626401778628362399359107132506177231354040057205570428678822068599327926328920350319336256613) print(long_to_bytes(flag))
答え:ctf4b{0,1,10,11...It's_so_annoying.___I'm_done}
Logical_SEESAW
問題
output.txt
とproblem.py
から、flagを復元せよ。
output.txt
cipher = ['11000010111010000100110001000000010001001011010000000110000100000100011010111110000010100110000001101110100110100100100010000110001001101000101010000010101000100000001010100010010010001011110001101010110100000010000010111110000010100010100011010010100010001001111001101010011000000101100', '11000110110010000100110001000000010001001111010010000110110000000110011010101110011010100110000010100110101100000100000000001110001001001000101000001010101001100000001010101010010110001001110001101010110100000110011010010110011000000100100011010000110010001001011001101010011000001101100', '11000010110010000100110001101000010001001111001000000110000100000110011000111110010010100110000000101110101101100000000010010110001001101000101010000010101000100000001000101110000010001001111001101010110100000010011010010110011000100000100011010010100010001001111001101010011000001101100', '11000110110010001100110000101000110001000111000000000110000000000110011010101110011000100110000010100110101101100100100010000100101001001000101010000010101000100000001010100110010010001001110001101010110000000110000010110110000000000000100011010010110010001011011001101010001000000111101', '11000110100010001100110000000000010001000111001010000110110100000110011010111110001010100110100011101110101110100010000000110100001001101000101010000010101001101000001000101000010010001001111001101010110000000010000010111110000000000010000011010010100010001001011001101010011000001111101', '11000010110010001100110001001000010001000011000000100110000000000110011010101110000010100110100011100110101110100010000000101100101001101000101010000010101001101000001010100010000010001011111001101010110100000110001010010110001010100100000011010010110010001011111001101010011000000101100', '11000110110010000100110001101000110001001011010000100110110000000110011000101110010000100110100001100110100110000000100010000110101001001000101010000010101001100000001000101110010010001011111001101010110000000010001010110110001010100110000011010000110010001011111001101010011000000101100', '11000010110010000100110000100000010001000111011000100110100000000110011000111110000010100110000001101110101111100100000010111110001001001000101000001010101001101000001000101010000110001011110001101010110000000110001010011110000010100010100011010010110010000011011001101010001000000111100', '11000010101010000100110001001000010001000011000000100110010000000100011000111110011000100110000001100110100101000010000000011100101001101000101000001010101001101000001010101110010110001001110001101010110000000010010010110110011000000010100011010000100010001011111001101010001000000101100', '11000010101010001100110000100000010001001111001010000110000000000100011010101110011000100110000011100110100111100110100000000110001001001000101010000010101000100000001000101100010010001011110001101010110000000110011010010110011010000000000011010010100010001011011001101010011000001101101', '11000010101010001100110001000000010001001011010010000110010100000100011000111110011000100110000010100110100111000100000000000100101001101000101010001010101000100000001000100000000110001001111001101010110000000110011010010110000010000100100011010000110010000011011001101010001000001101100', '11000110101010001100110001000000110001001111001010000110110000000110011010101110011000100110100001100110101111000100100010011110101001001000101010001010101000101000001000101100000110001011111001101010110100000010011010011110001000000100100011010010100010000001011001101010011000001111100', '11000110100010001100110001000000010001001011011010100110000000000100011000101110001000100110100001101110101101000110100010001100101001001000101010000010101000100000001010101100000010001001111001101010110100000110011010010110010000100110100011010010110010001001111001101010011000001101101', '11000110101010000100110000000000010001001111001010100110100100000100011010111110001000100110100001101110101100000000100000111110001001101000101000001010101001101000001010100110010010001011110001101010110100000110000010010110001010000010100011010010110010001001011001101010001000000101100', '11000010101010000100110000000000110001001011011010100110110000000110011000101110010010100110100000100110101111000010000000100100001001001000101000001010101001100000001000100000000010001011110001101010110000000010011010011110001010000000000011010010100010001001011001101010001000000101101', '11000110101010001100110001000000110001001111011000000110010100000100011000101110001010100110000001101110101110000100100000101110101001101000101000000010101000100000001010101010000010001011110001101010110000000010000010010110001000100100100011010000100010000001011001101010001000001111101']
problem.py
from Crypto.Util.number import * from random import random, getrandbits from flag import flag flag = bytes_to_long(flag.encode("utf-8")) length = flag.bit_length() key = getrandbits(length) while not length == key.bit_length(): key = getrandbits(length) flag = list(bin(flag)[2:]) key = list(bin(key)[2:]) cipher_L = [] for _ in range(16): cipher = flag[:] m = 0.5 for i in range(length): n = random() if n > m: cipher[i] = str(eval(cipher[i] + "&" + key[i])) cipher_L.append("".join(cipher)) print("cipher =", cipher_L)
解答
for文で何をやっているか分かれば、特に難しくない問題です。
for文では、cipher(=flag)のビットをある確率に基づいて0にする試行を16回行っています。
1回の試行につき、1/2の確率で、cipher[i] = str(eval(cipher[i] + "&" + key[i]))
が実行され、さらに確率1/2の確率(トータルで1/4の確率)でcipher[i]の値が0になってしまいます。
ですが、当然のことながら、もともとcipher[i]の値が0であれば、ずっとcipher[i]の値は0です。もともとcipher[i]の値が1であれば、たまに値が0になってしまうケースもありますが、3/4の確率でcipher[i]は1のままです。
従って、リストcipher_Lの各要素において、ビット位置iで一回でも1が登場したことがあれば、flag[i] = 1、一回も1が登場しなかったらflag[i] = 0となります。
以下、解答プログラムです。
from Crypto.Util.number import * cipher = ['11000010111010000100110001000000010001001011010000000110000100000100011010111110000010100110000001101110100110100100100010000110001001101000101010000010101000100000001010100010010010001011110001101010110100000010000010111110000010100010100011010010100010001001111001101010011000000101100', '11000110110010000100110001000000010001001111010010000110110000000110011010101110011010100110000010100110101100000100000000001110001001001000101000001010101001100000001010101010010110001001110001101010110100000110011010010110011000000100100011010000110010001001011001101010011000001101100', '11000010110010000100110001101000010001001111001000000110000100000110011000111110010010100110000000101110101101100000000010010110001001101000101010000010101000100000001000101110000010001001111001101010110100000010011010010110011000100000100011010010100010001001111001101010011000001101100', '11000110110010001100110000101000110001000111000000000110000000000110011010101110011000100110000010100110101101100100100010000100101001001000101010000010101000100000001010100110010010001001110001101010110000000110000010110110000000000000100011010010110010001011011001101010001000000111101', '11000110100010001100110000000000010001000111001010000110110100000110011010111110001010100110100011101110101110100010000000110100001001101000101010000010101001101000001000101000010010001001111001101010110000000010000010111110000000000010000011010010100010001001011001101010011000001111101', '11000010110010001100110001001000010001000011000000100110000000000110011010101110000010100110100011100110101110100010000000101100101001101000101010000010101001101000001010100010000010001011111001101010110100000110001010010110001010100100000011010010110010001011111001101010011000000101100', '11000110110010000100110001101000110001001011010000100110110000000110011000101110010000100110100001100110100110000000100010000110101001001000101010000010101001100000001000101110010010001011111001101010110000000010001010110110001010100110000011010000110010001011111001101010011000000101100', '11000010110010000100110000100000010001000111011000100110100000000110011000111110000010100110000001101110101111100100000010111110001001001000101000001010101001101000001000101010000110001011110001101010110000000110001010011110000010100010100011010010110010000011011001101010001000000111100', '11000010101010000100110001001000010001000011000000100110010000000100011000111110011000100110000001100110100101000010000000011100101001101000101000001010101001101000001010101110010110001001110001101010110000000010010010110110011000000010100011010000100010001011111001101010001000000101100', '11000010101010001100110000100000010001001111001010000110000000000100011010101110011000100110000011100110100111100110100000000110001001001000101010000010101000100000001000101100010010001011110001101010110000000110011010010110011010000000000011010010100010001011011001101010011000001101101', '11000010101010001100110001000000010001001011010010000110010100000100011000111110011000100110000010100110100111000100000000000100101001101000101010001010101000100000001000100000000110001001111001101010110000000110011010010110000010000100100011010000110010000011011001101010001000001101100', '11000110101010001100110001000000110001001111001010000110110000000110011010101110011000100110100001100110101111000100100010011110101001001000101010001010101000101000001000101100000110001011111001101010110100000010011010011110001000000100100011010010100010000001011001101010011000001111100', '11000110100010001100110001000000010001001011011010100110000000000100011000101110001000100110100001101110101101000110100010001100101001001000101010000010101000100000001010101100000010001001111001101010110100000110011010010110010000100110100011010010110010001001111001101010011000001101101', '11000110101010000100110000000000010001001111001010100110100100000100011010111110001000100110100001101110101100000000100000111110001001101000101000001010101001101000001010100110010010001011110001101010110100000110000010010110001010000010100011010010110010001001011001101010001000000101100', '11000010101010000100110000000000110001001011011010100110110000000110011000101110010010100110100000100110101111000010000000100100001001001000101000001010101001100000001000100000000010001011110001101010110000000010011010011110001010000000000011010010100010001001011001101010001000000101101', '11000110101010001100110001000000110001001111011000000110010100000100011000101110001010100110000001101110101110000100100000101110101001101000101000000010101000100000001010101010000010001011110001101010110000000010000010010110001000100100100011010000100010000001011001101010001000001111101'] print("len(cipher) = {0}".format(len(cipher))) counter_list = [0]*287 for item in cipher: for index, c in enumerate(item): counter_list[index] += int(c) print(counter_list) ans = "" for i in counter_list: if (i > 0): ans += "1" else: ans += "0" print(long_to_bytes(int(ans, 2)))
答え:ctf4b{Sh3_54w_4_SEESAW,_5h3_54id_50}
GFM
問題
output.txt
とproblem.sage
から、flagを復元せよ。
output.txt
p: 331941721759386740446055265418196301559 key: [116401981595413622233973439379928029316 198484395131713718904460590157431383741 210254590341158275155666088591861364763 63363928577909853981431532626692827712 85569529885869484584091358025414174710 149985744539791485007500878301645174953 257210132141810272397357205004383952828 184416684170101286497942970370929735721] [ 42252147300048722312776731465252376713 199389697784043521236349156255232274966 310381139154247583447362894923363190365 275829263070032604189578502497555966953 292320824376999192958281274988868304895 324921185626193898653263976562484937554 22686717162639254526255826052697393472 214359781769812072321753087702746129144] [211396100900282889480535670184972456058 210886344415694355400093466459574370742 186128182857385981551625460291114850318 13624871690241067814493032554025486106 255739890982289281987567847525614569368 134368979399364142708704178059411420318 277933069920652939075272826105665044075 61427573037868265485473537350981407215] [282725280056297471271813862105110111601 183133899330619127259299349651040866360 275965964963191627114681536924910494932 290264213613308908413657414549659883232 140491946080825343356483570739103790896 115945320124815235263392576250349309769 240154953119196334314982419578825033800 33183533431462037262108359622963646719] [ 53797381941014407784987148858765520206 136359308345749561387923094784792612816 26225195574024986849888325702082920826 262047729451988373970843409716956598743 170482654414447157611638420335396499834 270894666257247100850080625998081047879 91361079178051929124422796293638533509 34320536938591553179352522156012709152] [266361407811039627958670918210300057324 40603082064365173791090924799619398850 253357188908081828561984991424432114534 322939245175391203579369607678957356656 63315415224740483660852444003806482951 224451355249970249493628425010262408466 80574507596932581147177946123110074284 135660472191299636620089835364724566497] [147031054061160640084051220440591645233 286143152686211719101923153591621514114 330366815640573974797084150543488528130 144943808947651161283902116225593922999 205798118501774672701619077143286382731 317326656225121941341827388220018201533 14319175936916841467976601008623679266 112709661623759566156255015500851204670] [306746575224464214911885995766809188593 35156534122767743923667417474200538878 35608800809152761271316580867239668942 259728427797578488375863755690441758142 29823482469997458858051644485250558639 137507773879704381525141121774823729991 29893063272339035080311541822496817623 292327683738678589950939775184752636265] enc: [133156758362160693874249080602263044484 293052519705504374237314478781574255411 72149359944851514746901936133544542235 56884023532130350649269153560305458687 67693140194970657150958369664873936730 227562364727203645742246559359263307899 98490363636066788474326997841084979092 323336812987530088571937131837711189774] [244725074927901230757605861090949184139 63515536426726760809658259528128105864 297175420762447340692787685976316634653 279269959863745528135624660183844601533 203893759503830977666718848163034645395 163047775389856094351865609811169485260 103694284536703795013187648629904551283 322381436721457334707426033205713602738] [ 17450567396702585206498315474651164931 105594468721844292976534833206893170749 10757192948155933023940228740097574294 132150825033376621961227714966632294973 329990437240515073537637876706291805678 57236499879418458740541896196911064438 265417446675313880790999752931267955356 73326674854571685938542290353559382428] [270340230065315856318168332917483593198 217815152309418487303753027816544751231 55738850736330060752843300854983855505 236064119692146789532532278818003671413 104963107909414684818161043267471013832 234439803801976616706759524848279829319 173296466130000392237506831379251781235 34841816336429947760241770816424911200] [140341979141710030301381984850572416509 248997512418753861458272855046627447638 58382380514192982462591686716543036965 188097853050327328682574670122723990784 125356457137904871005571726686232857387 55692122688357412528950240580072267902 21322427002782861702906398261504812439 97855599554699774346719832323235463339] [298368319184145017709393597751160602769 311011298046021018241748692366798498529 165888963658945943429480232453040964455 240099237723525827201004876223575456211 306939673050020405511805882694537774846 7035607106089764511604627683661079229 198278981512146990284619915272219052007 255750707476361671578970680702422436637] [ 45315424384273600868106606292238082349 22526147579041711876519945055798051695 15778025992115319312591851693766890019 318446611756066795522259881812628512448 269954638404267367913546070681612869355 205423708248276366495211174184786418791 92563824983279921050396256326760929563 209843107530597179583072730783030298674] [ 662653811932836620608984350667151180 304181885849319274230319044357612000272 280045476178732891877948766225904840517 216340293591880460916317821948025035163 79726526647684009633247003110463447210 36010610538790393011235704307570914178 284067290617158853279270464803256026349 45816877317461535723616457939953776625]
problem.sage
FLAG = b'<censored>' SIZE = 8 p = random_prime(2^128) MS = MatrixSpace(GF(p), SIZE) print(MS) key = MS.random_element() while key.rank() != SIZE: key = MS.random_element() M = copy(MS.zero()) for i in range(SIZE): for j in range(SIZE): n = i * SIZE + j if n < len(FLAG): M[i, j] = FLAG[n] else: print(i, j, SIZE, len(FLAG)) M[i, j] = GF(p).random_element() enc = key * M * key inv_key = key.inverse() print('p:', p) print('key:', key) print('enc:', enc)
解答
sageという見かけないファイルが出てきました。 調べると、
Sage は,代数学,幾何学,数論,暗号理論,数値解析,および関連諸分野の研究と教育を支援する,フリーなオープンソース数学ソフトウェアである (https://doc.sagemath.org/pdf/ja/tutorial/tutorial-jp.pdf)
と出てきました。
プログラムを見てみると、どうやら行列の計算をしてるっぽいですね。
とりあえず、M
を復元するために、見よう見まねでsageプログラムを書いてみました。
SIZE = 8 p = 331941721759386740446055265418196301559 key = matrix(GF(p), [[116401981595413622233973439379928029316, 198484395131713718904460590157431383741, 210254590341158275155666088591861364763, 63363928577909853981431532626692827712, 85569529885869484584091358025414174710, 149985744539791485007500878301645174953, 257210132141810272397357205004383952828, 184416684170101286497942970370929735721], [42252147300048722312776731465252376713, 199389697784043521236349156255232274966, 310381139154247583447362894923363190365, 275829263070032604189578502497555966953, 292320824376999192958281274988868304895, 324921185626193898653263976562484937554, 22686717162639254526255826052697393472, 214359781769812072321753087702746129144], [211396100900282889480535670184972456058, 210886344415694355400093466459574370742, 186128182857385981551625460291114850318, 13624871690241067814493032554025486106, 255739890982289281987567847525614569368, 134368979399364142708704178059411420318, 277933069920652939075272826105665044075, 61427573037868265485473537350981407215], [282725280056297471271813862105110111601, 183133899330619127259299349651040866360, 275965964963191627114681536924910494932, 290264213613308908413657414549659883232, 140491946080825343356483570739103790896, 115945320124815235263392576250349309769, 240154953119196334314982419578825033800, 33183533431462037262108359622963646719], [ 53797381941014407784987148858765520206, 136359308345749561387923094784792612816, 26225195574024986849888325702082920826, 262047729451988373970843409716956598743, 170482654414447157611638420335396499834, 270894666257247100850080625998081047879, 91361079178051929124422796293638533509, 34320536938591553179352522156012709152], [266361407811039627958670918210300057324, 40603082064365173791090924799619398850, 253357188908081828561984991424432114534, 322939245175391203579369607678957356656, 63315415224740483660852444003806482951, 224451355249970249493628425010262408466, 80574507596932581147177946123110074284, 135660472191299636620089835364724566497], [147031054061160640084051220440591645233, 286143152686211719101923153591621514114, 330366815640573974797084150543488528130, 144943808947651161283902116225593922999, 205798118501774672701619077143286382731, 317326656225121941341827388220018201533, 14319175936916841467976601008623679266, 112709661623759566156255015500851204670], [306746575224464214911885995766809188593, 35156534122767743923667417474200538878, 35608800809152761271316580867239668942, 259728427797578488375863755690441758142, 29823482469997458858051644485250558639, 137507773879704381525141121774823729991, 29893063272339035080311541822496817623, 292327683738678589950939775184752636265]], ) enc = matrix(GF(p), [[133156758362160693874249080602263044484, 293052519705504374237314478781574255411, 72149359944851514746901936133544542235, 56884023532130350649269153560305458687, 67693140194970657150958369664873936730, 227562364727203645742246559359263307899, 98490363636066788474326997841084979092, 323336812987530088571937131837711189774], [244725074927901230757605861090949184139, 63515536426726760809658259528128105864, 297175420762447340692787685976316634653, 279269959863745528135624660183844601533, 203893759503830977666718848163034645395, 163047775389856094351865609811169485260, 103694284536703795013187648629904551283, 322381436721457334707426033205713602738], [ 17450567396702585206498315474651164931, 105594468721844292976534833206893170749, 10757192948155933023940228740097574294, 132150825033376621961227714966632294973, 329990437240515073537637876706291805678, 57236499879418458740541896196911064438, 265417446675313880790999752931267955356, 73326674854571685938542290353559382428], [270340230065315856318168332917483593198, 217815152309418487303753027816544751231, 55738850736330060752843300854983855505, 236064119692146789532532278818003671413, 104963107909414684818161043267471013832, 234439803801976616706759524848279829319, 173296466130000392237506831379251781235, 34841816336429947760241770816424911200], [140341979141710030301381984850572416509, 248997512418753861458272855046627447638, 58382380514192982462591686716543036965, 188097853050327328682574670122723990784, 125356457137904871005571726686232857387, 55692122688357412528950240580072267902, 21322427002782861702906398261504812439, 97855599554699774346719832323235463339], [298368319184145017709393597751160602769, 311011298046021018241748692366798498529, 165888963658945943429480232453040964455, 240099237723525827201004876223575456211, 306939673050020405511805882694537774846, 7035607106089764511604627683661079229, 198278981512146990284619915272219052007, 255750707476361671578970680702422436637], [ 45315424384273600868106606292238082349, 22526147579041711876519945055798051695, 15778025992115319312591851693766890019, 318446611756066795522259881812628512448, 269954638404267367913546070681612869355, 205423708248276366495211174184786418791, 92563824983279921050396256326760929563, 209843107530597179583072730783030298674], [ 662653811932836620608984350667151180, 304181885849319274230319044357612000272, 280045476178732891877948766225904840517, 216340293591880460916317821948025035163, 79726526647684009633247003110463447210, 36010610538790393011235704307570914178, 284067290617158853279270464803256026349, 45816877317461535723616457939953776625]] ) inv_key = key.inverse() M = inv_key * enc * inv_key print(M)
すると、
[ 99 116 102 52 98 123 100 49] [ 100 95 121 48 117 95 112 108] [ 52 121 95 119 49 116 104 95] [ 109 52 116 114 49 120 95 52] [ 110 100 95 103 52 108 48 105] [ 115 95 102 49 101 108 100 63] [ 125 299874036478097449921680569674270449076 78313956149379326243971992208332842951 327226201592994132731095913667371497964 313102235287242360753049665908275948842 67049960660712250081503091849025260343 248290952297957062940275450722399860860 269291320227258725106609060732491009052] # [220658601671619686519292673663329962459 301252534665220612936717033315754638138 195965589989033004660898442625447000198 160757164035005766598705084730459536481 112986659451024798658573623593539960466 29597742012660940046364385563937276149 217053364052085783101705935012612334241 148296482483328225203414489314538575852]
答えっぽいもの(asciiの十進値)が出てきたので、復元してみます。以下の解答プログラムで、flagを復元できました。
ans_list = [99, 116, 102, 52, 98, 123, 100, 49, 100, 95, 121, 48, 117, 95, 112, 108, 52, 121, 95, 119, 49, 116, 104, 95, 109, 52, 116, 114, 49, 120, 95, 52, 110, 100, 95, 103, 52, 108, 48, 105, 115, 95, 102, 49, 101, 108, 100, 63, 125] for c in ans_list: print(chr(c), end="")
答え:ctf4b{d1d_y0u_pl4y_w1th_m4tr1x_4nd_g4l0is_f1eld?}
Web
osoba
問題
美味しいお蕎麦を食べたいですね。フラグはサーバの /flag にあります! https://osoba.quals.beginners.seccon.jp/
サーバ側のプログラム https://beginners-dist-production.s3.isk01.sakurastorage.jp/osoba/osoba.tar.gz
解答
サーバ側のflaskプログラムを見てみます。
from flask import Flask, request, send_file, make_response app = Flask(__name__) @app.route("/", methods=["GET", "POST"]) def index(): page = request.args.get('page', 'public/index.html') response = make_response(send_file(page)) response.content_type = "text/html" return response if __name__ == '__main__': app.run(host="0.0.0.0", port=8080)
注目すべきところは、page = request.args.get('page', 'public/index.html')
です。
pageクエリパラメータに、ファイルパスを通してやれば、その値が変数pageに格納され、該当ファイルが返されます。
また、サーバ側のディレクトリ構造は以下の通りです。
. ├── app │ ├── Dockerfile │ ├── flag // 対象のflagファイル │ └── src │ ├── app.py │ ├── public │ │ ├── index.html │ │ ├── kikin.html │ │ ├── neck.html │ │ └── wip.html │ ├── requirements.txt │ └── uwsgi.ini ├── docker-compose.yml └── nginx └── nginx.conf
対象のflagファイルは、../flag
に存在しているので、https://osoba.quals.beginners.seccon.jp/?page=../flagでアクセスしてやれば、フラグが返ってきます。
答え:ctf4b{omisoshiru_oishi_keredomo_tsukuruno_taihen}
Werewolf
問題
I wish I could play as a werewolf...
https://werewolf.quals.beginners.seccon.jp/
サーバ側プログラム https://beginners-dist-production.s3.isk01.sakurastorage.jp/werewolf/app.py
解答
サーバ側プログラムを見てみます。
import os import random from flask import Flask, render_template, request, session # ==================== app = Flask(__name__) app.FLAG = os.getenv("CTF4B_FLAG") # ==================== class Player: def __init__(self): self.name = None self.color = None self.__role = random.choice(['VILLAGER', 'FORTUNE_TELLER', 'PSYCHIC', 'KNIGHT', 'MADMAN']) # :-) # self.__role = random.choice(['VILLAGER', 'FORTUNE_TELLER', 'PSYCHIC', 'KNIGHT', 'MADMAN', 'WEREWOLF']) @property def role(self): return self.__role # :-) # @role.setter # def role(self, role): # self.__role = role # ==================== @app.route("/", methods=["GET", "POST"]) def index(): if request.method == 'GET': return render_template('index.html') if request.method == 'POST': player = Player() for k, v in request.form.items(): player.__dict__[k] = v return render_template('result.html', name=player.name, color=player.color, role=player.role, flag=app.FLAG if player.role == 'WEREWOLF' else '' ) # ==================== if __name__ == '__main__': app.run(host=os.getenv("CTF4B_HOST"), port=os.getenv("CTF4B_PORT"))
注目すべきところは、flag=app.FLAG if player.role == 'WEREWOLF' else ''
です。
どうやら、player.role
に WEREWOLF
という文字を仕込めたらOKらしいです。
player.roleに値を代入する作業は、以下のコードで行われています。
for k, v in request.form.items(): player.__dict__[k] = v
よって、form-dataに、_Player_role = WEREWOLF
を仕込んでリクエストを送信します。するとフラグを抽出できます。
答え:ctf4b{there_are_so_many_hackers_among_us}
check_url
問題
Have you ever used curl ? https://check-url.quals.beginners.seccon.jp/
サーバ側プログラム https://beginners-dist-production.s3.isk01.sakurastorage.jp/check_url/index.php
解答
サーバ側プログラムを見てみます。
<!-- HTML Template --> <?php error_reporting(0); if ($_SERVER["REMOTE_ADDR"] === "127.0.0.1"){ echo "Hi, Admin or SSSSRFer<br>"; echo "********************FLAG********************"; }else{ echo "Here, take this<br>"; $url = $_GET["url"]; if ($url !== "https://www.example.com"){ $url = preg_replace("/[^a-zA-Z0-9\/:]+/u", "👻", $url); //Super sanitizing } if(stripos($url,"localhost") !== false || stripos($url,"apache") !== false){ die("do not hack me!"); } echo "URL: ".$url."<br>"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 2000); curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); echo "<iframe srcdoc='"; curl_exec($ch); echo "' width='750' height='500'></iframe>"; curl_close($ch); } ?> <!-- HTML Template -->
どうやら、サーバからサーバにアクセス($_SERVER["REMOTE_ADDR"] === "127.0.0.1"
)させることができれば、フラグをゲットできるみたいです。
ただプログラムを見てみると、.
が👻に変換させられたり、ループバックアドレスlocalhost
を入力できなくされています。つまり、単純にurlクエリパラメータとして、127.0.0.1
やlocalhost
を入力しても、$_SERVER["REMOTE_ADDR"] === "127.0.0.1"
とはなりません。
そこで私は、ループバックアドレスを違う形で表せないかと考えました。調べてたら、以下の記事が出てきました。
どうやら、0x7F000001
でlocalhostにアクセスできるみたい。これなら、ピリオドも含まれていないし、localhost
やapache
というワードも含まれていないので、うまく通りそう。
実際に、https://check-url.quals.beginners.seccon.jp/?url=https://0x7F000001 と入力してみると、フラグが出てきました。
答え:ctf4b{5555rf_15_53rv3r_51d3_5up3r_54n171z3d_r3qu357_f0r63ry}
misc
git-leak
問題
後輩が誤って機密情報をコミットしてしまったらしいです。ひとまずコミットを上書きして消したからこれで大丈夫ですよね?
https://beginners-dist-production.s3.isk01.sakurastorage.jp/git-leak/git-leak.zip
解答
git reflog
して、flag.txt
が含まれているコミットまで遡って終わり。
$ git cat-file -p 4cbb035d2ff072127b4e22919485127d2273e88e
答え:ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}
mail_adress_validator
問題
あなたのメールアドレスが正しいか調べます.
nc mail-address-validator.quals.beginners.seccon.jp 5100
プログラム https://beginners-dist-production.s3.isk01.sakurastorage.jp/Mail_Address_Validator/main.rb
解答
プログラムを見てみます。
#!/usr/bin/env ruby require 'timeout' $stdout.sync = true $stdin.sync = true pattern = /\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i begin Timeout.timeout(60) { Process.wait Process.fork { puts "I check your mail address." puts "please puts your mail address." input = gets.chomp begin Timeout.timeout(5) { if input =~ pattern puts "Valid mail address!" else puts "Invalid mail address!" end } rescue Timeout::Error exit(status=14) end } print(Process.last_status.to_i) case Process.last_status.to_i >> 8 when 0 then puts "bye." when 1 then puts "bye." when 14 then File.open("flag.txt", "r") do |f| puts f.read end else puts "What's happen?" end } rescue Timeout::Error puts "bye." end
ここでポイントなのは、以下の部分です。
... begin Timeout.timeout(5) { if input =~ pattern puts "Valid mail address!" else puts "Invalid mail address!" end } rescue Timeout::Error exit(status=14) end ... when 14 then File.open("flag.txt", "r") do |f| puts f.read end else ...
入力したメールアドレスがパターンマッチしているかどうかの判定に、5秒以上要せば、status=14でexitされます。 exitされると、フラグを抽出できます。
では、どうやって判定を5秒以上にすることができるのでしょう。 これは知識ゲーなのですが、ReDoS攻撃を知っていれば簡単に解法が思いつきます。
ReDoS攻撃については、以下の記事を参照してください。 qiita.com
よってメールアドレスとして、AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@
等を入力してみると、以下のフラグをゲットできます。
答え:ctf4b{1t_15_n0t_0nly_th3_W3b_th4t_15_4ff3ct3d_by_ReDoS}