Skip to content

Commit 1500d62

Browse files
committed
refactor: Improve input handling and user prompts; add TLS version input for SSL scanner; apply formatting updates
- Enhanced input handling to support comma-separated values - Improved user prompts in common utilities - Added TLS version input support in the SSL scanner - Made general formatting improvements
1 parent 7360f55 commit 1500d62

File tree

3 files changed

+98
-70
lines changed

3 files changed

+98
-70
lines changed

bugscanx/modules/scanners_pro/host_scanner_pro.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def read_hosts(filename=None, cidr=None):
1818

1919
def get_common_inputs(input_source):
2020
output = get_input("Enter output filename", default=f"result_{os.path.basename(str(input_source))}", validate_input=False)
21-
threads = get_input("Enter threads", "number", default="50")
21+
threads = get_input("Enter threads", "number", default="50", allow_comma_separated=False)
2222
return output, threads
2323

2424
def get_host_input():
@@ -28,12 +28,16 @@ def get_host_input():
2828
return None, cidr
2929
return filename, None
3030

31+
def list_to_comma_separated(result):
32+
return ', '.join(result) if isinstance(result, list) else result
33+
3134
def get_input_direct(no302=False):
3235
filename, cidr = get_host_input()
3336
port_list = get_input("Enter port(s)", "number", default="80").split(',')
3437
output, threads = get_common_inputs(filename or cidr)
3538
method_list = get_input("Select HTTP method(s)", "choice", multiselect=True,
36-
choices=["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "TRACE", "PATCH"])
39+
choices=["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "TRACE", "PATCH"],
40+
transformer=list_to_comma_separated)
3741

3842
scanner = DirectScanner()
3943
scanner.method_list = method_list
@@ -59,7 +63,7 @@ def get_input_proxy():
5963
payload = get_input("Enter payload", default=default_payload)
6064
port_list = get_input("Enter port(s)", "number", default="80").split(',')
6165
output, threads = get_common_inputs(filename or cidr)
62-
bug = get_input("Enter bug (optional)", default="", validate_input=False)
66+
bug = get_input("Enter bug", default="", validate_input=False, instruction="(optional)")
6367

6468
scanner = ProxyScanner()
6569
scanner.host_list = read_hosts(filename, cidr)
@@ -77,10 +81,11 @@ def get_input_proxy2():
7781
filename, cidr = get_host_input()
7882
port_list = get_input("Enter port(s)", "number", default="80").split(',')
7983
output, threads = get_common_inputs(filename or cidr)
80-
method_list = get_input("Select HTTP method", "choice", multiselect=True,
81-
choices=["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "TRACE", "PATCH"])
84+
method_list = get_input("Select HTTP method(s)", "choice", multiselect=True,
85+
choices=["GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS", "TRACE", "PATCH"],
86+
transformer=list_to_comma_separated)
8287

83-
proxy = get_input("Enter proxy (proxy:port)")
88+
proxy = get_input("Enter proxy", instruction="(proxy:port)")
8489

8590
use_auth = get_confirm(" Use proxy authentication?")
8691
proxy_username = None
@@ -101,10 +106,14 @@ def get_input_proxy2():
101106

102107
def get_input_ssl():
103108
filename, cidr = get_host_input()
109+
tls_version = get_input("Select TLS version", "choice",
110+
choices=list(SSLScanner.TLS_VERSIONS.keys()),
111+
default="TLS 1.2")
104112
output, threads = get_common_inputs(filename or cidr)
105113

106114
scanner = SSLScanner()
107115
scanner.host_list = read_hosts(filename, cidr)
116+
scanner.tls_version = SSLScanner.TLS_VERSIONS[tls_version]
108117

109118
return scanner, output, threads
110119

@@ -121,16 +130,16 @@ def get_input_ping():
121130
return scanner, output, threads
122131

123132
def get_user_input():
124-
mode = get_input("Select mode", "choice",
125-
choices=["direct", "direct-no302", "proxy-check", "proxy-request", "ping", "ssl"])
133+
mode = get_input("Select scanning mode", "choice",
134+
choices=["Direct", "Direct-no302", "Proxy-check", "Proxy-request", "Ping", "SSL"])
126135

127136
input_handlers = {
128-
'direct': lambda: get_input_direct(no302=False),
129-
'direct-no302': lambda: get_input_direct(no302=True),
130-
'proxy-check': get_input_proxy,
131-
'proxy-request': get_input_proxy2,
132-
'ping': get_input_ping,
133-
'ssl': get_input_ssl
137+
'Direct': lambda: get_input_direct(no302=False),
138+
'Direct-no302': lambda: get_input_direct(no302=True),
139+
'Proxy-check': get_input_proxy,
140+
'Proxy-request': get_input_proxy2,
141+
'Ping': get_input_ping,
142+
'SSL': get_input_ssl
134143
}
135144

136145
scanner, output, threads = input_handlers[mode]()
@@ -143,7 +152,7 @@ def main():
143152

144153
if output:
145154
with open(output, 'a+') as file:
146-
if mode == 'proxy-check':
155+
if mode == 'Proxy-check':
147156
json.dump(scanner.success_list(), file, indent=2)
148157
else:
149158
file.write('\n'.join([str(x) for x in scanner.success_list()]) + '\n')

bugscanx/modules/scanners_pro/scanners/ssl.py

Lines changed: 68 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,71 @@
33
from .base import BaseScanner
44

55
class SSLScanner(BaseScanner):
6-
host_list = []
7-
8-
def get_task_list(self):
9-
for host in self.filter_list(self.host_list):
10-
yield {
11-
'host': host,
12-
}
13-
14-
def log_info(self, status, server_name_indication):
15-
super().log(f'{status:<6} {server_name_indication}')
16-
17-
def log_info_result(self, **kwargs):
18-
status = kwargs.get('status', '')
19-
if status:
20-
status = 'True'
21-
server_name_indication = kwargs.get('server_name_indication', '')
22-
self.log_info(status, server_name_indication)
23-
24-
def init(self):
25-
super().init()
26-
self.log_info('Status', 'Server Name Indication')
27-
self.log_info('------', '----------------------')
28-
29-
def task(self, payload):
30-
server_name_indication = payload['host']
31-
32-
if not server_name_indication:
33-
return
34-
35-
response = {
36-
'server_name_indication': server_name_indication,
37-
}
38-
39-
try:
40-
socket_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
41-
socket_client.settimeout(5)
42-
socket_client.connect((server_name_indication, 443))
43-
socket_client = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2).wrap_socket(
44-
socket_client, server_hostname=server_name_indication, do_handshake_on_connect=True
45-
)
46-
response['status'] = True
47-
self.task_success(server_name_indication)
48-
self.log_info_result(**response)
49-
50-
except Exception:
51-
pass
52-
53-
self.log_replace(server_name_indication)
54-
55-
def complete(self):
56-
self.log_replace(self.colorize("Scan completed", "GREEN"))
57-
super().complete()
6+
host_list = []
7+
8+
def __init__(self):
9+
super().__init__()
10+
self.tls_version = ssl.PROTOCOL_TLS
11+
12+
TLS_VERSIONS = {
13+
'TLS 1.0': ssl.PROTOCOL_TLSv1,
14+
'TLS 1.1': ssl.PROTOCOL_TLSv1_1,
15+
'TLS 1.2': ssl.PROTOCOL_TLSv1_2,
16+
'TLS 1.3': ssl.PROTOCOL_TLS
17+
}
18+
19+
def get_task_list(self):
20+
for host in self.filter_list(self.host_list):
21+
yield {
22+
'host': host,
23+
}
24+
25+
def log_info(self, **kwargs):
26+
kwargs.setdefault('color', '')
27+
kwargs.setdefault('sni', '')
28+
kwargs.setdefault('tls_version', '')
29+
30+
messages = [
31+
self.colorize('{tls_version:<8}', 'CYAN'),
32+
self.colorize('{sni}', 'LGRAY'),
33+
]
34+
super().log(' '.join(messages).format(**kwargs))
35+
36+
def log_info_result(self, **kwargs):
37+
self.log_info(**kwargs)
38+
39+
def init(self):
40+
super().init()
41+
self.log_info(tls_version='TLS', sni='SNI')
42+
self.log_info(tls_version='---', sni='---')
43+
44+
def task(self, payload):
45+
sni = payload['host']
46+
47+
if not sni:
48+
return
49+
50+
response = {
51+
'sni': sni,
52+
'tls_version': 'Unknown'
53+
}
54+
55+
try:
56+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as socket_client:
57+
socket_client.settimeout(5)
58+
socket_client.connect((sni, 443))
59+
context = ssl.SSLContext(self.tls_version)
60+
with context.wrap_socket(
61+
socket_client, server_hostname=sni, do_handshake_on_connect=True
62+
) as ssl_socket:
63+
response['tls_version'] = ssl_socket.version()
64+
self.task_success(sni)
65+
self.log_info_result(**response)
66+
except Exception:
67+
pass
68+
69+
self.log_replace(sni)
70+
71+
def complete(self):
72+
self.log_replace(self.colorize("Scan completed", "GREEN"))
73+
super().complete()

bugscanx/utils/common.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,25 @@ def get_input(
4242
validate_input=True,
4343
instruction="",
4444
mandatory=True,
45+
allow_comma_separated=True,
4546
**kwargs
4647
):
4748
common_params = {
48-
"message": f" {message}:",
49+
"message": f" {message.strip()}" + ("" if instruction else ":"),
4950
"default": "" if default is None else str(default),
5051
"qmark": kwargs.pop("qmark", ""),
5152
"amark": kwargs.pop("amark", ""),
5253
"style": style,
53-
"instruction": instruction,
54+
"instruction": instruction + (":" if instruction else ""),
5455
"mandatory": mandatory,
5556
}
5657

5758
if validators is None and validate_input:
5859
validators = INPUT_VALIDATORS.get(input_type, [])
59-
60+
6061
if validate_input and validators:
62+
if input_type == "number":
63+
validators = [lambda x: is_digit(x, allow_comma_separated)]
6164
common_params["validate"] = create_validator(validators)
6265

6366
input_type_params = {

0 commit comments

Comments
 (0)