場阿忍愚CTF writeup

場阿忍愚CTFに参加していました。個人でまともにCTFに参加したのは初めてなので、思い出して記憶を元に再現しながら write up を書いていきます。

練習

練習 – image level 1

4枚画像が渡されるので、それらを縦につなげた時に見える文字を入力する。並べたりしなくても心の目で見ればなんとかなる。

芸術

ワットイズディス?

画像を見ると甲骨文字で大和世幾由利手伊(由、伊はひらがなでいう小文字の配置)(手とした文字は呈のようにも見えた)と書いてあり、大和セキュリティを当て字にしていると思われた。また、ヒントには大和セキュリティ勉強会のステッカーである旨が記されている。

さて、ここからどうすれば?ということで、Wikipedia などを見ながら「甲骨文字」「金文」などそれらしいキーワードを入れるも失敗。「当て字」などとも入力するが失敗。「大和世幾由利手伊」「大和世幾由利呈伊」などでもないようだった。しばらく放置していたが、もしやと思って全部ひらがなで「やまとせきゅりてぃ」と入れると成功。

cole nanee?

画像が与えられる(画像検索すると @yamatosecurity のTwitterアイコンであることがわかる)。「印」とかではないので頑張って読んで試行錯誤する。忍者というキーワードが他の問題でも頻出するあたりから「忍」を入れると成功。

Lines and Boxes

背景に一定の箱と線が書かれ、手前に漢字が二文字書かれた画像が渡される。漢字は楷書体のように見えるが日本語のものではないようで、また、漢字の部品というよりは「d」のように見える部分もある。「日本人には読みづらい」というヒントもあるのでアルファベットを描き起こした可能性もあるが、読めないので画像検索。そのままでは背景に邪魔されて出ないのでペイントでざっくり背景を除去すると中国の Xu Bing(徐冰)氏の作品であり、アルファベットではあることがわかる。中国由来の文字を中国の方が記したものなので、大和魂と主張するのはあまりに中国に失礼そう。つまり、恐らく大和魂という主張と関係ない。アルファベットであるという類推に自信を得て右の文字を頑張って読むとplayだったので、画像検索にキーワード「play」を追加して解説画像を入手。「word play」だったことがわかる。

二進術

壱萬回

elfバイナリが与えられ、実行すると

3 * 8 = 

のような形式で加減乗除及び割り算の余りを求める問題を出してきて、手で答えると次の問題を出してくる模様。なので、基本的にはこの形式に沿って回答すればよいが、改行しないで出してくるのでバッファを無効化する必要がある。プログラム側ではどうにもならなかったので LD_PRELOADで他プロセスのバッファリング無効化 – murankの日記 の手段を取った。C言語で書いていたが、途中で双方向にパイプするのは意外と面倒なことに気づき、入力を適宜流すために nc でうまくパイプしてごまかしてフラグを得た。

Unity遊戯如何様

Mac 用のアプリが渡され、実行すると3Dのゲームが始まる。3つコインをとるとフラグが出るようだが、3つ目のコインがとんでもないところにおいてありとれない。

手がかりとして記されている通り、 Assembly-CSharp.dll を探し、.Net逆コンパイラを探し(ILSpyを仕様)、GUI Manager以下のコードを読むと、”its_3D_Game_Tutorial”がフラグのように見えるが入力しても失敗。前後のコードを読むもこれを上書きするような処理はなかった。ふとこれが文字の通り表示されているかという可能性に思い至り、中でもフォントの可能性を考慮した時、アルファベットのフォントではデザイン上の理由で小文字も大文字と同様に表示するものがあると想起。確かに他の文字列はソースコード上で小文字なのに大文字で表示されていたので大文字で”ITS_3D_GAME_TUTORIAL”と入力して成功。

解読術

image level 5

zipを解凍するとアルファベットが書かれた画像がいくつかある。Windows のエクスプローラだと撮影日時が記されているので、その順に入力すると正解。(実際には分以下の精度が必要になって Ubuntu で unzip した結果を見たような気もする)

Ninjya Crypto

忍者なら読める暗号とする画像がある。忍者の暗号についてしらべると忍者文字がそれらしいことが判り、「やまといえば」と読める。やまといえば、ということで「かわ」がフラグ。

攻撃術

