#!/usr/bin/env python3 """ 已登录IP深度增强分析脚本 ======================== 对 KR.分布式矩阵IP_已登录 中的已成功登录IP进行全方位深度分析和字段丰富。 功能: 1. GeoIP地理定位(国家/省/市/ISP/AS号) 2. 真实连接验证 + 系统信息采集(CPU/内存/磁盘/网络/内核) 3. 多端口扫描(40+常用端口) 4. 服务版本识别 + 已知漏洞匹配(CVE) 5. 分布式算力矩阵业务字段(算力评估/部署适配度/节点角色推荐) 6. 问题诊断与风险评估 所有结果写回 MongoDB KR.分布式矩阵IP_已登录 用法: python3 enhance_logged_ips.py # 增强所有已登录IP python3 enhance_logged_ips.py --ip 1.2.3.4 # 仅增强指定IP python3 enhance_logged_ips.py --dry-run # 仅分析不写库 依赖: pip install pymongo asyncssh aiohttp """ import asyncio import json import sys import time import socket import struct import argparse import logging from datetime import datetime from pathlib import Path from collections import defaultdict sys.stdout.reconfigure(line_buffering=True) sys.stderr.reconfigure(line_buffering=True) # ===================================================== # 配置 # ===================================================== MONGO_URI = 'mongodb://admin:admin123@localhost:27017/?authSource=admin' MONGO_DB = 'KR' MONGO_COLLECTION = '分布式矩阵IP_已登录' # 扫描端口列表(40+常用端口) SCAN_PORTS = { # SSH相关 22: "SSH", 2222: "SSH-Alt", 2200: "SSH-Alt2", # Web服务 80: "HTTP", 443: "HTTPS", 8080: "HTTP-Proxy", 8443: "HTTPS-Alt", 8888: "宝塔面板", 8000: "HTTP-Dev", 8001: "HTTP-Dev2", 3000: "Grafana/Node", 9090: "Prometheus", # 远程桌面 3389: "RDP", 5900: "VNC", 5901: "VNC-1", # 数据库 3306: "MySQL", 5432: "PostgreSQL", 27017: "MongoDB", 6379: "Redis", 11211: "Memcached", 9200: "Elasticsearch", 5984: "CouchDB", 8529: "ArangoDB", # 消息队列 5672: "RabbitMQ", 9092: "Kafka", 2181: "ZooKeeper", # 容器/编排 2375: "Docker-API", 2376: "Docker-TLS", 10250: "Kubelet", 6443: "K8s-API", 8500: "Consul", # FTP/文件 21: "FTP", 69: "TFTP", 445: "SMB", 139: "NetBIOS", 873: "Rsync", 2049: "NFS", # 其它 23: "Telnet", 25: "SMTP", 53: "DNS", 110: "POP3", 143: "IMAP", 161: "SNMP", 1080: "SOCKS", 1433: "MSSQL", 1521: "Oracle", 9999: "通用管理", } # SSH版本 → 已知漏洞映射 SSH_VULNERABILITIES = { "OpenSSH_5": [ {"cve": "CVE-2016-0777", "severity": "HIGH", "desc": "roaming信息泄露,可窃取私钥"}, {"cve": "CVE-2016-0778", "severity": "HIGH", "desc": "roaming缓冲区溢出"}, {"cve": "CVE-2015-5600", "severity": "HIGH", "desc": "MaxAuthTries绕过暴力破解"}, ], "OpenSSH_6": [ {"cve": "CVE-2016-0777", "severity": "HIGH", "desc": "roaming信息泄露"}, {"cve": "CVE-2016-10009", "severity": "HIGH", "desc": "ssh-agent远程代码执行"}, {"cve": "CVE-2016-10010", "severity": "MEDIUM", "desc": "权限提升漏洞"}, ], "OpenSSH_7.0": [ {"cve": "CVE-2016-10009", "severity": "HIGH", "desc": "ssh-agent远程代码执行"}, ], "OpenSSH_7.2": [ {"cve": "CVE-2016-10009", "severity": "HIGH", "desc": "ssh-agent远程代码执行"}, {"cve": "CVE-2016-10012", "severity": "MEDIUM", "desc": "沙箱绕过"}, ], "OpenSSH_7.4": [ {"cve": "CVE-2017-15906", "severity": "MEDIUM", "desc": "只读模式下可创建空文件"}, ], "OpenSSH_8": [ {"cve": "CVE-2021-28041", "severity": "MEDIUM", "desc": "ssh-agent双重释放漏洞"}, {"cve": "CVE-2021-41617", "severity": "MEDIUM", "desc": "AuthorizedKeysCommand权限提升"}, ], "OpenSSH_9.0": [ {"cve": "CVE-2023-38408", "severity": "HIGH", "desc": "PKCS#11远程代码执行"}, ], "OpenSSH_9.1": [ {"cve": "CVE-2023-38408", "severity": "HIGH", "desc": "PKCS#11远程代码执行"}, ], "OpenSSH_9.3": [ {"cve": "CVE-2024-6387", "severity": "CRITICAL", "desc": "regreSSHion - 远程无认证RCE"}, ], "OpenSSH_9.7": [ {"cve": "CVE-2024-6387", "severity": "CRITICAL", "desc": "regreSSHion - 远程无认证RCE"}, ], "OpenSSH_9.8": [], # 已修复 "OpenSSH_9.9": [], # 较新版本,暂无已知高危漏洞 } # 服务端口 → 已知漏洞映射 SERVICE_VULNERABILITIES = { 6379: [ {"cve": "CVE-2022-0543", "severity": "CRITICAL", "desc": "Redis Lua沙箱逃逸RCE"}, {"vuln": "未授权访问", "severity": "HIGH", "desc": "Redis默认无密码,可直接写入SSH公钥"}, ], 27017: [ {"vuln": "未授权访问", "severity": "HIGH", "desc": "MongoDB默认无认证,可直接读写数据"}, ], 2375: [ {"vuln": "Docker API未授权", "severity": "CRITICAL", "desc": "Docker Remote API未授权,可直接创建容器逃逸"}, ], 9200: [ {"vuln": "未授权访问", "severity": "HIGH", "desc": "Elasticsearch默认无认证"}, {"cve": "CVE-2015-1427", "severity": "CRITICAL", "desc": "Groovy脚本RCE"}, ], 11211: [ {"vuln": "未授权访问", "severity": "HIGH", "desc": "Memcached默认无认证,可被用于DDoS放大"}, ], 3306: [ {"vuln": "弱密码", "severity": "MEDIUM", "desc": "MySQL默认root空密码或弱密码"}, ], 8888: [ {"vuln": "宝塔面板", "severity": "MEDIUM", "desc": "宝塔默认端口,可能存在历史漏洞"}, {"cve": "CVE-2024-XXX", "severity": "HIGH", "desc": "宝塔phpmyadmin未授权访问(历史)"}, ], 5900: [ {"vuln": "VNC弱认证", "severity": "HIGH", "desc": "VNC通常使用弱密码或无密码"}, ], 23: [ {"vuln": "Telnet明文传输", "severity": "HIGH", "desc": "Telnet传输不加密,凭证可被嗅探"}, ], 445: [ {"cve": "CVE-2017-0144", "severity": "CRITICAL", "desc": "EternalBlue永恒之蓝SMB远程代码执行"}, ], 873: [ {"vuln": "Rsync未授权", "severity": "HIGH", "desc": "Rsync无认证模式可读写任意文件"}, ], 10250: [ {"vuln": "Kubelet未授权", "severity": "CRITICAL", "desc": "Kubelet API未授权可执行容器命令"}, ], } # ===================================================== # GeoIP 查询(使用免费API) # ===================================================== async def get_geoip_info(ip): """通过多个免费API获取IP地理信息""" import aiohttp result = { "country": "", "country_code": "", "province": "", "city": "", "isp": "", "org": "", "as_number": "", "as_name": "", "latitude": 0.0, "longitude": 0.0, "timezone": "", } # API 1: ip-api.com(免费,限速45/min) try: async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: url = f"http://ip-api.com/json/{ip}?fields=status,message,country,countryCode,regionName,city,lat,lon,timezone,isp,org,as,query&lang=zh-CN" async with session.get(url) as resp: if resp.status == 200: data = await resp.json() if data.get("status") == "success": result["country"] = data.get("country", "") result["country_code"] = data.get("countryCode", "") result["province"] = data.get("regionName", "") result["city"] = data.get("city", "") result["isp"] = data.get("isp", "") result["org"] = data.get("org", "") as_info = data.get("as", "") if as_info: parts = as_info.split(" ", 1) result["as_number"] = parts[0] if parts else "" result["as_name"] = parts[1] if len(parts) > 1 else "" result["latitude"] = data.get("lat", 0.0) result["longitude"] = data.get("lon", 0.0) result["timezone"] = data.get("timezone", "") return result except Exception as e: logging.warning(f"ip-api.com查询失败({ip}): {e}") # API 2: ipinfo.io(备用) try: async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session: url = f"https://ipinfo.io/{ip}/json" async with session.get(url) as resp: if resp.status == 200: data = await resp.json() result["country"] = data.get("country", "") result["province"] = data.get("region", "") result["city"] = data.get("city", "") result["org"] = data.get("org", "") result["timezone"] = data.get("timezone", "") loc = data.get("loc", "") if loc and "," in loc: lat, lon = loc.split(",") result["latitude"] = float(lat) result["longitude"] = float(lon) except Exception as e: logging.warning(f"ipinfo.io查询失败({ip}): {e}") return result # ===================================================== # 端口扫描 # ===================================================== async def scan_port(ip, port, timeout=3): """异步扫描单个端口""" try: _, writer = await asyncio.wait_for( asyncio.open_connection(ip, port), timeout=timeout ) writer.close() await writer.wait_closed() return port, True except Exception: return port, False async def scan_all_ports(ip, timeout=3, concurrency=50): """扫描所有配置的端口""" semaphore = asyncio.Semaphore(concurrency) results = {} async def limited_scan(p): async with semaphore: return await scan_port(ip, p, timeout) tasks = [limited_scan(port) for port in SCAN_PORTS] for coro in asyncio.as_completed(tasks): port, is_open = await coro if is_open: results[port] = SCAN_PORTS[port] return results async def grab_banner(ip, port, timeout=5): """尝试抓取服务Banner""" try: reader, writer = await asyncio.wait_for( asyncio.open_connection(ip, port), timeout=timeout ) # 对HTTP端口发送请求 if port in (80, 8080, 8000, 8001, 8888, 3000, 9090, 8443, 443): writer.write(b"GET / HTTP/1.0\r\nHost: " + ip.encode() + b"\r\n\r\n") await writer.drain() elif port in (21, 25, 110, 143): pass # 这些端口主动发Banner else: writer.write(b"\r\n") await writer.drain() try: data = await asyncio.wait_for(reader.read(1024), timeout=3) banner = data.decode('utf-8', errors='replace').strip()[:200] except Exception: banner = "" writer.close() await writer.wait_closed() return banner except Exception: return "" # ===================================================== # SSH连接验证 + 系统信息采集 # ===================================================== async def verify_and_collect_sysinfo(ip, port, username, password, timeout=15): """ SSH登录验证 + 采集系统信息 返回 (verified, sysinfo_dict, issues_list) """ sysinfo = { "hostname": "", "kernel": "", "os_release": "", "cpu_model": "", "cpu_cores": 0, "cpu_threads": 0, "cpu_freq_mhz": 0, "ram_total_mb": 0, "ram_used_mb": 0, "ram_free_mb": 0, "disk_total_gb": 0, "disk_used_gb": 0, "disk_free_gb": 0, "uptime_seconds": 0, "uptime_human": "", "load_avg": "", "network_interfaces": [], "public_ip_verified": "", "docker_installed": False, "docker_version": "", "python_version": "", "arch": "", "swap_total_mb": 0, "swap_used_mb": 0, } issues = [] verified = False try: import asyncssh async with asyncssh.connect( ip, port=port, username=username, password=password, known_hosts=None, login_timeout=timeout, client_keys=[], kex_algs=["ecdh-sha2-nistp256", "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group1-sha1"], encryption_algs=["aes128-ctr", "aes256-ctr", "aes128-cbc", "aes256-cbc", "3des-cbc"], server_host_key_algs=["ssh-rsa", "rsa-sha2-256", "rsa-sha2-512", "ecdsa-sha2-nistp256", "ssh-ed25519"], ) as conn: verified = True # 先用一条简单命令测试shell是否可用 shell_available = False shell_error_type = "" try: test_r = await asyncio.wait_for( conn.run("echo OK", check=False), timeout=8 ) if (test_r.stdout or "").strip() == "OK": shell_available = True except Exception as e: shell_error_type = str(e)[:100] if "Connection lost" in str(e) or "connection closed" in str(e).lower(): issues.append("SSH登录成功但Shell立即断开 → 可能是蜜罐或受限设备") sysinfo["_shell_type"] = "honeypot_or_restricted" elif "Session request failed" in str(e): issues.append("SSH登录成功但不允许Shell会话 → 可能是网络设备(路由器/交换机)或仅允许端口转发") sysinfo["_shell_type"] = "no_shell_session" else: issues.append(f"Shell测试失败: {str(e)[:80]}") sysinfo["_shell_type"] = "unknown_error" if not shell_available: # 标记shell不可用,跳过后续命令采集 sysinfo["_shell_available"] = False return verified, sysinfo, issues sysinfo["_shell_available"] = True sysinfo["_shell_type"] = "interactive" # 定义要收集的命令(合并多个命令减少连接次数) # 第一批:基础信息(单行输出,快速) commands_fast = { "hostname": "hostname 2>/dev/null", "kernel": "uname -r 2>/dev/null", "arch": "uname -m 2>/dev/null", "cpu_cores": "nproc 2>/dev/null || grep -c processor /proc/cpuinfo 2>/dev/null", "uptime": "cat /proc/uptime 2>/dev/null", "load": "cat /proc/loadavg 2>/dev/null", "docker_ver": "docker --version 2>/dev/null", "python_ver": "python3 --version 2>/dev/null || python --version 2>/dev/null", "selinux": "getenforce 2>/dev/null || echo disabled", } # 第二批:详细信息(可能较慢) commands_detail = { "os_release": "cat /etc/os-release 2>/dev/null | head -5", "cpu_info": "cat /proc/cpuinfo 2>/dev/null | grep -E 'model name|processor|cpu MHz' | head -10", "mem_info": "free -m 2>/dev/null | head -3", "disk_info": "df -h / 2>/dev/null | tail -1", "network": "ip -4 addr show 2>/dev/null | grep inet | grep -v 127.0.0.1 | head -5", "swap": "free -m 2>/dev/null | grep Swap", "users_logged": "who 2>/dev/null | wc -l", "crontab": "crontab -l 2>/dev/null | wc -l", "listening_ports": "ss -tlnp 2>/dev/null | tail -20 || netstat -tlnp 2>/dev/null | tail -20", "iptables_rules": "iptables -L -n 2>/dev/null | wc -l || echo 0", } # 第三批:网络相关(可能最慢) commands_slow = { "public_ip": "curl -s --connect-timeout 5 ifconfig.me 2>/dev/null || curl -s --connect-timeout 5 icanhazip.com 2>/dev/null", "last_logins": "last -5 2>/dev/null | head -5", } commands = {**commands_fast, **commands_detail, **commands_slow} results = {} cmd_fail_count = 0 for key, cmd in commands.items(): try: r = await asyncio.wait_for( conn.run(cmd, check=False), timeout=8 ) results[key] = (r.stdout or "").strip() except Exception as e: results[key] = "" cmd_fail_count += 1 # 如果连续失败3次,说明连接不稳定,停止采集 if cmd_fail_count >= 3: issues.append(f"命令执行连续失败{cmd_fail_count}次,停止采集 ({str(e)[:50]})") break # 解析结果 sysinfo["hostname"] = results.get("hostname", "")[:50] sysinfo["kernel"] = results.get("kernel", "")[:50] sysinfo["arch"] = results.get("arch", "")[:20] # OS释义 os_text = results.get("os_release", "") for line in os_text.split("\n"): if line.startswith("PRETTY_NAME="): sysinfo["os_release"] = line.split("=", 1)[1].strip().strip('"')[:80] break # CPU cpu_text = results.get("cpu_info", "") for line in cpu_text.split("\n"): line = line.strip() if line.startswith("model name"): sysinfo["cpu_model"] = line.split(":", 1)[1].strip()[:80] if ":" in line else "" elif line.startswith("cpu MHz"): try: sysinfo["cpu_freq_mhz"] = int(float(line.split(":", 1)[1].strip())) except Exception: pass try: cores = results.get("cpu_cores", "0").strip().split("\n")[0] sysinfo["cpu_cores"] = int(cores) sysinfo["cpu_threads"] = int(cores) # 简化 except Exception: pass # 内存 mem_text = results.get("mem_info", "") for line in mem_text.split("\n"): if line.startswith("Mem:"): parts = line.split() try: sysinfo["ram_total_mb"] = int(parts[1]) sysinfo["ram_used_mb"] = int(parts[2]) sysinfo["ram_free_mb"] = int(parts[3]) if len(parts) > 3 else 0 except Exception: pass # Swap swap_text = results.get("swap", "") if swap_text.startswith("Swap:"): parts = swap_text.split() try: sysinfo["swap_total_mb"] = int(parts[1]) sysinfo["swap_used_mb"] = int(parts[2]) except Exception: pass # 磁盘 disk_text = results.get("disk_info", "") if disk_text: parts = disk_text.split() try: def parse_size(s): s = s.upper() if s.endswith("G"): return float(s[:-1]) elif s.endswith("T"): return float(s[:-1]) * 1024 elif s.endswith("M"): return float(s[:-1]) / 1024 return 0 if len(parts) >= 4: sysinfo["disk_total_gb"] = round(parse_size(parts[1]), 1) sysinfo["disk_used_gb"] = round(parse_size(parts[2]), 1) sysinfo["disk_free_gb"] = round(parse_size(parts[3]), 1) except Exception: pass # Uptime uptime_text = results.get("uptime", "") if uptime_text: try: secs = float(uptime_text.split()[0]) sysinfo["uptime_seconds"] = int(secs) days = int(secs // 86400) hours = int((secs % 86400) // 3600) sysinfo["uptime_human"] = f"{days}天{hours}小时" except Exception: pass # Load average sysinfo["load_avg"] = results.get("load", "")[:30] # Network interfaces net_text = results.get("network", "") interfaces = [] for line in net_text.split("\n"): line = line.strip() if "inet " in line: parts = line.split() for i, p in enumerate(parts): if p == "inet" and i + 1 < len(parts): interfaces.append(parts[i + 1]) sysinfo["network_interfaces"] = interfaces # Public IP verification sysinfo["public_ip_verified"] = results.get("public_ip", "")[:20] # Docker docker_ver = results.get("docker_ver", "") if docker_ver: sysinfo["docker_installed"] = True sysinfo["docker_version"] = docker_ver[:50] # Python sysinfo["python_version"] = results.get("python_ver", "")[:30] # 问题诊断 # 1. 内存不足 if sysinfo["ram_total_mb"] > 0 and sysinfo["ram_total_mb"] < 512: issues.append(f"内存不足: 仅{sysinfo['ram_total_mb']}MB,不满足算力节点最低要求(512MB)") elif sysinfo["ram_total_mb"] > 0 and sysinfo["ram_used_mb"] / sysinfo["ram_total_mb"] > 0.9: issues.append(f"内存使用率过高: {sysinfo['ram_used_mb']}/{sysinfo['ram_total_mb']}MB ({sysinfo['ram_used_mb']/sysinfo['ram_total_mb']*100:.0f}%)") # 2. 磁盘不足 if sysinfo["disk_free_gb"] > 0 and sysinfo["disk_free_gb"] < 1: issues.append(f"磁盘空间不足: 仅剩{sysinfo['disk_free_gb']}GB") # 3. 高负载 load_text = sysinfo["load_avg"] if load_text and sysinfo["cpu_cores"] > 0: try: load_1m = float(load_text.split()[0]) if load_1m > sysinfo["cpu_cores"] * 2: issues.append(f"系统负载过高: {load_1m} (CPU核数:{sysinfo['cpu_cores']})") except Exception: pass # 4. SSH版本过旧 # (外部调用时判断) # 5. SELinux状态 selinux = results.get("selinux", "").lower() if "enforcing" in selinux: issues.append("SELinux启用(enforcing),可能影响部署") # 6. 防火墙规则 try: iptables_count = int(results.get("iptables_rules", "0").strip()) if iptables_count > 10: issues.append(f"检测到{iptables_count}条iptables规则,可能影响通信") except Exception: pass # 7. 其他用户登录 try: users_count = int(results.get("users_logged", "0").strip()) if users_count > 0: issues.append(f"当前有{users_count}个用户在线") except Exception: pass # 8. 定时任务 try: cron_count = int(results.get("crontab", "0").strip()) if cron_count > 0: issues.append(f"发现{cron_count}个crontab定时任务") except Exception: pass # 监听端口信息 listening = results.get("listening_ports", "") sysinfo["_listening_ports_raw"] = listening[:500] except ImportError: issues.append("asyncssh未安装,无法进行SSH连接验证") except Exception as e: ename = type(e).__name__ emsg = str(e)[:100] if "PermissionDenied" in ename or "auth" in emsg.lower(): issues.append(f"SSH认证失败: 密码可能已变更") elif "Timeout" in ename or "timeout" in emsg.lower(): issues.append(f"SSH连接超时: 主机可能已不可达") elif "ConnectionRefused" in ename or "refused" in emsg.lower(): issues.append(f"SSH连接被拒绝: 端口可能已关闭") elif "ConnectionLost" in ename: issues.append(f"SSH连接丢失: 网络不稳定") else: issues.append(f"SSH连接异常: {ename} - {emsg}") return verified, sysinfo, issues # ===================================================== # 漏洞匹配 # ===================================================== def match_ssh_vulnerabilities(ssh_version): """根据SSH版本匹配已知漏洞""" vulns = [] if not ssh_version: return vulns ver = ssh_version.upper() for pattern, cve_list in SSH_VULNERABILITIES.items(): if pattern.upper().replace("_", "-") in ver.replace("_", "-"): vulns.extend(cve_list) break # 通用检查 if "OpenSSH" in ver: try: # 提取版本号 import re match = re.search(r'OpenSSH[_\s](\d+)\.(\d+)', ver, re.IGNORECASE) if match: major = int(match.group(1)) minor = int(match.group(2)) if major < 7: vulns.append({ "vuln": "版本过旧", "severity": "HIGH", "desc": f"OpenSSH {major}.{minor} 版本过旧,存在多个已知漏洞,建议升级" }) elif major == 7 and minor < 4: vulns.append({ "vuln": "版本较旧", "severity": "MEDIUM", "desc": f"OpenSSH {major}.{minor} 版本较旧,建议升级到8.0+" }) # CVE-2024-6387 regreSSHion 影响 8.5p1-9.7p1 if (major == 8 and minor >= 5) or (major == 9 and minor <= 7): already_has = any(v.get("cve") == "CVE-2024-6387" for v in vulns) if not already_has: vulns.append({ "cve": "CVE-2024-6387", "severity": "CRITICAL", "desc": "regreSSHion - 远程无认证RCE (影响8.5p1-9.7p1)" }) except Exception: pass return vulns def match_service_vulnerabilities(open_ports): """根据开放端口匹配服务漏洞""" vulns = [] for port in open_ports: if port in SERVICE_VULNERABILITIES: for v in SERVICE_VULNERABILITIES[port]: v_copy = dict(v) v_copy["port"] = port v_copy["service"] = SCAN_PORTS.get(port, f"端口{port}") vulns.append(v_copy) return vulns # ===================================================== # 算力评估 & 业务字段 # ===================================================== def compute_matrix_fields(sysinfo, open_ports, vulns, verified, issues): """计算分布式算力矩阵业务相关字段""" fields = {} # ── 1. 算力评分 (0-100) ── score = 0 reasons = [] # CPU cores = sysinfo.get("cpu_cores", 0) if cores >= 8: score += 30 reasons.append(f"CPU{cores}核(+30)") elif cores >= 4: score += 20 reasons.append(f"CPU{cores}核(+20)") elif cores >= 2: score += 10 reasons.append(f"CPU{cores}核(+10)") elif cores >= 1: score += 5 reasons.append(f"CPU{cores}核(+5)") # RAM ram = sysinfo.get("ram_total_mb", 0) if ram >= 16384: score += 25 reasons.append(f"内存{ram}MB(+25)") elif ram >= 8192: score += 20 reasons.append(f"内存{ram}MB(+20)") elif ram >= 4096: score += 15 reasons.append(f"内存{ram}MB(+15)") elif ram >= 2048: score += 10 reasons.append(f"内存{ram}MB(+10)") elif ram >= 1024: score += 5 reasons.append(f"内存{ram}MB(+5)") # Disk disk = sysinfo.get("disk_free_gb", 0) if disk >= 100: score += 15 reasons.append(f"磁盘剩余{disk}GB(+15)") elif disk >= 50: score += 10 reasons.append(f"磁盘剩余{disk}GB(+10)") elif disk >= 20: score += 5 reasons.append(f"磁盘剩余{disk}GB(+5)") # Docker if sysinfo.get("docker_installed"): score += 10 reasons.append("Docker已安装(+10)") # Python if sysinfo.get("python_version"): score += 5 reasons.append("Python可用(+5)") # Uptime stability uptime = sysinfo.get("uptime_seconds", 0) if uptime > 86400 * 30: # 30天+ score += 10 reasons.append(f"运行{uptime//86400}天(+10)") elif uptime > 86400 * 7: # 7天+ score += 5 reasons.append(f"运行{uptime//86400}天(+5)") # Connection verified if verified: score += 5 reasons.append("连接已验证(+5)") fields["compute_score"] = min(score, 100) fields["compute_score_details"] = reasons # ── 2. 算力等级 ── if score >= 80: fields["compute_grade"] = "A" # 优质算力节点 elif score >= 60: fields["compute_grade"] = "B" # 可用算力节点 elif score >= 40: fields["compute_grade"] = "C" # 基础算力节点 elif score >= 20: fields["compute_grade"] = "D" # 低配节点 else: fields["compute_grade"] = "E" # 不推荐 # ── 3. 推荐节点角色 ── roles = [] if cores >= 4 and ram >= 4096: roles.append("计算节点") if disk >= 100: roles.append("存储节点") if cores >= 2 and ram >= 2048 and sysinfo.get("docker_installed"): roles.append("容器执行节点") if ram >= 2048: roles.append("代理转发节点") if cores >= 1 and ram >= 512: roles.append("轻量任务节点") if not roles: roles.append("待评估") fields["recommended_roles"] = roles # ── 4. 部署就绪度 ── deploy_ready = True deploy_blockers = [] if not verified: deploy_ready = False deploy_blockers.append("SSH连接不可用") shell_available = sysinfo.get("_shell_available", True) shell_type = sysinfo.get("_shell_type", "") if not shell_available: deploy_ready = False if shell_type == "honeypot_or_restricted": deploy_blockers.append("蜜罐或受限设备(Shell登录即断)") elif shell_type == "no_shell_session": deploy_blockers.append("不支持Shell会话(网络设备/仅转发)") else: deploy_blockers.append("Shell不可用") if ram > 0 and ram < 512: deploy_ready = False deploy_blockers.append("内存不足512MB") if disk > 0 and disk < 1: deploy_ready = False deploy_blockers.append("磁盘不足1GB") if any("认证失败" in i for i in issues): deploy_ready = False deploy_blockers.append("SSH密码已失效") fields["deploy_ready"] = deploy_ready fields["deploy_blockers"] = deploy_blockers # ── 5. 安全风险等级 ── critical_vulns = [v for v in vulns if v.get("severity") == "CRITICAL"] high_vulns = [v for v in vulns if v.get("severity") == "HIGH"] if critical_vulns: fields["security_risk"] = "CRITICAL" elif high_vulns: fields["security_risk"] = "HIGH" elif vulns: fields["security_risk"] = "MEDIUM" else: fields["security_risk"] = "LOW" fields["vulnerability_count"] = len(vulns) fields["critical_vuln_count"] = len(critical_vulns) # ── 6. 网络质量评估 ── # 根据开放端口推测网络环境 web_ports = [p for p in open_ports if p in (80, 443, 8080, 8443)] db_ports = [p for p in open_ports if p in (3306, 5432, 27017, 6379)] mgmt_ports = [p for p in open_ports if p in (8888, 3000, 9090)] if len(open_ports) > 15: fields["network_exposure"] = "HIGH" # 暴露端口多 elif len(open_ports) > 8: fields["network_exposure"] = "MEDIUM" else: fields["network_exposure"] = "LOW" # ── 7. 用途推测 ── use_hints = [] if web_ports: use_hints.append("Web服务器") if db_ports: use_hints.append("数据库服务器") if mgmt_ports: use_hints.append("管理面板") if sysinfo.get("docker_installed"): use_hints.append("容器化部署") if 3389 in open_ports or 5900 in open_ports: use_hints.append("远程桌面/可视化") if 21 in open_ports: use_hints.append("FTP文件服务") if 25 in open_ports: use_hints.append("邮件服务器") fields["inferred_usage"] = use_hints if use_hints else ["通用服务器"] # ── 8. 节点状态标签 ── if verified and deploy_ready and shell_available: fields["node_status"] = "ready" # 可立即部署 elif verified and shell_available and not deploy_ready: fields["node_status"] = "limited" # Shell可用但有其他限制 elif verified and not shell_available: if shell_type == "honeypot_or_restricted": fields["node_status"] = "honeypot" # 蜜罐/受限 elif shell_type == "no_shell_session": fields["node_status"] = "network_device" # 网络设备 else: fields["node_status"] = "shell_restricted" # Shell受限 else: fields["node_status"] = "unreachable" # 不可达 fields["shell_available"] = shell_available fields["shell_type"] = shell_type return fields # ===================================================== # 主处理逻辑 # ===================================================== async def enhance_single_ip(doc, dry_run=False): """增强单个IP的全部字段""" ip = doc["ip"] port = doc.get("port", 22) username = doc.get("username", "root") password = doc.get("password", "") print(f"\n{'='*60}") print(f" 处理: {ip}:{port} ({username})") print(f"{'='*60}") # 1. GeoIP 地理定位 print(f" [1/5] GeoIP地理定位...", end=" ", flush=True) geo = await get_geoip_info(ip) region_str = f"{geo['country']}|{geo['province']}|{geo['city']}" print(f"完成 → {region_str} ({geo['isp']})") # 2. 多端口扫描 print(f" [2/5] 端口扫描({len(SCAN_PORTS)}个端口)...", end=" ", flush=True) open_ports = await scan_all_ports(ip, timeout=3, concurrency=30) print(f"完成 → {len(open_ports)}个开放") for p, svc in sorted(open_ports.items()): print(f" 端口 {p:>5} → {svc}") # 3. Banner抓取(对开放端口) print(f" [3/5] Banner抓取...", end=" ", flush=True) banners = {} for p in open_ports: if p != port: # SSH端口已有版本信息 b = await grab_banner(ip, p, timeout=3) if b: banners[str(p)] = b[:150] print(f"完成 → {len(banners)}个有Banner") # 4. SSH连接验证 + 系统信息 print(f" [4/5] SSH连接验证+系统信息采集...", end=" ", flush=True) verified, sysinfo, issues = await verify_and_collect_sysinfo(ip, port, username, password) status_text = "连接成功" if verified else "连接失败" print(f"完成 → {status_text}") if verified: print(f" 主机名: {sysinfo['hostname']}") print(f" 系统: {sysinfo['os_release']}") print(f" 内核: {sysinfo['kernel']}") print(f" 架构: {sysinfo['arch']}") print(f" CPU: {sysinfo['cpu_model']} ({sysinfo['cpu_cores']}核)") print(f" 内存: {sysinfo['ram_total_mb']}MB (已用{sysinfo['ram_used_mb']}MB)") print(f" 磁盘: {sysinfo['disk_total_gb']}GB (剩余{sysinfo['disk_free_gb']}GB)") print(f" 运行: {sysinfo['uptime_human']}") print(f" Docker: {'已安装 ' + sysinfo['docker_version'] if sysinfo['docker_installed'] else '未安装'}") print(f" Python: {sysinfo['python_version'] or '未检测到'}") # 5. 漏洞匹配 print(f" [5/5] 漏洞分析...", end=" ", flush=True) ssh_vulns = match_ssh_vulnerabilities(doc.get("ssh_version", "")) svc_vulns = match_service_vulnerabilities(list(open_ports.keys())) all_vulns = ssh_vulns + svc_vulns print(f"完成 → {len(all_vulns)}个漏洞/风险") for v in all_vulns: severity = v.get("severity", "?") cve = v.get("cve", v.get("vuln", "未知")) desc = v.get("desc", "") color = "\033[91m" if severity == "CRITICAL" else "\033[93m" if severity == "HIGH" else "\033[94m" print(f" {color}[{severity}] {cve}: {desc}\033[0m") # 6. 算力评估 matrix_fields = compute_matrix_fields(sysinfo, list(open_ports.keys()), all_vulns, verified, issues) print(f"\n --- 算力评估 ---") print(f" 算力评分: {matrix_fields['compute_score']}/100 (等级: {matrix_fields['compute_grade']})") print(f" 推荐角色: {', '.join(matrix_fields['recommended_roles'])}") print(f" 部署就绪: {'是' if matrix_fields['deploy_ready'] else '否 - ' + ', '.join(matrix_fields['deploy_blockers'])}") print(f" 安全风险: {matrix_fields['security_risk']} ({matrix_fields['vulnerability_count']}个漏洞)") print(f" 网络暴露: {matrix_fields['network_exposure']} ({len(open_ports)}个开放端口)") print(f" 推测用途: {', '.join(matrix_fields['inferred_usage'])}") print(f" 节点状态: {matrix_fields['node_status']}") if issues: print(f"\n --- 问题诊断 ({len(issues)}项) ---") for i, issue in enumerate(issues, 1): print(f" {i}. {issue}") # 组装更新文档 update_doc = { # 地理信息 "geo": { "country": geo["country"], "country_code": geo["country_code"], "province": geo["province"], "city": geo["city"], "isp": geo["isp"], "org": geo["org"], "as_number": geo["as_number"], "as_name": geo["as_name"], "latitude": geo["latitude"], "longitude": geo["longitude"], "timezone": geo["timezone"], }, "region": region_str, # 连接验证 "connection_verified": verified, "last_verify_time": datetime.now(), # 系统信息 "sysinfo": { "hostname": sysinfo["hostname"], "kernel": sysinfo["kernel"], "arch": sysinfo["arch"], "os_release": sysinfo["os_release"], "cpu_model": sysinfo["cpu_model"], "cpu_cores": sysinfo["cpu_cores"], "cpu_threads": sysinfo["cpu_threads"], "cpu_freq_mhz": sysinfo["cpu_freq_mhz"], "ram_total_mb": sysinfo["ram_total_mb"], "ram_used_mb": sysinfo["ram_used_mb"], "ram_free_mb": sysinfo["ram_free_mb"], "swap_total_mb": sysinfo["swap_total_mb"], "swap_used_mb": sysinfo["swap_used_mb"], "disk_total_gb": sysinfo["disk_total_gb"], "disk_used_gb": sysinfo["disk_used_gb"], "disk_free_gb": sysinfo["disk_free_gb"], "uptime_seconds": sysinfo["uptime_seconds"], "uptime_human": sysinfo["uptime_human"], "load_avg": sysinfo["load_avg"], "network_interfaces": sysinfo["network_interfaces"], "public_ip_verified": sysinfo["public_ip_verified"], "docker_installed": sysinfo["docker_installed"], "docker_version": sysinfo["docker_version"], "python_version": sysinfo["python_version"], }, # 开放端口 "open_ports": {str(p): svc for p, svc in sorted(open_ports.items())}, "open_port_count": len(open_ports), "port_banners": banners, # 漏洞信息 "vulnerabilities": all_vulns, "vulnerability_count": len(all_vulns), "critical_vuln_count": matrix_fields["critical_vuln_count"], # 算力矩阵业务字段 "compute_score": matrix_fields["compute_score"], "compute_score_details": matrix_fields["compute_score_details"], "compute_grade": matrix_fields["compute_grade"], "recommended_roles": matrix_fields["recommended_roles"], "deploy_ready": matrix_fields["deploy_ready"], "deploy_blockers": matrix_fields["deploy_blockers"], "security_risk": matrix_fields["security_risk"], "network_exposure": matrix_fields["network_exposure"], "inferred_usage": matrix_fields["inferred_usage"], "node_status": matrix_fields["node_status"], "shell_available": matrix_fields.get("shell_available", False), "shell_type": matrix_fields.get("shell_type", ""), # 问题诊断 "issues": issues, "issue_count": len(issues), # 元数据 "enhance_time": datetime.now(), "enhance_version": "1.0", } if not dry_run: return update_doc else: return None async def main(): parser = argparse.ArgumentParser(description="已登录IP深度增强分析") parser.add_argument("--ip", help="仅处理指定IP") parser.add_argument("--dry-run", action="store_true", help="仅分析不写库") parser.add_argument("--verbose", "-v", action="store_true") args = parser.parse_args() logging.basicConfig( level=logging.DEBUG if args.verbose else logging.WARNING, format="%(asctime)s [%(levelname)s] %(message)s" ) import pymongo client = pymongo.MongoClient(MONGO_URI) db = client[MONGO_DB] coll = db[MONGO_COLLECTION] # 查询目标 query = {} if args.ip: query["ip"] = args.ip docs = list(coll.find(query)) print(f"\n{'#'*60}") print(f" 已登录IP深度增强分析 v1.0") print(f" 目标: {len(docs)} 个IP") print(f" 模式: {'试运行' if args.dry_run else '正式写库'}") print(f"{'#'*60}") if not docs: print("[!] 无目标IP") sys.exit(1) start_time = time.time() results = [] for doc in docs: update_doc = await enhance_single_ip(doc, dry_run=args.dry_run) if update_doc and not args.dry_run: coll.update_one( {"ip": doc["ip"]}, {"$set": update_doc} ) print(f"\n >>> MongoDB已更新: {doc['ip']}") results.append(doc["ip"]) elapsed = time.time() - start_time print(f"\n{'#'*60}") print(f" 分析完成!") print(f" 处理IP数: {len(docs)}") print(f" 已更新: {len(results)}") print(f" 耗时: {elapsed:.1f}秒") if results: print(f" 已更新的IP: {', '.join(results)}") print(f"{'#'*60}") # 打印最终数据结构摘要 if results: print(f"\n === 新增字段清单 ===") print(f" 地理信息: geo.country/province/city/isp/org/as_number/latitude/longitude") print(f" 区域简写: region (国家|省|市)") print(f" 连接验证: connection_verified, last_verify_time") print(f" 系统信息: sysinfo.hostname/kernel/os_release/cpu_*/ram_*/disk_*/uptime_*/docker_*/python_version") print(f" 开放端口: open_ports, open_port_count, port_banners") print(f" 漏洞信息: vulnerabilities[], vulnerability_count, critical_vuln_count") print(f" 算力评估: compute_score/compute_grade/recommended_roles") print(f" 部署状态: deploy_ready/deploy_blockers/node_status") print(f" 安全评估: security_risk/network_exposure") print(f" 用途推测: inferred_usage") print(f" 问题诊断: issues[], issue_count") print(f" 元数据: enhance_time, enhance_version") client.close() if __name__ == "__main__": asyncio.run(main())