PythonのRequestsライブラリを使用して、Webサーバーのディレクトリ探索をします。
HTTP、HTTPSおよびProxy経由の場合を想定しています。
Proxyは、Burp SuiteやOWASP ZAP等を想定しています。
OSは、Parrot OS Security Editionです。
Requestsライブラリの使い方の備忘録です。
プログラム
IPアドレス、ポート、HTTP、HTTPS、Proxy有り無しの設定は、変数の初期値の変更や、コメントアウトに位置を変更することで切り替えます。
#!/usr/bin/python3
from signal import signal, SIGINT
from sys import exit
import requests
# 証明書の検証を行わないときにInsecureRequestWarningを表示させないため
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
# Ctrl+Cでプログラムを中止するため
def handler(signal_received, frame):
# Handle any cleanup here
print(' [+]Exiting...')
exit(0)
signal(SIGINT, handler)
# 変数の初期化
# HTTPかHTTPSを選択する
hostname = "http://192.168.56.116"
#hostname = "https://192.168.56.116"
# デフォルトのポートから変更するとき
port = 80
#port = 443
# ワードリストファイルを指定する
wordlist_file = "/usr/share/dirb/wordlists/small.txt"
wordlist = ""
word = ""
# ヘッダファイルを指定する
header_file = "header.txt"
headrslist = ""
header = ""
headerlist = ""
header_result = {}
proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080',
}
# メインルーチン
if __name__=="__main__":
try:
with open(wordlist_file) as f:
wordlist = [s.rstrip() for s in f.readlines()]
print("Wordlist: " + wordlist_file)
try:
with open(header_file) as f2:
headerslist = [s.rstrip() for s in f2.readlines()]
print("Headerlist: " + header_file)
for header in headerslist:
if header == '':
break
if (header.startswith('GET') == False
and header.startswith('POST') == False
and header.startswith('Host') == False
and header.startswith('Connection') == False
and header.startswith('Content-Type') == False
and header.startswith('Content-Length') == False):
headerlist = header.split(': ')
header_result[headerlist[0]] = headerlist[1]
except FileExistsError:
header = ""
print("ヘッダーファイルが存在しません")
except FileNotFoundError:
header = ""
print("ヘッダーファイルが存在しません")
keep_alive = requests.Session()
#keep_alive.proxies.update(proxies)
for word in wordlist:
# デフォルトのポートから変更するとき
url = hostname + '/' + word + '/'
#url = hostname + ':' + str(port) + '/' + word + '/'
# 証明書を検証しないとき
#response = keep_alive.get(url)
response = keep_alive.get(url, headers=header_result)
#response = keep_alive.get(url, verify=False)
#response = keep_alive.get(url, headers=header_result, verify=False)
status = response.status_code
connection = response.headers['Connection']
if status != 404:
print(word + " " + str(status))
if connection == "close":
keep_alive.close()
keep_alive = requests.Session()
#keep_alive.proxies.update(proxies)
keep_alive.close()
except FileExistsError:
print("Wordlistファイルが存在しません")
except FileNotFoundError:
print("Wordlistファイルが存在しません")
説明
モジュール
Ctrl+Cでプログラムを中止するために、signal, SIGINT
http通信のために、requests
をインポートしています。
from signal import signal, SIGINT
from sys import exit
import requests
警告表示をさせない
オレオレ証明書のWebサーバーでテストする際に、証明書の検証を無効にしただけでは、Warningが表示されてしまうので、警告表示させないようにします。
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
Ctrl+Cでプログラムを中止する
これを書いておけば、Ctrl+Cでプログラムを中止できます。
def handler(signal_received, frame):
# Handle any cleanup here
print(' [+]Exiting...')
exit(0)
signal(SIGINT, handler)
ホストのIPアドレスおよびプロトコル
ホストのIPアドレス(ホスト名でもOKなはず)を記載します。
HTTPSの場合は、上側をコメントアウトして、下側を有効にします。
hostname = "http://192.168.56.116"
#hostname = "https://192.168.56.116"
デフォルトのポートから変更するとき
デフォルトのポートから変更するときに設定します。
ただし、URLを作成するところのコードを有効にしないと変更されません。
port = 80
#port = 443
ワードリストファイルを指定する
ワードリストのファイルを指定します。
wordlist_fileは、ワードリストが記載されたファイル名
wordlistは、ファイルからワードを読み込みリスト形式にしたリスト
wordは、wordlistから一つずつ取り出したワード
wordlist_file = "/usr/share/dirb/wordlists/small.txt"
wordlist = ""
word = ""
ヘッダーファイルを指定する
ヘッダーを指定したい場合、ヘッダーファイルを指定します。
ヘッダーファイルのフォーマットは、通常のリクエストヘッダーと同じです。
Burp Suite等で、リクエストをそのままファイルに保存してもいいです。
リクエスト行、Hostヘッダー、Connectionヘッダー、Content-Typeヘッダー、Content-Lengthヘッダーは、追加せずに削除します。
header_fileは、ヘッダーが記載されたファイル名
headrslistは、ファイルからワードを読み込みリスト形式にしたリスト
headerは、headrslistから一つずつ取り出したヘッダー
headerlistは、headerをヘッダー名と値で分けたリスト
header_resultは、ヘッダー名をキーとした辞書
header_file = ""
headrslist = ""
header = ""
headerlist = ""
header_result = {}
プロキシーの設定
プロキシーのアドレス、ポートを指定する。
ただし、セッションを作成するところのコードを有効にしないとプロキシーが有効になりません。
proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080',
}
ワードリスト読み込み
ディレクトリ名の一覧ファイルを読み込みます。
readlines()で読み込んだ時点では、改行コードが含まれているため、rstrip()で改行コードを削除しています。
with open(wordlists) as f:
wordlist = [s.rstrip() for s in f.readlines()]
print("Wordlist: " + wordlists)
ヘッダーの読み込み
ヘッダーを読み込みます。
リクエスト行、Hostヘッダー、Connectionヘッダー、Content-Typeヘッダー、Content-Lengthヘッダーは、削除します。
読み込んだヘッダーは、ヘッダー名をキーとした辞書を作成します。
for header in headerslist:
if header == '':
break
if (header.startswith('GET') == False
and header.startswith('POST') == False
and header.startswith('Host') == False
and header.startswith('Connection') == False
and header.startswith('Content-Type') == False
and header.startswith('Content-Length') == False):
headerlist = header.split(': ')
header_result[headerlist[0]] = headerlist[1]
セッションを作成
キープアライブのために、セッションを作成します。
プロキシーを有効する場合は、2行目を有効にします。
keep_alive = requests.Session()
#keep_alive.proxies.update(proxies)
リクエストの送信
wordlistから1個ずつワードを取り出して、リクエストを送信します。
デフォルトポートから変更する場合は、urlを作成する式の上側をコメントアウトして、下側を有効にします。
ヘッダーを追加する場合は、headers=header_resultがある式を有効にします。
httpsの証明書の検証を無効にする場合は、verify=Falseがある式を有効にします。
statusは、HTTPステータスコードを取得します。
connectionは、レスポンスヘッダーのConnectionを取得します。
HTTPステータスコードが、404でなければ、その時のワードとステータスを表示します。
レスポンスヘッダーのConnectionが、closeになると、一旦、セッションをcloseして、再度、セッションを作成します。
プロキシーを有効する場合は、ここも有効にします。
for word in wordlist:
url = hostname + '/' + word + '/'
#url = hostname + ':' + str(port) + '/' + word + '/'
#response = keep_alive.get(url)
response = keep_alive.get(url, headers=header_result)
#response = keep_alive.get(url, verify=False)
#response = keep_alive.get(url, headers=header_result, verify=False)
status = response.status_code
connection = response.headers['Connection']
if status != 404:
print(word + " " + str(status))
if connection == "close":
keep_alive.close()
keep_alive = requests.Session()
#keep_alive.proxies.update(proxies)
keep_alive.close()
プロキシーを使用する場合
OWASP ZAPの場合は、上記のコードで問題ないですが
Burp Suiteの場合、上記のコードでは、予期しないときにBurp Suiteからセッションを閉じられて、エラーになります。
エラーにならないように、毎回セッションを閉じるコードです。
このコードでも、OWASP ZAP、Burp Suiteの両方とも、サーバーとの間は、キープアライブになるようです。
#!/usr/bin/python3
from signal import signal, SIGINT
from sys import exit
import requests
# 証明書の検証を行わないときにInsecureRequestWarningを表示させないため
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
# Ctrl+Cでプログラムを中止するため
def handler(signal_received, frame):
# Handle any cleanup here
print(' [+]Exiting...')
exit(0)
signal(SIGINT, handler)
# 変数の初期化
# HTTPかHTTPSを選択する
hostname = "http://192.168.56.116"
#hostname = "https://192.168.56.116"
# デフォルトのポートから変更するとき
port = 80
#port = 443
# ワードリストファイルを指定する
wordlist_file = "/usr/share/dirb/wordlists/small.txt"
wordlist = ""
word = ""
# ヘッダファイルを指定する
header_file = "header.txt"
headrslist = ""
header = ""
headerlist = ""
header_result = {}
proxies = {
'http': 'http://127.0.0.1:8080',
'https': 'http://127.0.0.1:8080',
}
# メインルーチン
if __name__=="__main__":
try:
with open(wordlist_file) as f:
wordlist = [s.rstrip() for s in f.readlines()]
print("Wordlist: " + wordlist_file)
try:
with open(header_file) as f2:
headerslist = [s.rstrip() for s in f2.readlines()]
print("Headerlist: " + header_file)
for header in headerslist:
if header == '':
break
if (header.startswith('GET') == False
and header.startswith('POST') == False
and header.startswith('Host') == False
and header.startswith('Connection') == False
and header.startswith('Content-Type') == False
and header.startswith('Content-Length') == False):
headerlist = header.split(': ')
result[headerlist[0]] = headerlist[1]
except FileExistsError:
header = ""
print("Headerファイルが存在しません")
except FileNotFoundError:
header = ""
print("Headerファイルが存在しません")
for word in wordlist:
keep_alive = requests.Session()
keep_alive.proxies.update(proxies)
# デフォルトのポートから変更するとき
url = hostname + '/' + word + '/'
#url = hostname + ':' + str(port) + '/' + word + '/'
# 証明書を検証しないとき
#response = keep_alive.get(url)
response = keep_alive.get(url, headers=header_result)
#response = keep_alive.get(url, verify=False)
#response = keep_alive.get(url, headers=header_result, verify=False)
status = response.status_code
if status != 404:
print(word + " " + str(status))
keep_alive.close()
except FileExistsError:
print("Wordlistファイルが存在しません")
except FileNotFoundError:
print("Wordlistファイルが存在しません")