craSH

ソースコードを読むと、大まかにはメモリの容量は決め打ちされていないが、ファイルサイズに相当するメモリを書き換える際にロックを一切していない。そのため、catの引数にとられたファイルが出力先になっている場合は、現在のサイズを元に出力先のサイズを決定後、新しいサイズを元にメモリへの書き込みを行う。

cat a a a > a

などとすると確保されたメモリを超過して書き込む。craSH 2 はなかなか大変な問題で結局解けなかったが、 craSH についてはメモリを破壊してプログラムをクラッシュさせるだけでよいので適当に上記コマンドを打つか、それでもだめならそのあと作ったファイルを幾つか表示させていればフラグが出てくる。

解析術

Doubtful Files

自己解凍形式のファイルが渡され、解凍してもそれ以上どうしようもないファイル群が登場する。Linux で strings などを試みたところとりあえず RAR の自己解凍形式であることがわかり、

unrar lta 151-DoubtfulFiles.exe

してみたところ、

  Type: NTFS alternate data stream
Target: :Zone.Identifier

という指定がされたファイルがあることに気づく。このキーワードで調べたところ Zone Identifier の存在を知り、これのファイルのうちファイルを大きめのものを見ると BASE64 エンコードされたらしい文字列が複数あり、結合してデコードするとフラグが出てきた。

情報漏洩

USB のパケットをキャプチャした様子があり、途中に大きなパケットがある。その直前のパケットに「PNG」「IHDR」の文字が見えるので、このあたりで検索すると、PNGファイルは「0x89 P N G」の4バイトで始まることがわかった。そのため、この一連のパケットから「0x89 P N G」で始まる部分を見た。既に着目した2パケットを結合してできた png ファイルを開くと、完全ではないものの画像が表示され、フラグはすべて表示されていた。

Speech by google translate

渡されたwavファイルではフラグのようなものを喋っているが、最後の文字が途中で途切れている。がんばって聞いて入力しても失敗。

ファイルサイズに対して少し再生時間が短いので、 RIFF の仕様を調べて本来より短くデータサイズを記したヘッダを修正すると最後まで聞くことができ、突破。

Cool Gadget

画像があるが、よくわからない。 strings で見てみると、 removeme={} で囲まれた文字列が出てくる。中身は BASE64 デコードされており、解凍すると Salted__ で始まるバイナリが登場する。これで調べたところ、opensslを用い、PBE(パスワードベース暗号化)で暗号化されているようだ。また、aes128-cbcの文字列が画像のメタデータとして埋め込まれていた。

そうなると暗号化パスワードがわからないが、removeme={} の部分を丸ごと削除した画像を見ると画像に Cryptex で文字列を示している様子が示された。これがパスワードとかんがえられる。これで暗号化解除してフラグを得た。

$ openssl enc -base64 -d -aes-128-cbc -in enc.txt -k EAHIV
flag={Cryptex is cool!}

enc.txtはremoveme={}の括弧の中身である。

電網術

ftp is not secure.

ftp で FLAG.tar をやり取りしているので、 Wireshark のダンプ機能で取り出して解凍。

ベーシック

BASIC 認証をしているパケットが渡されている。

user:pass が http://burning.nsc.gr.jp

となっている。nsc.gr.jp について軽く調べると場阿忍愚CTFと無関係ではないので、まず http://burning.nsc.gr.jp/ に接続し、ユーザー名が「http」パスワードが「//burning.nsc.gr.jp」で BASIC 認証を突破して解決。

六十秒

NTPで4回時刻を問い合わせたあとpingを一回打ったものをキャプチャしたものが渡された。いずれもパケットの行き先は NICT の NTP サーバ。

問題文を見ると、「昼九つ半の暗号を用いた」とある。昼九つ半は江戸時代に日本で使われていた不定時法で、季節変動があるが大まかな雰囲気としては現代で言う十三時頃である。なので多少無理があるが、じゅうさんじ、あんごう、といえば ROT13 変換、ということで、どこかで ROT13 が登場すると考えられる。(なお江戸時代でも定時法は一部で使われており、調べてみたところその場合は十二支を使うようだ。こちらは地方視太陽時の二十四時間制と一対一対応する。)

