本記事は、オライリージャパンから発行されている「サイバーセキュリティプログラミング ―Pythonで学ぶハッカーの思考(原題:Black Hat Python)」の学習メモとして、書籍ではPython2で書かれていますが、自分なりに解釈した上でPython3に書き直しをしています。
前回は、オープンソースで利用できるCMS(WordPressやJoomla、Drupalなど)を使ったWebアプリケーションにおける、ファイルやディレクトリ構成を把握するためのツール作成方法について学びました。
今回は辞書ファイルを使い、Webアプリケーションにおけるファイルやディレクトリ構成を把握するためのツール作成方法について学んでいきます。
はじめに
前回では、CMS上で動作しているWebアプリケーションのファイルやディレクトリ構成をスキャンする方法について考察しました。
しかし、実際のWebアプリケーションでは各々が独自のファイルやディレクトリ構成を保持しています。
そんな中でも、サーバ構築手順のルーチン化や慣例に従ったファイルやディレクトリ構成を構築していることも多いと思います。
今回では、Webアプリケーションシステムでよく使われているファイル名やディレクトリ名の辞書ファイルを基にしたスキャン方法について考察していきます。
なお、辞書ファイルについては書籍で紹介されているものをダウンロードして使用します。
ライブラリのインポートとグローバル変数の定義
以下が今回インポートするライブラリとグローバル変数の定義になります。
# content_bruter.py import requests from threading import Thread from queue import Queue threads = 50 target_url = "http://localhost/bhp" wordlist_file = "all-dirs.txt" resume = None user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101Firefox/19.0"
build_wordlist関数の作成
辞書ファイルを読み込み、キュークラスオブジェクトにプットしていきます。
また、ネットワークエラーなどでGETリクエストが出来なくなった場合に備えて、"resume"変数を用意し、途中から辞書ファイルをロードできるような仕組みになっています。
def build_wordlist(wordlist_file): words = Queue() found_resume = False with open(wordlist_file, 'r', encoding='utf-8') as f: lines = f.readlines() for w in lines: if resume is not None: if found_resume: words.put(w.rstrip()) else: if word == resume: found_resume = True print("Resuming wordlist from: {}".format(resume)) else: words.put(w.rstrip()) return words
dir_bruter関数の作成
今回の総当たり攻撃のメイン部分の処理になります。
def dir_bruter(word_queue, extensions=None): while not word_queue.empty(): attempt = word_queue.get() attempt_list = [] if "." not in attempt: attempt_list.append("/{}/".format(attempt)) else: attempt_list.append("/{}".format(attempt)) if extensions: for extension in extensions: attempt_list.append("/{}{}".format(attempt, extension)) for brute in attempt_list: url = "{}{}".format(target_url, brute) try: headers = {} headers["User_Agent"] = user_agent r = requests.get(url, headers=headers) if r.status_code != 404: print("[{}] => {}".format(r.status_code, url)) except: print("!!! {} => {}".format(r.status_code, url)) pass
main関数の作成
以下がメイン関数になります。
"extensions"は拡張子のリストを定義し、もし辞書ファイルから読み取った単語に拡張子が付いていれば、任意の拡張子も追加してスキャンします。
def main(): word_queue = build_wordlist(wordlist_file) extensions = [".php", ".bak", ".orig", ".inc"] print("[*] Start Brutefoce ...") for i in range(threads): t = Thread(target=dir_bruter, args=[word_queue, extensions]) t.start() if __name__ == '__main__': main()
動作確認
それでは、上記で作成したスクリプトを実行してみます。
今回は、テスト用のサーバとしてWSL UbuntuのApache上の特定のディレクトリをスキャンしてみます。
> python content_bruter.py [*] Start Brutefoce ... [200] => http://localhost/bhp/login/ [200] => http://localhost/bhp/login.php [200] => http://localhost/bhp/??/ [200] => http://localhost/bhp/??.php [200] => http://localhost/bhp/administrator/
問題なく動作していることが確認できました。
最後に
今回は辞書ファイルを使い、Webアプリケーションのファイルやディレクトリ構成をスキャンする方法について学びました。
しかし、実際にはWebサーバ側に大量のアクセスログが残ってしまうため、ある程度ファイルやディレクトリ名を絞ったり、時間間隔を調整したりといろいろと工夫が必要になると思います。