- 6 大模块:扫描/账号管理/节点部署/暴力破解/算力调度/监控运维 - SKILL 总控 + 子模块 SKILL - 排除大文件(>5MB)与敏感凭证 Co-authored-by: Cursor <cursoragent@cursor.com>
208 lines
6.7 KiB
Python
208 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
扫描结果 → 暴力破解 桥接工具
|
||
================================
|
||
从 01_扫描模块 的输出结果中提取 SSH 开放端口的 IP,
|
||
生成暴力破解器可用的目标列表。
|
||
|
||
支持的输入格式:
|
||
- 01_扫描模块 mumayi_full_scan.py 输出的 JSON
|
||
- nmap XML 输出
|
||
- masscan JSON 输出
|
||
- 纯文本 IP 列表
|
||
|
||
用法:
|
||
# 从扫描JSON导入
|
||
python3 scan_to_brute.py --input ../01_扫描模块/results/scan_results.json --output targets.txt
|
||
|
||
# 从nmap XML导入
|
||
python3 scan_to_brute.py --input scan.xml --format nmap --output targets.txt
|
||
|
||
# 从CSV导入(指定IP列和端口列)
|
||
python3 scan_to_brute.py --input hosts.csv --format csv --ip-col ip --port-col port --output targets.txt
|
||
|
||
# 直接对接暴力破解
|
||
python3 scan_to_brute.py --input scan.json --pipe | python3 ssh_bruter.py --targets /dev/stdin
|
||
"""
|
||
|
||
import json
|
||
import csv
|
||
import sys
|
||
import argparse
|
||
import xml.etree.ElementTree as ET
|
||
from pathlib import Path
|
||
|
||
# SSH 相关端口
|
||
SSH_PORTS = {22, 2222, 22222, 10022, 20022, 222, 2022}
|
||
|
||
|
||
def extract_from_json(filepath):
|
||
"""从JSON扫描结果提取SSH目标"""
|
||
targets = []
|
||
with open(filepath, "r", encoding="utf-8") as f:
|
||
data = json.load(f)
|
||
|
||
if isinstance(data, list):
|
||
for item in data:
|
||
ip = item.get("ip") or item.get("host") or item.get("address")
|
||
if not ip:
|
||
continue
|
||
|
||
# 查找SSH端口
|
||
open_ports = item.get("open_ports") or item.get("ports") or []
|
||
ssh_ports = []
|
||
for p in open_ports:
|
||
if isinstance(p, dict):
|
||
port_num = p.get("port", 0)
|
||
service = p.get("service", "").lower()
|
||
if port_num in SSH_PORTS or "ssh" in service:
|
||
ssh_ports.append(port_num)
|
||
elif isinstance(p, int):
|
||
if p in SSH_PORTS:
|
||
ssh_ports.append(p)
|
||
|
||
if ssh_ports:
|
||
for port in ssh_ports:
|
||
targets.append(f"{ip}:{port}")
|
||
# 如果没发现SSH端口但有该IP,默认加22
|
||
elif item.get("ssh") or item.get("has_ssh"):
|
||
targets.append(f"{ip}:22")
|
||
|
||
elif isinstance(data, dict):
|
||
# 键值对格式 {ip: {ports: [...]}}
|
||
for ip, info in data.items():
|
||
if isinstance(info, dict):
|
||
ports = info.get("ports") or info.get("open_ports") or []
|
||
for p in ports:
|
||
port_num = p if isinstance(p, int) else p.get("port", 0)
|
||
if port_num in SSH_PORTS:
|
||
targets.append(f"{ip}:{port_num}")
|
||
|
||
return targets
|
||
|
||
|
||
def extract_from_nmap_xml(filepath):
|
||
"""从nmap XML输出提取SSH目标"""
|
||
targets = []
|
||
tree = ET.parse(filepath)
|
||
root = tree.getroot()
|
||
|
||
for host in root.findall(".//host"):
|
||
addr_elem = host.find("address[@addrtype='ipv4']")
|
||
if addr_elem is None:
|
||
continue
|
||
ip = addr_elem.get("addr")
|
||
|
||
for port in host.findall(".//port"):
|
||
portid = int(port.get("portid", 0))
|
||
state = port.find("state")
|
||
service = port.find("service")
|
||
|
||
if state is not None and state.get("state") == "open":
|
||
svc_name = service.get("name", "") if service is not None else ""
|
||
if portid in SSH_PORTS or "ssh" in svc_name.lower():
|
||
targets.append(f"{ip}:{portid}")
|
||
|
||
return targets
|
||
|
||
|
||
def extract_from_masscan(filepath):
|
||
"""从masscan JSON输出提取SSH目标"""
|
||
targets = []
|
||
with open(filepath, "r") as f:
|
||
# masscan JSON格式可能不完全标准
|
||
content = f.read().strip()
|
||
if content.endswith(","):
|
||
content = content[:-1]
|
||
if not content.startswith("["):
|
||
content = "[" + content + "]"
|
||
data = json.loads(content)
|
||
|
||
for item in data:
|
||
ip = item.get("ip")
|
||
ports = item.get("ports", [])
|
||
for p in ports:
|
||
port_num = p.get("port", 0)
|
||
if port_num in SSH_PORTS:
|
||
targets.append(f"{ip}:{port_num}")
|
||
|
||
return targets
|
||
|
||
|
||
def extract_from_csv(filepath, ip_col="ip", port_col="port"):
|
||
"""从CSV提取SSH目标"""
|
||
targets = []
|
||
with open(filepath, "r", encoding="utf-8") as f:
|
||
reader = csv.DictReader(f)
|
||
for row in reader:
|
||
ip = row.get(ip_col, "").strip()
|
||
port = row.get(port_col, "22").strip()
|
||
if ip:
|
||
port_num = int(port) if port.isdigit() else 22
|
||
if port_num in SSH_PORTS:
|
||
targets.append(f"{ip}:{port_num}")
|
||
|
||
return targets
|
||
|
||
|
||
def extract_from_text(filepath):
|
||
"""从纯文本提取(每行一个IP或IP:port)"""
|
||
targets = []
|
||
with open(filepath, "r") as f:
|
||
for line in f:
|
||
line = line.strip()
|
||
if not line or line.startswith("#"):
|
||
continue
|
||
if ":" in line:
|
||
targets.append(line)
|
||
else:
|
||
targets.append(f"{line}:22")
|
||
return targets
|
||
|
||
|
||
def main():
|
||
parser = argparse.ArgumentParser(description="扫描结果转暴力破解目标列表")
|
||
parser.add_argument("--input", "-i", required=True, help="输入文件路径")
|
||
parser.add_argument("--format", "-f", choices=["json", "nmap", "masscan", "csv", "text"],
|
||
default="json", help="输入格式 (默认 json)")
|
||
parser.add_argument("--output", "-o", help="输出文件路径")
|
||
parser.add_argument("--ip-col", default="ip", help="CSV中IP列名")
|
||
parser.add_argument("--port-col", default="port", help="CSV中端口列名")
|
||
parser.add_argument("--pipe", action="store_true", help="输出到stdout(管道模式)")
|
||
|
||
args = parser.parse_args()
|
||
|
||
# 提取目标
|
||
fmt = args.format
|
||
if fmt == "json":
|
||
targets = extract_from_json(args.input)
|
||
elif fmt == "nmap":
|
||
targets = extract_from_nmap_xml(args.input)
|
||
elif fmt == "masscan":
|
||
targets = extract_from_masscan(args.input)
|
||
elif fmt == "csv":
|
||
targets = extract_from_csv(args.input, args.ip_col, args.port_col)
|
||
elif fmt == "text":
|
||
targets = extract_from_text(args.input)
|
||
else:
|
||
targets = extract_from_json(args.input)
|
||
|
||
# 去重
|
||
targets = list(dict.fromkeys(targets))
|
||
|
||
print(f"[*] 提取到 {len(targets)} 个SSH目标", file=sys.stderr)
|
||
|
||
# 输出
|
||
if args.pipe or not args.output:
|
||
for t in targets:
|
||
print(t)
|
||
else:
|
||
with open(args.output, "w") as f:
|
||
for t in targets:
|
||
f.write(t + "\n")
|
||
print(f"[+] 已保存到 {args.output}", file=sys.stderr)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|