OverTheWire やってみた(Natas 編)

前回 に引き続き CTF(Capture The Flag)として OverTheWire をやってみる。今回はウェブセキュリティを扱った Natas 編をやる。ページは以下。

overthewire.org

ウェブセキュリティなんてほぼ知らないぞ。課題をクリアしていけるか分からんがとりあえずやってみよう。課題の状況を把握したうえで5分くらい手が止まったら解法をググろう。そんくらいの気楽さでやるのが大切。知らんけど。

Level 0

クライアントサイドの html ファイルのソースを見るとパスワード発見。まずはサーバサイドは関係ないのか。

Level 1

前の課題と同じく html ファイルのソース見るだけ。右クリックが禁止されているので右クリックからソースを表示できないってことか。最初から Chrome で F12 でソース見てたので最初課題の意味が分からなかった。

Level 2

pixcel.png という謎のファイルをサーバから受信しているのが怪しい。このファイルの URL を見ると files というディレクトリが存在していて、このディレクトリにある users.txt というファイルの中にパスワードを発見。静的コンテンツへのアクセス権限の設定誤りってことなんだろう。

Level 3

Google でも見つけられないってことは、このページのコンテンツからは到達できないページにパスワードが隠されているのだろうと推測。適当なディレクトリ名を叩いてみてもなにも見つからず。

解法をググった。robots.txt にヒントが書かれているらしい。そういえばこういう検索エンジンのクローリングを制御するためのファイルがあったのを思い出した。あとは簡単で、クローリング対象から除外されているディレクトリにアクセスすればパスワードが見つかる。

Level 4

index.php にアクセスするが、そのアクセス元(HTTP referer)が http://natas5.natas.labs.overthewire.org/ でないとパスワードが手に入らないらしい。そこで curl コマンドを使って referrer を偽装してリクエストを送信することでパスワードをゲット。

こういう操作は慣れていないがググりながらやればなんとかなる。Chrome の拡張とかもあるらしい。この先の課題次第ではそういうのをインストールするかも。

Level 5

ログインしてないからアクセスできないと起こられる。ブラウザに保存されているクッキーをみると loggedin というキーに 0 が設定されているのを発見。怪しいのでその値を 1 に変更して再度アクセスしてみるとパスワードを発見。ログイン状態の管理方法が杜撰だとこうなるってことか。

Level 6

文字列を入力するフォームがある。認証ロジックの PHPソースコードを公開してくれていて、どうやら入力された文字列が変数 $secret の値と一致するか調べているらしい。$secret の値は includes/secret.inc というファイルの中にあるっぽく、そのファイルを見るとキーを発見できた。

Level 7

クエリパラメータとしてパスを与えると、相対パスとして解釈されてそのファイルの中身にアクセスしてくれるらしい。ってことで /etc/natas_webpass/natas8 にアクセスできるような相対パスを書いてパスワードをゲット。いわゆるディレクトリトラバーサル

Level 8

文字列を入力するフォームがある。その認証ロジックでは bin2hex(strrev(base64_encode($secret))) のように入力文字列の変換を処理をした上で正解となる文字列と一致するかを見ている。これらの演算はすべて可逆なので、ひとつずつ逆変換をしてゆけば入力文字列が特定できる。逆変換の実装には PHP のプログラムをオンラインで書いて実行できるサービスを使った。

paiza.io

Level 9

ユーザが入力した文字列が、実行されるコマンドに埋め込まれる。その実装が grep -i $key dictionary.txt のように直接的に埋め込むようになっている。これを上手く利用して以下のようなコマンドが実行されるように入力文字列を設計した。結果としてパスワードを cat できるのでクリア。

grep -i xxx dictionary.txt ; cat /etc/natas_webpass/natas10 ; echo dictionary.txt

こういう任意のコマンドを実行できる脆弱性は一般に arbitrary code execution として知られているらしい。今回の脆弱性にもっと適切な名前があるのかもしれないが。

Level 10

前の課題とほとんど同じだが、grep コマンドが実行される前に文字列が ;, |, & の3つの記号を持つかどうかチェックしている。そのため前の課題のようにコマンドをパイプできない。とはいえ grep コマンドは cat に近いものがあるので、以下のように任意の文字列をマッチさせるコマンドを実行させてパスワードをゲット。

