Skip to content

Commit 256a35d

Browse files
authored
Add files via upload
1 parent 75ae5ec commit 256a35d

File tree

1 file changed

+266
-0
lines changed

1 file changed

+266
-0
lines changed

vulnerability_scanner.py

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
import requests # HTTPリクエストを送信するためのライブラリ
2+
from bs4 import BeautifulSoup # HTMLやXMLファイルからデータを抽出するためのライブラリ
3+
import argparse # コマンドライン引数を解析するためのライブラリ
4+
from urllib.parse import urljoin, urlparse # URLを操作するためのモジュール
5+
import json # JSON形式でデータを扱うためのライブラリ
6+
from colorama import Fore, Style, init # コンソール出力に色を付けるためのライブラリ
7+
8+
# coloramaを初期化(Windows環境で色が正しく表示されるようにするため)
9+
init()
10+
11+
# グローバル変数としてverboseフラグを定義
12+
VERBOSE = False
13+
# 検出された脆弱性を格納するリスト
14+
detected_vulnerabilities = []
15+
16+
def make_request(url, method='GET', data=None, params=None):
17+
"""
18+
HTTPリクエストを送信し、レスポンスを返す共通ヘルパー関数。
19+
リクエスト中のエラーを捕捉し、Noneを返す。
20+
"""
21+
try:
22+
if method.upper() == 'POST':
23+
response = s.post(url, data=data)
24+
else:
25+
response = s.get(url, params=params)
26+
response.raise_for_status() # HTTPエラーがあれば例外を発生させる
27+
return response
28+
except requests.exceptions.RequestException as e:
29+
if VERBOSE:
30+
print(f"{Fore.RED}[-] リクエストエラー: {e}{Style.RESET_ALL}")
31+
return None
32+
33+
def get_all_forms(url):
34+
"""
35+
指定されたURLからすべてのHTMLフォームを抽出する関数。
36+
HTTPリクエストを送信し、BeautifulSoupを使ってHTMLを解析する。
37+
"""
38+
if VERBOSE:
39+
print(f"{Fore.CYAN}[i] フォームを検索中: {url}{Style.RESET_ALL}")
40+
response = make_request(url)
41+
if response:
42+
soup = BeautifulSoup(response.content, 'html.parser')
43+
return soup.find_all('form')
44+
return [] # リクエスト失敗時は空のリストを返す
45+
46+
def get_form_details(form):
47+
"""
48+
HTMLフォーム要素から詳細(アクションURL、メソッド、入力フィールド)を抽出する関数。
49+
"""
50+
details = {}
51+
# フォームのaction属性を取得。存在しない場合は空文字列。小文字に変換。
52+
action = form.attrs.get('action', '').lower()
53+
# フォームのmethod属性を取得。存在しない場合は'get'。小文字に変換。
54+
method = form.attrs.get('method', 'get').lower()
55+
56+
inputs = []
57+
# フォーム内のすべての'input'タグを見つける
58+
for input_tag in form.find_all('input'):
59+
# inputタグのtype属性を取得。存在しない場合は'text'。
60+
input_type = input_tag.attrs.get('type', 'text')
61+
# inputタグのname属性を取得。
62+
input_name = input_tag.attrs.get('name')
63+
# inputフィールドの詳細をリストに追加
64+
inputs.append({'type': input_type, 'name': input_name})
65+
66+
details['action'] = action
67+
details['method'] = method
68+
details['inputs'] = inputs
69+
return details
70+
71+
def test_sql_injection_on_url_params(url, payload):
72+
"""
73+
URLパラメータに対してSQLインジェクションをテストする。
74+
"""
75+
parsed_url = urlparse(url)
76+
query = parsed_url.query
77+
if query:
78+
test_url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}?{query}{payload}"
79+
if VERBOSE:
80+
print(f"{Fore.BLUE} [+] URLパラメータをテスト中: {test_url}{Style.RESET_ALL}")
81+
response = make_request(test_url)
82+
if response:
83+
content = response.content.decode().lower()
84+
if "mysql" in content or "syntax error" in content or "sqlstate" in content:
85+
print(f"{Fore.RED} [!!!] SQLインジェクションの脆弱性を発見しました (URLパラメータ): {test_url}{Style.RESET_ALL}")
86+
detected_vulnerabilities.append({
87+
"type": "SQL Injection",
88+
"location": "URL Parameter",
89+
"url": test_url,
90+
"payload": payload
91+
})
92+
return True
93+
return False
94+
95+
def test_sql_injection_on_form(url, form_details, payload):
96+
"""
97+
フォームに対してSQLインジェクションをテストする。
98+
"""
99+
target_url = urljoin(url, form_details['action'])
100+
data = {}
101+
for input_tag in form_details['inputs']:
102+
if input_tag['type'] == 'text' and input_tag['name']:
103+
data[input_tag['name']] = payload
104+
105+
if VERBOSE:
106+
print(f"{Fore.BLUE} [+] フォームをテスト中 (Action: {form_details['action']}, Method: {form_details['method'].upper()}): {target_url}{Style.RESET_ALL}")
107+
response = make_request(target_url, method=form_details['method'], data=data if form_details['method'] == 'post' else None, params=data if form_details['method'] == 'get' else None)
108+
if response:
109+
content = response.content.decode().lower()
110+
if "mysql" in content or "syntax error" in content or "sqlstate" in content:
111+
print(f"{Fore.RED} [!!!] SQLインジェクションの脆弱性を発見しました (フォーム): {target_url}{Style.RESET_ALL}")
112+
if VERBOSE:
113+
print(f"{Fore.YELLOW} [i] フォーム詳細: {form_details}{Style.RESET_ALL}")
114+
detected_vulnerabilities.append({
115+
"type": "SQL Injection",
116+
"location": "Form",
117+
"url": target_url,
118+
"payload": payload,
119+
"form_details": form_details
120+
})
121+
return True
122+
return False
123+
124+
def scan_sql_injection(url):
125+
"""
126+
指定されたURLとフォームに対してSQLインジェクションの脆弱性をスキャンする関数。
127+
基本的なSQLエラーメッセージをレスポンスから検出する。
128+
"""
129+
print(f"\n{Fore.GREEN}[*] SQLインジェクションのスキャンを開始します: {url}{Style.RESET_ALL}")
130+
sql_payload = "'" # 最も基本的なSQLiペイロード
131+
132+
# URLパラメータのスキャン
133+
test_sql_injection_on_url_params(url, sql_payload)
134+
135+
# フォームのスキャン
136+
forms = get_all_forms(url)
137+
if VERBOSE:
138+
print(f"{Fore.CYAN} [i] {len(forms)}個のフォームを検出しました。{Style.RESET_ALL}")
139+
for form in forms:
140+
test_sql_injection_on_form(url, get_form_details(form), sql_payload)
141+
142+
def test_xss_on_url_params(url, payload):
143+
"""
144+
URLパラメータに対してXSSをテストする。
145+
"""
146+
parsed_url = urlparse(url)
147+
query = parsed_url.query
148+
if query:
149+
test_url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}?{query}{payload}"
150+
if VERBOSE:
151+
print(f"{Fore.BLUE} [+] URLパラメータをテスト中: {test_url}{Style.RESET_ALL}")
152+
response = make_request(test_url)
153+
if response:
154+
content = response.content.decode()
155+
if payload in content:
156+
print(f"{Fore.RED} [!!!] XSSの脆弱性を発見しました (URLパラメータ): {test_url}{Style.RESET_ALL}")
157+
detected_vulnerabilities.append({
158+
"type": "XSS",
159+
"location": "URL Parameter",
160+
"url": test_url,
161+
"payload": payload
162+
})
163+
return True
164+
return False
165+
166+
def test_xss_on_form(url, form_details, payload):
167+
"""
168+
フォームに対してXSSをテストする。
169+
"""
170+
target_url = urljoin(url, form_details['action'])
171+
data = {}
172+
for input_tag in form_details['inputs']:
173+
if input_tag['type'] == 'text' and input_tag['name']:
174+
data[input_tag['name']] = payload
175+
176+
if VERBOSE:
177+
print(f"{Fore.BLUE} [+] フォームをテスト中 (Action: {form_details['action']}, Method: {form_details['method'].upper()}): {target_url}{Style.RESET_ALL}")
178+
response = make_request(target_url, method=form_details['method'], data=data if form_details['method'] == 'post' else None, params=data if form_details['method'] == 'get' else None)
179+
if response:
180+
content = response.content.decode()
181+
if payload in content:
182+
print(f"{Fore.RED} [!!!] XSSの脆弱性を発見しました (フォーム): {target_url}{Style.RESET_ALL}")
183+
if VERBOSE:
184+
print(f"{Fore.YELLOW} [i] フォーム詳細: {form_details}{Style.RESET_ALL}")
185+
detected_vulnerabilities.append({
186+
"type": "XSS",
187+
"location": "Form",
188+
"url": target_url,
189+
"payload": payload,
190+
"form_details": form_details
191+
})
192+
return True
193+
return False
194+
195+
def scan_xss(url):
196+
"""
197+
指定されたURLとフォームに対してクロスサイトスクリプティング(XSS)の脆弱性をスキャンする関数。
198+
ペイロードがレスポンスにそのまま反映されるかを検出する。
199+
"""
200+
print(f"\n{Fore.GREEN}[*] XSSのスキャンを開始します: {url}{Style.RESET_ALL}")
201+
xss_payload = "<script>alert('xss')</script>" # 基本的なXSSペイロード
202+
203+
# URLパラメータのスキャン
204+
test_xss_on_url_params(url, xss_payload)
205+
206+
# フォームのスキャン
207+
forms = get_all_forms(url)
208+
if VERBOSE:
209+
print(f"{Fore.CYAN} [i] {len(forms)}個のフォームを検出しました。{Style.RESET_ALL}")
210+
for form in forms:
211+
test_xss_on_form(url, get_form_details(form), xss_payload)
212+
213+
def main():
214+
"""
215+
スクリプトのエントリーポイント。
216+
コマンドライン引数を解析し、スキャン処理を呼び出す。
217+
"""
218+
global VERBOSE # グローバル変数VERBOSEを変更可能にする
219+
220+
# ArgumentParserオブジェクトを作成し、スクリプトの説明を設定
221+
parser = argparse.ArgumentParser(description='Webサイトの簡易脆弱性スキャナ (XSS, SQLi)')
222+
# '--url'または'-u'引数を追加。必須引数で、スキャン対象のURLを指定する。
223+
parser.add_argument('--url', '-u', required=True, help='スキャン対象のURL')
224+
# --sql オプションを追加 (SQLインジェクションスキャンを有効にする)
225+
parser.add_argument('--sql', action='store_true', help='SQLインジェクションスキャンを実行します')
226+
# --xss オプションを追加 (XSSスキャンを有効にする)
227+
parser.add_argument('--xss', action='store_true', help='XSSスキャンを実行します')
228+
# --output-json オプションを追加 (結果をJSONファイルに保存する)
229+
parser.add_argument('--output-json', help='スキャン結果をJSON形式で指定されたファイルに保存します')
230+
# --verbose または -v オプションを追加 (詳細な出力を表示する)
231+
parser.add_argument('--verbose', '-v', action='store_true', help='詳細な出力を表示する')
232+
233+
args = parser.parse_args() # コマンドライン引数を解析
234+
target_url = args.url # 解析されたURLを取得
235+
VERBOSE = args.verbose # verboseフラグを設定
236+
237+
print(f"\n{Fore.CYAN}--- スキャン対象: {target_url} ---{Style.RESET_ALL}")
238+
239+
# スキャン対象の選択ロジック
240+
if args.sql or args.xss: # --sql または --xss のいずれかが指定された場合
241+
if args.sql:
242+
scan_sql_injection(target_url)
243+
print(f"\n{Style.BRIGHT}{Fore.WHITE}" + "-" * 50 + f"{Style.RESET_ALL}") # 区切り線
244+
if args.xss:
245+
scan_xss(target_url)
246+
print(f"\n{Style.BRIGHT}{Fore.WHITE}" + "-" * 50 + f"{Style.RESET_ALL}") # 区切り線
247+
else: # どちらも指定されなかった場合、両方のスキャンを実行
248+
scan_sql_injection(target_url)
249+
print(f"\n{Style.BRIGHT}{Fore.WHITE}" + "-" * 50 + f"{Style.RESET_ALL}") # 区切り線
250+
scan_xss(target_url)
251+
print(f"\n{Style.BRIGHT}{Fore.WHITE}" + "-" * 50 + f"{Style.RESET_ALL}") # 区切り線
252+
253+
print(f"{Fore.GREEN}スキャンが完了しました。{Style.RESET_ALL}")
254+
255+
# JSONファイルへの保存処理
256+
if args.output_json:
257+
try:
258+
with open(args.output_json, 'w', encoding='utf-8') as f:
259+
json.dump(detected_vulnerabilities, f, indent=4, ensure_ascii=False)
260+
print(f"スキャン結果を {args.output_json} にJSON形式で保存しました。")
261+
except Exception as e:
262+
print(f"[-] JSONファイルの保存中にエラーが発生しました: {e}")
263+
264+
# スクリプトが直接実行された場合にmain関数を呼び出す
265+
if __name__ == '__main__':
266+
main()

0 commit comments

Comments
 (0)