2
2
# -*- coding: utf-8 -*-
3
3
"""
4
4
File Name: path_detector.py
5
- Description : 路径检测模块,提高了路径探测速度和效率
5
+ Description : 优化路径检测模块,提高路径探测速度和效率,增加 SSL 错误处理与重试机制
6
6
Author : sule01u
7
7
date: 2023/10/8
8
8
"""
9
9
10
10
import time
11
11
import requests
12
12
from urllib .parse import urljoin
13
- from concurrent .futures import as_completed
13
+ from concurrent .futures import ThreadPoolExecutor , as_completed
14
14
from utils .custom_headers import TIMEOUT , DEFAULT_HEADER
15
+ from colorama import Fore
15
16
from utils .logging_config import configure_logger
16
- from utils .global_thread_pool import GlobalThreadPool # 引入全局线程池
17
17
from requests .adapters import HTTPAdapter
18
18
from urllib3 .util .retry import Retry
19
19
import threading
20
+ import ssl
21
+ from urllib3 .exceptions import InsecureRequestWarning
22
+ import warnings
23
+
24
+ # 禁用urllib3中的不安全请求警告
25
+ warnings .simplefilter ('ignore' , InsecureRequestWarning )
20
26
21
27
# 初始化日志记录
22
28
logger = configure_logger (__name__ )
23
29
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
+
24
44
class PathDetector :
25
45
"""路径探测类"""
26
46
MAX_FAILED_COUNT = 80
27
- MAX_SUCCESS_COUNT = 80
47
+ MAX_SUCCESS_COUNT = 50
28
48
CHUNK_SIZE = 1024
29
49
SSE_MAX_SIZE = 5120 # 5KB
30
50
MAX_RESPONSE_LENGTH = 102400 # 100KB
51
+ PATH_THREAD_COUNT = 3 # 使用独立的3个线程池进行路径探测
31
52
32
53
def __init__ (self , paths , proxy_manager ):
33
54
self .paths = paths
@@ -39,9 +60,10 @@ def detect(self, url):
39
60
path_failed_count = 0
40
61
path_success_count = 0
41
62
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 ()}
45
67
46
68
for future in as_completed (futures ):
47
69
path = futures [future ]
@@ -50,7 +72,6 @@ def detect(self, url):
50
72
if result :
51
73
detected_paths .append (result )
52
74
path_success_count += 1
53
- logger .info (f"[Success] Detected sensitive path: { result } " , extra = {"target" : result })
54
75
55
76
if path_success_count > self .MAX_SUCCESS_COUNT :
56
77
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):
64
85
logger .info (f"Exceeded maximum failed count of { self .MAX_FAILED_COUNT } , stopping path detection for { url } " )
65
86
break
66
87
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." )
75
88
76
89
return detected_paths
77
90
@@ -88,7 +101,6 @@ def _make_request(self, url):
88
101
session = self ._get_session () # 获取线程本地的 Session 对象
89
102
try :
90
103
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 })
92
104
if "text/event-stream" in res .headers .get ("Content-Type" , "" ):
93
105
# SSE 流式传输处理
94
106
content = b""
@@ -98,14 +110,40 @@ def _make_request(self, url):
98
110
break
99
111
return content .decode ("utf-8" , errors = "ignore" )
100
112
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 })
101
117
# 返回前 MAX_RESPONSE_LENGTH 的内容
102
118
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 版本重新连接
103
123
except requests .RequestException as e :
104
124
logger .debug (f"Request error: { e } " , extra = {"target" : url })
105
125
except Exception as e :
106
126
logger .error (f"An unexpected error occurred during path detection: { e } " , extra = {"target" : url })
107
127
return None
108
128
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
+
109
147
def _get_session (self ):
110
148
"""获取线程本地的 Session 对象,如果不存在则创建"""
111
149
if not hasattr (self .thread_local , 'session' ):
@@ -116,14 +154,17 @@ def _get_session(self):
116
154
session .timeout = TIMEOUT
117
155
session .max_redirects = 3
118
156
157
+ # 配置自定义 SSL 适配器
158
+ ssl_adapter = SSLAdapter (ssl_version = ssl .PROTOCOL_TLSv1_2 ) # 默认使用 TLSv1.2
159
+ session .mount ('https://' , ssl_adapter )
160
+
119
161
# 配置 HTTPAdapter,启用 keep-alive 和连接池
120
162
adapter = HTTPAdapter (
121
163
pool_connections = 200 ,
122
164
pool_maxsize = 200 ,
123
- max_retries = Retry (total = 3 , backoff_factor = 0.3 )
165
+ max_retries = Retry (total = 3 , backoff_factor = 0.3 ) # 启用重试机制
124
166
)
125
167
session .mount ('http://' , adapter )
126
- session .mount ('https://' , adapter )
127
168
128
169
self .thread_local .session = session
129
170
return self .thread_local .session
@@ -143,11 +184,8 @@ def close_sessions(detector_instance):
143
184
if __name__ == '__main__' :
144
185
# 测试用例
145
186
from managers .proxy_manager import ProxyManager
146
-
147
- # 初始化全局线程池
148
- GlobalThreadPool .initialize (max_workers = 50 ) # 新增:初始化全局线程池
149
-
150
187
proxy_manager = ProxyManager ()
151
188
paths = {"actuator" : "_links" , "actuator/beans" : "beans" }
152
189
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