SECCON CTF 2022 Domestic FinalsにチームtraPで参加してきました。参加記はこちら。
私が解けた問題はかなり少ないですが、そのwriteupとなります。
writeup
Jeopardy
[rev] Whisky
この問題を含め、rev、pwnの問題はすでにptr-yudai先生のwriteupが公開されています。
まず、与えられたbackdoor_plugin.so
ファイルをGhidraなどで開くと、特徴的な関数としてuwsgi_backdoor_request
関数及びbackdoor
関数が見られます。uWSGIは使用したことがなかったので、ドキュメントを見ながら解き進めます。
このプラグインは、どうやらドキュメントのThe Symcall pluginと似たような形をしているようです。uwsgi_backdoor_request
を読むと、uwsgi_backdoor_request
がmysym_function
に対応しており、その引数はwsgi_request
構造体であることがわかります。
backdoor
関数を呼び出すには以下の3つの条件があることがわかります。
-
HTTP_BACKDOOR
をwsgi_request
からuwsgi_get_var
関数で読み出し、値がenabled
である。 -
wsgi_request
のあるフィールドの値が16である。 -
wsgi_request
のあるフィールドの値が0でない。
まず、1をクリアするために、uwsgi_get_var
関数の挙動を調べます。すると、どうやらCGI環境変数を読みだしていることがわかり、これにHTTP_BACKDOOR
を追加してGET
リクエストを送ると良いことがわかります。ここで、CGI環境変数を変えることはできないのでは...?と思い、時間を溶かしましたが、この説明にあるように、ヘッダーの名前にHTTP_
というprefixをつけたものが自動で設定されるため、keyがBackdoor
、valueがenabled
であるヘッダーを追加してリクエストを送ればよいことがわかりました。
次に、2、3をクリアするために、wsgi_req + 0x1d8
及びwsgi_req + 0xc0
が表すフィールドを調べます。しばらくwsgi_request
構造体の定義を探したところ、ptr-yudaiさんのwriteupにもあるように、uwsgi.hにあることがわかります。フィールドのサイズを0x1d8
及び0xc0
になるまでひとつひとつ足していってもよかったのですが、面倒だったので以下のようなコードを記述し、Docker上で用意した環境で走らせることでフィールドを手動二分探索(?)しました。
int mysym(struct wsgi_request *wsgi_req) {
// read request variables
if (uwsgi_parse_vars(wsgi_req)) {
return -1;
}
// get HTTP_BACKDOOR
uint16_t vlen = 0;
char *v = uwsgi_get_var(wsgi_req, "HTTP_BACKDOOR", 13, &vlen);
// send status
if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1;
// send content_type
if (uwsgi_response_add_content_type(wsgi_req, "text/plain", 10)) return -1;
// uri_lenをフィールド名に変更する
int a = offsetof(struct wsgi_request, uri_len);
char ret[32] = {};
itoa(a, ret, 10);
// send the body
if (uwsgi_response_write_body_do(wsgi_req, ret, strlen(ret))) return -1;
return UWSGI_OK;
}
# create gcc env
FROM gcc:latest
# copy source code
COPY mysym.c ./mysym.c
COPY uwsgi.h ./uwsgi.h
# build mysym.c
RUN gcc -fPIC -shared -o mysym.so `uwsgi --cflags` mysym.c
# use uwsgi
FROM python:3.7
# copy mysym.so
COPY --from=0 /mysym.so /mysym.so
# install uwsgi
RUN pip install uwsgi
ENTRYPOINT ["uwsgi", "--dlopen", "/mysym.so", "--symcall", "mysym", "--http-socket", ":9090", "--http-socket-modifier1", "18"]
これにより、フィールドがそれぞれauthorization_len
及びuri
であることがわかりました。この情報を用いると、Authorization
ヘッダーの長さが16であり、URI
が設定されており、ヘッダーにBackdoor: enabled
が設定されている際にbackdoor
関数が呼び出され、URI
の位置のファイルをAuthorization
の内容をキーにしてAES128-cbc
でエンコードした結果がヘッダーに追加されてレスポンスとなることがわかります。
よって、以下のようなコードを書くことで、フラグを得ました。
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
key = b'abcdeabcdeabcdez'
print(key)
enc = long_to_bytes(0x761a7bb2cead118aa222cbfcdc2671dae5bd34f196064cad31d1a30404ec231127e6b9be073515395fdbd07f4934e3cbe3865e7f958acbd860a9c181a0d28b10)
cipher = AES.new(key, AES.MODE_ECB)
print(cipher.decrypt(enc))
print(cipher.decrypt(enc).decode('utf-8'))
KoH
Heptarchy
与えられたバイナリとdiffの少ないバイナリを吐くソースコードを提出し、そのdiffの少なさを競うKoHです。
私は最初に適当なソースコードを提出するだけのお仕事で、ほとんど取り組んでおらず、チームメンバーのImperiがずっといい感じの点数を取ってくれていたのですが、wasmだけ私の提出したemccのサンプルソースコードがずっと2位のバイナリを吐き続けました(!?)。
まとめ
私はずっと他のrev2問とにらめっこしており、全然問題を解くことができなかったので、もっと精進して他2問も解けるように頑張りたいと思いました。ほかの問題に関しては後々チームメンバーがwriteupを出すと思います。
運営の皆さん、ありがとうございました。