pingにはペイロードがあり、BASE64のようだが解読できず、ROT13変換しても解読できない。NTP のパケットサイズが二通りあるのでよくみると、リクエストの内容をそのままレスポンスに返す部分でROT13変換するとURLになりそうな文字列を送っており、変換すると「amazon.co.jp」「k.kyotou.ac.jp」「k.rulers」という内容が読み取れる。とはいってもわからないが、この問題には「サンタ殿からのヒント:素数グッズは生協以外でも販売されていますが、そのIDは削るために使われます。」とある。Amazon で京都大学の素数ものさしの販売ページに行くと、そのURLに含まれる販売者IDがpingのペイロードと一致することがわかる。pingのペイロードからそれを取り除いて BASE64 変換することでフラグを得た。

Japanese kids are knowing.

IP アドレスとともに、「ポートスキャンは苦しゅうない」との記述があるので、ポートスキャンを実施。開いていたポートのうち片方は ssh だったが、もう片方は telnet でつなぐと低速で何かを送ってきた。よく見るとかえるの歌で、メッセージの末尾に自身は動物でありその英語での名前の md5 変換がフラグである旨が記されているので、 “frog” を md5 変換したものがフラグとした。

Malicious Code

楽しい問題。一般的なユーザーが攻撃されたものを想定したパケットキャプチャが出題され、被害者のユーザーの動きを真面目に追っていけば途中でフラグが見つかる。パケットキャプチャのファイル単体でも読み物のような楽しさがあった。

まず、パケットをキャプチャすると、最初にウェブサーバに接続し、 iframe に埋め込まれた plink ファイルを読み込んでいる。その通信が完了したあとは謎のサーバと謎の通信をしているが、ストリームの先頭部分で検索しても TLS であることがわかるのみ、その暗号化も Diffie-Hellman 鍵共有がなされているため、秘密鍵を入手することができたとしてもどうしようもないことがわかる。

何はともあれ plink のファイルを(文脈的にマルウェアなので、実行せずに)覗いたところ、何かを x.js として書きだしたあとその x.js を実行していることに気づく。(当初は Windows の WHS の対応言語に js があることに気づかず、古い IE で Active X を実行されたことを疑っていたが、Windows に js ファイルを作成した時にユーザーが開く操作をすれば js ファイルは普通に実行できることに気づいた。)外側の eval を外すと、まさに怪しい通信の通信先(httpsであることが判明)にリクエストを投げ、レスポンスの内容をそのままコマンドとして実行するコードが登場。宛先をLAN内のサーバに書き換えて実行したところ、自身のネットワークインターフェイスのIPアドレスをPOSTで送出していること(とその形式)が判明。おおまかに攻撃の雰囲気を掴みとった感じを得つつ、それ以上のパケットもないと考えられた。なにはともあれとりあえず被害者が実施されたコマンドを見ようと、 IP アドレスをキャプチャファイルに合わせたリクエストを投げると、コマンドの代わりにフラグが降ってきた。

諜報術

KDL

1998年にKDLがどういう人材を募集していたかということで、InternetArchive で当時のKDLのウェブページを表示し、それらしい文字列をコピペ。

Mr.Nipps

山寺純社長が日本時間で2015年8月13日5:30にどこにいらっしゃったか、という旨を問われている。フラグはGPS由来の緯度と経度が必要。

なぜか社長の Twitter アカウントを見つけるのに苦労したが、@junyamaderaであることがわかる。そうとなればこの前後のツイートを見たいが、量が多くて遡るのが大変なので twilog で閲覧すると、無事このアカウントは twilog に登録されていたようで、該当するツイートを見つける。


とりあえず店の名前が書かれているのでウェブ検索し、登場した Google Maps の位置情報を打ち込んでもはずれ。ふとツイートに位置情報が埋め込まれていることに気づいたが、表示は地名のみで、Webでは座標は降ってこないようだった。APIで当該ツイートを取得してそれらしいものを入れると正解だった。

Akiko-chan

顔写真が与えられ、それがどこの wordpress サイトにあったかを問われる問題(フラグは *.wordpress.com の形式)。画像検索するとすぐに wordpress.com 以下のものが見つかるので、そのまま入力して送信。

タナカハック

「taなんとか123」のユーザー名を探す問題。答えは www.yamatosecurity.com に公開されているファイルにあるとされている。

