🎉

ImaginaryCTF Web[Methodical] Writeup

2022/10/14に公開

ImaginaryCTF Web[Methodical]

ソースコードはこちら

     1	#!/usr/bin/env python3
       
     2	from flask import Flask, request, Response, redirect, GET
     3	from flask_limiter import Limiter
     4	from flask_limiter.util import get_remote_address
       
     5	app = Flask(__name__)
     6	limiter = Limiter(
     7	    app,
     8	    key_func=get_remote_address,
     9	    default_limits=["50000 per hour"],
    10	    storage_uri="memory://",
    11	)
       
    12	@app.route('/')
    13	@limiter.limit("5/second")
    14	def index():
    15	    return Response(open(__file__).read(), mimetype='text/plain')
       
    16	@app.route('/docker')
    17	@limiter.limit("5/second")
    18	def docker():
    19	    return Response(open("Dockerfile").read(), mimetype='text/plain')
       
    20	@app.route('/flag', methods=GET)
    21	@limiter.limit("5/second")
    22	def flag():
    23	    return open('flag.txt').read()
       
    24	@app.after_request
    25	def hide_allowed(response):
    26	    response.headers["Allow"] = ""
    27	    return response
       
    28	app.run('0.0.0.0', 6006)

20行目〜23行目に

    20	@app.route('/flag', methods=GET)
    21	@limiter.limit("5/second")
    22	def flag():
    23	    return open('flag.txt').read()

/flagにアクセスしたらFLAGを取得できそうだと思う。
アクセスすると、

Method Not Allowed
The method is not allowed for the requested URL.

405エラーが返され、受け入れられないHTTPメソッドが使用されたと言われる。
ソースコードをよく見てみると、methods=GETとmethodsに変数を指定している、
GET変数に何が格納されているのか、探してみる。


Dockerfileを見てみる。

     1	FROM ubuntu:20.04
       
     2	RUN apt-get update -y && \
     3	    apt-get install -y python3-pip python3-dev
       
     4	WORKDIR /app
       
     5	RUN pip3 install flask Flask-Limiter
       
     6	RUN echo aW1wb3J0IG9zCkdFVD0obGFtYmRhOlsqc3RyKG9zLnVyYW5kb20oMzIpKV0pKCAp\
     7	|base64 -d >> `python3 -c 'print(__import__("flask").__file__)'`
       
     8	COPY . /app
       
     9	RUN chmod -R 555 /app
       
    10	USER 1000:1000
       
    11	ENTRYPOINT [ "python3" ]
       
    12	CMD [ "server.py" ]

6、7行目でbase64の文字列をデコードしてプログラムに渡している。
base64の文字列をデコードしてみると、

import os
GET=(lambda:[*str(os.urandom(32))])( )

32文字のランダムな文字をリストとしてGET変数に格納している。
手元で真似してGET変数を表示してみる。

['b', "'", 'W', '1', '\\', 'x', 'f', '6', '&', '\\', 'x', 'a', '5', '\\', 'x', 'c', 'f', '\\', 'x', '9', 'e', '\\', 'x', 'd', '9', '&', '\\', 'x', 'b', 'f', '\\', 'x', '9', '8', 'Y', '\\', 'x', '0', '2', '1', 'm', '\\', 'x', 'e', 'd', 'B', '\\', 'x', 'd', '9', '\\', 'x', '8', '5', '\\', 'x', 'f', '2', 'A', '\\', 'x', 'f', '4', '\\', 'x', 'd', '9', 'Q', '\\', 'x', 'b', 'e', '\\', 'x', 'c', '9', '\\', 'x', '1', '3', 'w', 'k', 'y', 'F', '\\', 'x', 'c', '8', "'"]

このリストの中でマッチした文字や記号があれば有効なメソッドだと判断され、FLGAが表示されそうです。
何度実行しても存在するバックスラッシュをメソッドにセットし送信してみます。

$ curl -X \\ http://puzzler7.imaginaryctf.org:6006/flag
ictf{uppercase_HTTP_method_names_are_so_2021}

Discussion