grep -i ".*" /etc/natas_webpass/natas11 dictionary.txt 

Level11

XOR 暗号(XOR cipher)のキーを見つける問題。PHParray( "showpassword"=>"no", "bgcolor"=>"#ffffff") みたいなオブジェクトが文字列化され、XOR 暗号にかけられ、Base64エンコードされたものがクッキーに保存される。XOR 暗号以外の部分は可逆操作なので XOR の前後の文字列が求まる。XOR 暗号のキーは暗号化の前後の2文を XOR にかければ求まるので、これを求める処理を PHP で書いた。見つかったキー qw8J を設定して array( "showpassword"=>"yes", "bgcolor"=>"#ffffff") を暗号化した文字列をキャッシュに設定し、それを読み出すとパスワードが表示される。

この課題は実装も面倒くさかったが、それ以外にもかなりハマった。というのも Chrome でキャッシュされてる文字列を見ると ClVLIh4ASCsCBE8lAxMacFMZV2hdVVotEhhUJQNVAmhSEV4sFxFeaAw%3D というものだった。最後の %3D= を URL エンコードしたものだと気づかず、見つかった XOR 暗号のキーを設定して array( "showpassword"=>"no", "bgcolor"=>"#ffffff") を変換しても上記のものとは一致しないことに頭を悩ませていた。2つの文字列の並べて初めて URL エンコードに気づいた。クッキーに保存されるタイミングで一部の記号が URL エンコードされるのか?PHP の仕様だと思うがよく分からん。

Level12

ファイルをアップロードできるうえ、アップロードしたファイルをサーバから取得できる。拡張子も手元の html を書き換えることで好きに設定できる。しかし攻撃できそうなところが見つからなかった。まったく分からん。

答えをググる。どうやら任意のファイルをアップロードできるので PHP ファイルをアップロードすると PHP を実行できるらしい。なるほど。そして /etc/natas_webpass/natas13cat するコマンドを実行させることでパスワードを入手できる。ウェブアプリとか作るときにはアップロードできるファイルの種類に制限をかけるのが安全ということなんだろう。

Level13

前の課題とほぼ同じであるうえ、アップロードできるのが画像のみに制限されたバージョン。すでにできる気がしない。とりあえず exif_imagetype をググってみたら以下のような記事を発見。

この関数はファイルの先頭バイトを見て画像であるかどうかをチェックしているだけなので、先頭だけごまかせば PHP のコードを書けるらしい。拡張子は filename 属性として、アップロードするファイルとは別に設定できるので、結果として拡張子が PHP のコードをアップロードできる。そしてパスワードゲット。

Level14

与えたユーザとパスワードが SQL に組み込まれて実行される。これは SQL インジェクションができそう。さすがに知っている。Web アプリ実装するときは SQL クエリを作るときに文字列結合はしたらダメ。SQL の WHERE 句の条件式に or "a" = "a" のような常に真になる条件が付くように入力値を設定すればパスワードゲット。

Level15

データベースの users テーブルにアカウントが存在するかどうかを調べる処理が実行される。SQL に任意の条件を埋め込めて、その結果が空かどうかだけ分かる状況。2分探索とかでパスワードの範囲を絞るとかはできそうだけどそれ用のプログラム書くか?

書くしか無さそうなのでプログラム書いてみた。SQLLIKE 文で指定するパターンに対して1文字ずつ末尾に追加してみてなおマッチするかどうかを調べた。case sensitive なマッチにするために BINARY password LIKE (候補) のような SQL を発行する。追加する文字の範囲は ASCII コードで33番目から126番目あたりまで広めに取ってみた。ただし LIKE を使うので _& は除外した。その結果パスワードらしき文字列が得られた。ググってみてもこうやるのが正解だったっぽい。こりゃ大変だ。

疲れたのでおしまい。Level16 以降はまた今度の機会にしよう。

所感

前回の badit 編より頭を使う課題が多かった印象。セキュリティも PHP も特に詳しく知らなかったが意外と解ける課題が多かった。たまにやるには楽しい。それにしても、悲しいことにウェブアプリには脆弱になりそうな箇所がたくさん存在するんだな。世の中のウェブエンジニアに今日もご苦労さまですと言いたい。