www.yamatosecurity.com のドメイン内はいくら探しても www.tanakazakku.com ドメインのウェブページをフレームで表示している HTML ファイルしか返してこず、このサーバーに当該ファイルがHTTPでおいてあるとは考え難かった。また、 HTTP とは指定されていないので HTTPS, ftp, smb などに常識的なポートでの接続を試みたがうまくいかなかった。

また、サンタ殿からのヒントには「wgetとgrepとバイナリーエディターさえあればどんな問題でも解けるでござる。」と書かれており、 http で公開されていると判断するのが妥当だった。

こうなれば少し無理はあるが www.tanakazakku.com ドメインも「www.yamatosecurity.com から全画面でのフレームで埋め込まれているから、事実上 www.yamatosecurity.com で公開されている」と解釈して、このドメインも含めて探索した。wget で –recursive オプションを指定し、深さが深くなりすぎないようにしてリンク先のファイルを取得。あとは123を含むファイルを探して、前後のバイナリを見て当該する文字を入力すれば成功。

$ wget --recursive --html-extension --convert-links --page-requisites --no-parent --domains www.yamatosecurity.com,www.tanakazakku.com http://www.tanakazakku.com/yamatosecurity
(snip.)
$ grep 123 . -R
Binary file ./www.tanakazakku.com/yamatosecurity/files/networkforensics1.pdf matches
$ strings ./www.tanakazakku.com/yamatosecurity/files/networkforensics1.pdf |grep 123
(snip.)
<</Author(tanakazakkarini123)/CreationDate(D:20130422102054Z)/Creator(Microsoft PowerPoint)/Keywords()/ModDate(D:20150918163456+09'00')/Producer(Mac OS X 10.6.8 Quartz PDFContext)>>

タイムトラベル

平成25年9月21日に50.115.13.104が紐付いていたドメイン名を問われている。このIPアドレスとともに色々なキーワードを試して過去のドメイン名が記されたページを見つけた記憶があるが、どのようなキーワードだったか思い出せず、また今思いつくものをいくつか試してみても見当たらない。ある程度運にまかせつつ色々試すと解ける、くらいの問題だった。

記述術

search_duplicate_character_string

ふたつの異なる部分文字列で完全に一致するものを探して入力せよとのこと。ずらす文字数を一文字から(長さ-1)までの場合それぞれで先頭から見ていって、最も長いもののみを保持して出力すればよい。200,000バイトあるが、競技プログラミングと違って二秒で終わらせる必要はないのでO(N2)で充分。

JavaScript Puzzle

穴埋めパズルが登場する。開発者コンソールに左から入力してオブジェクトの要素などが存在するか調べたり、文字コードなどは雰囲気で読んだりして埋める。最終的に以下のようになる。場阿忍愚CTF JavaScript Puzzle

Count Number Of Flag’s Substring!

ウェブフォームから文字列を送ると、それがフラグの部分文字列として登場する回数を教えてくれる。/flag={[a-z_{}]+}/の形式であることはわかっている。

