Skip to content

Commit 6e3697a

Browse files
committed
update
1 parent d93d516 commit 6e3697a

File tree

2 files changed

+66
-25
lines changed

2 files changed

+66
-25
lines changed

managers/scanner_manager.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def scan_url(self, url, pbar=None):
100100
if is_spring is False:
101101
if pbar:
102102
pbar.update(1)
103+
pbar.refresh()
103104
return
104105

105106
# 进行路径检测
@@ -111,7 +112,9 @@ def scan_url(self, url, pbar=None):
111112
self.reporter.generate(url, is_spring, detected_paths, found_cves)
112113
if pbar:
113114
pbar.update(1)
115+
pbar.refresh()
114116
except Exception as e:
115117
logger.error(f"Error processing URL: {e}", extra={"target": url})
116118
if pbar:
117-
pbar.update(1)
119+
pbar.update(1)
120+
pbar.refresh()

scanners/path_detector.py

+62-24
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,53 @@
22
# -*- coding: utf-8 -*-
33
"""
44
File Name: path_detector.py
5-
Description : 路径检测模块,提高了路径探测速度和效率
5+
Description : 优化路径检测模块,提高路径探测速度和效率,增加 SSL 错误处理与重试机制
66
Author : sule01u
77
date: 2023/10/8
88
"""
99

1010
import time
1111
import requests
1212
from urllib.parse import urljoin
13-
from concurrent.futures import as_completed
13+
from concurrent.futures import ThreadPoolExecutor, as_completed
1414
from utils.custom_headers import TIMEOUT, DEFAULT_HEADER
15+
from colorama import Fore
1516
from utils.logging_config import configure_logger
16-
from utils.global_thread_pool import GlobalThreadPool # 引入全局线程池
1717
from requests.adapters import HTTPAdapter
1818
from urllib3.util.retry import Retry
1919
import threading
20+
import ssl
21+
from urllib3.exceptions import InsecureRequestWarning
22+
import warnings
23+
24+
# 禁用urllib3中的不安全请求警告
25+
warnings.simplefilter('ignore', InsecureRequestWarning)
2026

2127
# 初始化日志记录
2228
logger = configure_logger(__name__)
2329

30+
class SSLAdapter(HTTPAdapter):
31+
"""自定义 SSL 适配器,指定 SSL/TLS 版本"""
32+
def __init__(self, ssl_version=None, **kwargs):
33+
self.ssl_version = ssl_version
34+
super().__init__(**kwargs)
35+
36+
def init_poolmanager(self, *args, **kwargs):
37+
kwargs['ssl_version'] = self.ssl_version
38+
super().init_poolmanager(*args, **kwargs)
39+
40+
def proxy_manager_for(self, *args, **kwargs):
41+
kwargs['ssl_version'] = self.ssl_version
42+
return super().proxy_manager_for(*args, **kwargs)
43+
2444
class PathDetector:
2545
"""路径探测类"""
2646
MAX_FAILED_COUNT = 80
27-
MAX_SUCCESS_COUNT = 80
47+
MAX_SUCCESS_COUNT = 50
2848
CHUNK_SIZE = 1024
2949
SSE_MAX_SIZE = 5120 # 5KB
3050
MAX_RESPONSE_LENGTH = 102400 # 100KB
51+
PATH_THREAD_COUNT = 3 # 使用独立的3个线程池进行路径探测
3152

3253
def __init__(self, paths, proxy_manager):
3354
self.paths = paths
@@ -39,9 +60,10 @@ def detect(self, url):
3960
path_failed_count = 0
4061
path_success_count = 0
4162
detected_paths = []
42-
try:
43-
# 使用全局线程池进行并发探测
44-
futures = {GlobalThreadPool.submit_task(self._detect_path, url, path, signature): path for path, signature in self.paths.items()}
63+
64+
# 使用独立的线程池进行路径探测,并指定最大线程数为3
65+
with ThreadPoolExecutor(max_workers=self.PATH_THREAD_COUNT) as executor:
66+
futures = {executor.submit(self._detect_path, url, path, signature): path for path, signature in self.paths.items()}
4567

4668
for future in as_completed(futures):
4769
path = futures[future]
@@ -50,7 +72,6 @@ def detect(self, url):
5072
if result:
5173
detected_paths.append(result)
5274
path_success_count += 1
53-
logger.info(f"[Success] Detected sensitive path: {result}", extra={"target": result})
5475

5576
if path_success_count > self.MAX_SUCCESS_COUNT:
5677
logger.info(f"Exceeded maximum success count of {self.MAX_SUCCESS_COUNT}, stopping path detection for {url}")
@@ -64,14 +85,6 @@ def detect(self, url):
6485
logger.info(f"Exceeded maximum failed count of {self.MAX_FAILED_COUNT}, stopping path detection for {url}")
6586
break
6687
time.sleep(0.05) # 防止过快请求导致目标被封禁
67-
except KeyboardInterrupt:
68-
logger.warning("User interrupted the path detection process with Ctrl + C")
69-
# 取消所有未完成的任务
70-
for future in futures:
71-
future.cancel()
72-
logger.info("All pending tasks were cancelled successfully.")
73-
finally:
74-
logger.info(f"Path detection process for {url} finished.")
7588