flag={で始まることがわかり、それを入力すると1になるので、あとは一文字ずつ[a-z_{}]の文字それぞれを試しながら伸ばしていき、文字が増えなくなったら終了。フラグ一文字あたり29クエリ程度(と前後の処理及び試行錯誤)が必要だが、それより短縮できるとも思えないのでそのまま実施。

解凍?

184-flag.txt をダウンロードし、 file コマンドで見ると bzip2 で圧縮されていることがわかる。ならばと解凍するとまた bzip2 で圧縮されており、また bzip2 で解凍すると今度は ZIP が出てくる、という具合に、マトリョーシカのように圧縮を繰り返されている。何度か手でやると、 ZIP, tar, bzip2, gzip の四種類が登場する。

手でやっていられないほどの段数を経ていると想定されるので、ある程度やって雰囲気を掴んだら自動化して最終的な flag.txt を抽出。

#!/bin/sh

while true
do
	ft=`file flag.txt`
	echo "$ft"

	if echo "$ft" | grep Zip
	then
		unzip -o flag.txt
		continue
	fi

	if echo "$ft" | grep "POSIX tar"
	then
		tar -xvf flag.txt
		continue
	fi

	if echo "$ft" | grep "bzip2 compressed"
	then
		mv flag.txt flag.txt.bz2
		bunzip2 flag.txt.bz2
		continue
	fi

	if echo "$ft" | grep "gzip"
	then
		mv flag.txt flag.txt.gz
		gzip -d flag.txt.gz
		continue
	fi

	break
done

Make sorted Amida kuji!

最初に問題の概要を把握するのに苦労するが、最初の並びが与えられるので、それを昇順に並び替えるあみだくじを指示された数重複なく与えるとフラグがもらえる模様。JSファイルを見るとあみだの線をどの場所に何度置いたかでフラグを生成しており、与えられた回数作るというよりは全種類作る必要があって作者が数を明示してくれているという雰囲気。

最初は 3 1 2 0 を並び替える問題が登場し、また、一つの正解が最初から入力されている。手で線をずらしながら適当に入力すると次のステージに進めた。

第二ステージ(最終)では、 9 8 6 5 7 3 2 1 0 4 をソートするあみだくじを62個(先ほどの考察の通りすべて)作る必要がある。正解も明示されていない。

色々紙で試行錯誤して一つ発見し、そこから線を上下にずらしたりしてみても20通りくらいでしかない。また、全探索は現実的でなさそうだった。

一旦考えなおし、現状で左端にある9,8 を右端に送り込むこと、縦の線が10本程度しかないため、この程度は必要になる(開始位置が少し違うものもあるが、制約条件としての強さは同じ)。

端から端まで二要素を届けるあみだくじ

また、あみだくじとして有効なすべてのパターンは、下の図かその上下反転のいずれかの部分あみだくじ(この状態を仮に「基本あみだくじ」とする。また、部分あみだくじとは元のあみだくじのうち横線を0本以上取り除いたものと定義する。)を元に、「線を上下にずらす」「開いている場所に上下に連続した二本の線を入れる」の操作のみで作ることができることがわかる。これよりも高密度にすることはできず、一本でも上下にずらした状態は単に使用可能な線が減るだけだからである。

基本あみだくじの基本

この二つの制約を整理する、まず基本あみだくじを全探索できることに気づく。基本あみだくじに使用可能な線45本のうち17本は必須のため、2^29程度の探索空間を二つ探索すればよい。

これで、8通りの基本あみだくじが見つかったので、これらを一旦印刷(比喩表現ではなく、プリンタを用いて紙に出力すること)する。

これらのほとんどは数本上下にずらしたり開いた空間に二本の連続した線を入れるだけだが、多くの模様を持つものが2通り見つかる。同時に、この二つはそれぞれ、高々29本の線に自由度があるだけであることに気づいた。そのため、これらについても全探索可能である(それぞれ20通りあった)。

これで62通り発見した。ソースコードのフラグを生成する部分には「simulate this function!!!」とあるが、印刷して手書きで発見した情報も含めて全部コードに起こすのもこの関数のシミュレーション結果を検証するのも面倒なので、全探索で発見した20通り×2をコピペできるようにし、あとは手打ちでブラウザで打ち込んでフラグを得た。

超文書転送術

GIFアニメ生成サイト

接続すると、画像をアップロードすることでGIFアニメを生成するウェブサイトが登場する。色々なURLパターンを試しているうちに、新規画像の生成時に渡されるURLのパス「/movies/newgif/:id」のID部分を1にするとID=1である最初のGIFアニメが閲覧でき、しかもフラグを言いたげな内容が表示されている。

しばらく待つと一瞬だけフラグが表示される。自宅で解いていればGIFアニメを処理する気の利いたツールを探すところだが、電車の中でタブレットで解いていたので、Windows の Snipping Tool でタイミングよくスクリーンショットをよることでフラグの獲得に成功。

Network Tools

ネットワーク系の色々なコマンドをオプションを指定して叩けるサイトが出てくる。コマンドインジェクションかと思うが、オプションががっちり制限されていてインジェクションはおろか普通にネットワーク系のコマンドで遊ぶことすらできない。コメントに利用可能なオプション一覧があるが、ヌルインジェクションなども含め任意のコマンドを動かすことは難しいようだ。

画面の下部に bash のバージョンが表示されており、もしやと思って調べてみるとまさに最近話題になった shellshock の脆弱性を抱えたバージョンだった。

試しに

$ curl -H 'User-Agent: () { :;}; ls ' http://210.146.64.37:60888/exec -F 'cmd=ifconfig' -F 'option=' | less

などとしてみると、lsが見つからない旨が表示される。lsを /bin/ls にすると flag.txt が見つかるので、/bin/cat flag.txt してフラグを得た。

箱庭XSS

実行ファイルが渡され、ウェブのフォームが表示されており、入力すると下に入力したものが大文字に変換されて表示される。試しに<b>a</b>などと入力してみると、まさに太字になった。

なので alert を入れた scriptタグを入れてみるが、動かない。指定の仕方がおかしかったか、ブラウザのバージョンなどもあるのか、などと試行錯誤の末、ローカルに alert するだけの js ファイルを置き、<script type=”text/javascript” src=”C:\Users\(中略)\hoge.js” /> といったことをすると、alert の代わりにフラグが表示された。

YamaToDo

ユーザが指定したエンコーディングで ToDo のメモが保存できるウェブサイト「YamaToDo」から、ユーザー「yamato」のメモを盗み出すというもの。

ソースコードを見ると、PHP の MySQL 呼び出しからユーザーの指定エンコーディングで直接 SET NAMES しており、かつ mysqli_real_escape_string を使っている。よく言われるしてはいけないパターンであることがわかる。

また、文字コード指定は sjis だけ弾いており、弾くエラーメッセージは’sjis? so sweeeeeeeeeet’であった。その時は単に英語としての意味が解らないとしていたが、後日気になって英語で sweet という語をインターネットの辞書(例: sweet – definition of sweet in English from the Oxford dictionary)で引くと、技術水準に言及する場合では円滑にことを運ぶことなどを意味するようであるため、「sjis?やるなああああああ(阻止)」という雰囲気であり、近いところまで来ていると判断できるともいえる。

手元の MySQL で文字コードの一覧を出力すると cp932 があり、こちらは使えることがわかる。そのため、その状態で「ソ’」などとすると’をエスケープする\がソの2バイト目で打ち消され、好きな文字をSQL文の続きとして入れられる。

複文は指定できず、また不正ログインについてもユーザ名及びパスワードについての妥当性検査が厳しいためできないが、INSERT のサブクエリとしてSELECTを入れて他人のメモを自分のメモにINSERTすることができた。あとはそれに従って解けばよかったが、エラーメッセージが表示されず、「同じテーブルを二回参照するときはテーブル名を指定しないとたいてい動かない」ことに気づかずはまっていた。手元で実行してようやく気づいてフラグらしき文字列を入手、ブラウザのエンコーディングを修正してフラグを得た。

Yamatoo

なかなか楽しかった問題。

SQLite で構築された検索サイトからフラグを盗み出す問題。DBのスキーマは別途渡されており、 flag というテーブルに最大60文字の flag というカラムがあることが判っている。

ソースコード中に

        if (mb_strlen($keyword) > 2) {
            $words = implode(' ', ngram($keyword));
            $where = "exists (select 1 from `site_fts` where `site`.rowid = `site_fts`.rowid and `words` match '{$words}') or `title` like '%{$keyword}%'";
        } else {
            $where = "`title` like '%{$keyword}%'";
        }

        $result = $pdo->query("select * from `site` where {$where}");

とあり、エスケープもしていないので注入箇所をみつけることそのものは容易だった。しかし、いくつかの関門がある。

まず、第一の関門は先ほど引用したコード中で呼び出されている ngram という関門の突破。以下のように定義されている。

    function ngram($text, $n = 2)
    {
		$return = array();
		$n = (int)$n;

        foreach (array_filter(explode(' ', trim($text))) as $word) {
			$length = mb_strlen($word) - $n;
            if ($length > 0) {
			    for ($i = 0; $i <= $length; $i++) {
			    	$return[] = mb_substr($word, $i, $n);
                }
            } else {
                $return[] = $word;
            }
        }

		return $return;

(必要に応じて字下げがずれているのを修正すると)流し込んだ文字列はここでズタズタにされてしまうことが判る。前半部分にあるためコメントアウトによる無効化もできない。そのため、ここを突破する方法を暫く考えるが、「”’」(アポストロフィ三つ)を流し込むと、ズタズタにされる方では「” ”」となり、エスケープされたアポストロフィー二つとなってそれ以後も文字列とみなされるが、後半部では「”’」となり、エスケープされたアポストロフィーのあとのアポストロフィーでクオート部分が終了して外に出ることができる。(なお、最初、「\\’」を試してうまくいかず、SQLiteのドキュメントを見てSQLiteでは「”」とエスケープすることを知った。)

第二の関門は、彼らが導入したとしているWAFだった。

        if (preg_match('/like|glob|nullif|case|union|sleep|substr|instr|soundex|load/i', $keyword) === 1) {
            exit('WAF~><');
        }

わふ~><

UNION しようとしてもわふーされてしまう。SQLite のドキュメントのSQLite Query Language: SELECTを見ても、抜け道は思いつかない。一応、WHERE句で true false がわかれば 1 bit の情報を密輸できるので、これを繰り返すしかない。先ほどの「Count Number Of Flag’s Substring!」と同様の作戦が使える。

ただし、substrやlikeを使って部分文字列を検索しようとするとわふーされてしまうという第三の関門にあたった。とりあえずlengthは使えたので文字数を調べてみると、フラグは59文字あるようだった。

SQLiteのドキュメントの文字列処理関数一覧を漁っていると、replace がまだわふーされていないことに気づく。length(replace((SELECT flag FROM flag), “flag”, “”)) といったことをすると55文字になることに気づく。これでようやく「Count Number Of Flag’s Substring!」と同様の作戦でフラグを得た。

Yamatonote

YAMLでノートをアップデートできるメモサイト「Yamatonote」からメモを奪う問題。

ソースコードを見ると、とりあえずデータベースへのアクセスのクラスにプレイスホルダらしき概念が導入されているのに、なぜか手で処理していることに気づく。とても怪しい。とはいえ全体を見ている最中だったので続けて他のコードを見るが、YAMLの処理はSQLに流し込まれるだけであり、SQLへのエスケープが適切になされればとりあえずSQLから何かが漏洩するようなものはないと思われた。

そのため、データベース処理の関数、中でもエスケープ周りに注目すると、

        foreach ($param as $key => $value) {
            $value = sprintf("'%s'", mysqli_real_escape_string($this->link, $value));
            $sql = str_replace($key, $value, $sql);
        }

とあった。例えば、 {:key1 => ‘:key2’, ‘:key2’ => ‘ほげ’} というものが入力され、 :key1 が先に処理されれば、例えば

初期値: SELECT * FROM user WHERE id = :key1 AND password = :key2
一周目: SELECT * FROM user WHERE id = ':key2' AND password = :key2
二周目: SELECT * FROM user WHERE id = ''ほげ'' AND password = 'ほげ'

となり、クオートからの脱出まで完了してあとは流しこむだけ状態になっていることがわかる。実際に使うのは INSERT 文なので Yamatodo と類似の戦略が使えるが、Yamatodo と違ってエラーメッセージが表示されるので、むしろ Yamatodo より難易度は低いと感じた。PHPの連想配列でどの順でキーが登場するか調べつつ、新規メモを生成する部分に流し込んで自分のメモに呼び出したいメモを登場させてフラグを得た。

箱庭XSS 2

箱庭XSSのコードをそのまま流し込むとフラグが登場。違いがわからないまま完了。

兵法術

まさかの詰将棋問題。CTFっぽくないし、脳内で探索するとしか言いようがないので省略。電車の中で解いて時間を潰しつつCTFっぽくない問題を潰そうと思っていたら乗り過ごした。

所感など

CTFにまともに個人参加するのは初でしたし、全体の戦果はそれなりに健闘できたと考えています。残った問題はいずれもある程度の時間取り組んだ上で、芸術以外は write up を読んで勉強しようと思える問題でした。また、現に他の参加者の方の write up を読み、例えば Ninja no Aikotoba については折角怪しいと思った strcmp の返り値の処理についての考察が甘すぎたために答えに辿りつけなかった(そして恐らく然るべき考察には自分では辿りつけなかった)ことが明らかになりました。一方で、得点を見れば10位であり、周囲と比較しても一応まともに戦えた順位であると言えると思います。

CTFと云うものに手を付けてからの経験はまだまだ浅いので、今後この方面も楽しんでいきたい、と感じました。