7689
return detected_paths
7790

@@ -88,7 +101,6 @@ def _make_request(self, url):
88101
session = self._get_session() # 获取线程本地的 Session 对象
89102
try:
90103
with session.get(url, stream=True, allow_redirects=False) as res:
91-
logger.debug(f"[{res.status_code}] [Content-Length: {res.headers.get('Content-Length', 0)}]", extra={"target": url})
92104
if "text/event-stream" in res.headers.get("Content-Type", ""):
93105
# SSE 流式传输处理
94106
content = b""
@@ -98,14 +110,40 @@ def _make_request(self, url):
98110
break
99111
return content.decode("utf-8", errors="ignore")
100112
elif res.status_code == 200:
113+
# ANSI 控制字符实现闪动效果
114+
blinking_effect = "\033[5m"
115+
# 修改logger.info调用,输出红色闪动的成功消息
116+
logger.info(f"{blinking_effect}{Fore.RED} [{res.status_code}] [Content-Length: {res.headers.get('Content-Length', 0)}] {Fore.CYAN}<-- [Success] {Fore.RESET}", extra={"target": url})
101117
# 返回前 MAX_RESPONSE_LENGTH 的内容
102118
return res.text[:self.MAX_RESPONSE_LENGTH]
119+
logger.info(f"[{res.status_code}] [Content-Length: {res.headers.get('Content-Length', 0)}]", extra={"target": url})
120+
except requests.exceptions.SSLError as ssl_error:
121+
logger.error(f"SSL error occurred for {url}: {ssl_error}", extra={"target": url})
122+
return self._retry_with_different_ssl_version(session, url) # 使用不同的 SSL/TLS 版本重新连接
103123
except requests.RequestException as e:
104124
logger.debug(f"Request error: {e}", extra={"target": url})
105125
except Exception as e:
106126
logger.error(f"An unexpected error occurred during path detection: {e}", extra={"target": url})
107127
return None
108128

129+
def _retry_with_different_ssl_version(self, session, url):
130+
"""尝试使用不同的 SSL/TLS 版本重新发起请求"""
131+
ssl_versions = [ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_2]
132+
for version in ssl_versions:
133+
try:
134+
# 使用不同的 SSL 版本进行重试
135+
ssl_adapter = SSLAdapter(ssl_version=version)
136+
session.mount('https://', ssl_adapter)
137+
with session.get(url, stream=True, allow_redirects=False) as res:
138+
if res.status_code == 200:
139+
logger.info(f"Successfully connected using SSL version: {version}", extra={"target": url})
140+
return res.text[:self.MAX_RESPONSE_LENGTH]
141+
except requests.exceptions.SSLError as ssl_error:
142+
logger.warning(f"Retry with SSL version {version} failed: {ssl_error}", extra={"target": url})
143+
except Exception as e:
144+
logger.error(f"An unexpected error occurred during SSL retry: {e}", extra={"target": url})
145+
return None
146+
109147
def _get_session(self):
110148
"""获取线程本地的 Session 对象,如果不存在则创建"""
111149
if not hasattr(self.thread_local, 'session'):
@@ -116,14 +154,17 @@ def _get_session(self):
116154
session.timeout = TIMEOUT
117155
session.max_redirects = 3
118156

157+
# 配置自定义 SSL 适配器
158+
ssl_adapter = SSLAdapter(ssl_version=ssl.PROTOCOL_TLSv1_2) # 默认使用 TLSv1.2
159+
session.mount('https://', ssl_adapter)
160+
119161
# 配置 HTTPAdapter,启用 keep-alive 和连接池
120162
adapter = HTTPAdapter(
121163
pool_connections=200,
122164
pool_maxsize=200,
123-
max_retries=Retry(total=3, backoff_factor=0.3)
165+
max_retries=Retry(total=3, backoff_factor=0.3) # 启用重试机制
124166
)
125167
session.mount('http://', adapter)
126-
session.mount('https://', adapter)
127168

128169
self.thread_local.session = session
129170
return self.thread_local.session
@@ -143,11 +184,8 @@ def close_sessions(detector_instance):
143184
if __name__ == '__main__':
144185
# 测试用例
145186
from managers.proxy_manager import ProxyManager
146-
147-
# 初始化全局线程池
148-
GlobalThreadPool.initialize(max_workers=50) # 新增:初始化全局线程池
149-
150187
proxy_manager = ProxyManager()
151188
paths = {"actuator": "_links", "actuator/beans": "beans"}
152189
path_d = PathDetector(paths, proxy_manager)
153-
print(path_d.detect("http://192.168.1.13:8080/"))
190+
print(path_d.detect("http://192.168.1.13:8080/"))
191+
print(path_d.detect("http://192.168.1.13:8083/"))

0 commit comments

Comments
 (0)