2. 网络拓扑探测与关键节点识别

任务目标

在红队内网渗透初期,快速绘制目标网络拓扑并定位关键节点(核心交换机、域控服务器)是后续攻击路径规划的基础。本案例将使用 traceroute 进行路径发现,结合 SNMP 协议探测设备类型与系统信息,最终汇总数据生成可视化拓扑图。

环境依赖

  • Kali Linux 或任意 Linux 发行版
  • Python 3.8+,需安装 python-nmap、python-snmp(或 easysnmp)、matplotlib、networkx
  • 目标网络允许 ICMP 和 UDP 161 端口通信(演练环境已授权)

核心实现

以下脚本完成三个核心动作:① 对目标网段执行 traceroute 路径追踪;② 对存活主机进行 SNMP 系统信息采集(sysDescr、sysName、sysLocation);③ 基于 SNMP 的 sysObjectID 判断设备类型(交换机/路由器/服务器),并输出拓扑节点关系。

#!/usr/bin/env python3
import subprocess
import ipaddress
from snmp_helper import snmp_get_oid, snmp_get_oid_single
import networkx as nx
import matplotlib.pyplot as plt

# ========== 配置 ==========
TARGET_NETWORK = "192.168.10.0/24"   # 演练目标网段
SNMP_COMMUNITY = "public"            # 演练环境只读团体字
TIMEOUT = 2
RETRIES = 1

# ========== 1. Traceroute 路径发现 ==========
def traceroute_path(dest_ip):
    """返回从本机到目标IP经过的跳数列表"""
    try:
        result = subprocess.run(
            ["traceroute", "-n", "-m", "15", "-w", "1", dest_ip],
            capture_output=True, text=True, timeout=30
        )
        hops = []
        for line in result.stdout.splitlines()[1:]:
            parts = line.split()
            if len(parts) >= 2 and parts[1] != "*":
                hops.append(parts[1])
        return hops
    except Exception:
        return []

# ========== 2. SNMP 设备探测 ==========
def snmp_probe(ip):
    """采集设备系统信息,返回设备类型与关键标识"""
    oids = {
        "sysName": "1.3.6.1.2.1.1.5.0",
        "sysDescr": "1.3.6.1.2.1.1.1.0",
        "sysObjectID": "1.3.6.1.2.1.1.2.0",
        "sysLocation": "1.3.6.1.2.1.1.6.0"
    }
    info = {"ip": ip}
    for key, oid in oids.items():
        try:
            value = snmp_get_oid_single(ip, community=SNMP_COMMUNITY, oid=oid,
                                        timeout=TIMEOUT, retries=RETRIES)
            info[key] = value.pretty_print() if value else ""
        except Exception:
            info[key] = ""
    # 根据 sysObjectID 判断设备类型
    obj_id = info.get("sysObjectID", "")
    if "9.1" in obj_id:      # Cisco 交换机/路由器
        info["type"] = "switch" if "Switch" in info.get("sysDescr", "") else "router"
    elif "1.3.6.1.4.1.311" in obj_id:  # Microsoft
        info["type"] = "windows_server"
    elif "1.3.6.1.4.1.8072" in obj_id: # Linux net-snmp
        info["type"] = "linux_server"
    else:
        info["type"] = "unknown"
    return info

# ========== 3. 拓扑构建与可视化 ==========
def build_topology(ip_list):
    """根据 traceroute 路径和 SNMP 信息构建拓扑图"""
    G = nx.Graph()
    # 添加所有探测到的节点
    for ip in ip_list:
        info = snmp_probe(ip)
        if info.get("sysName"):
            label = f"{info['sysName']}\n{ip}\n[{info['type']}]"
        else:
            label = f"{ip}\n[unknown]"
        G.add_node(ip, label=label, type=info["type"])

    # 根据 traceroute 路径添加边(相邻跳数之间)
    for ip in ip_list:
        hops = traceroute_path(ip)
        if len(hops) < 2:
            continue
        # 将本机作为起点
        prev = "127.0.0.1"
        for hop in hops:
            if hop in ip_list or hop == ip:  # 只保留目标网段内的路径
                G.add_edge(prev, hop)
                prev = hop
            elif prev != "127.0.0.1":
                prev = hop  # 跨网段跳数不建边,但更新prev

    return G

def draw_topology(G):
    """绘制拓扑图并保存为PNG"""
    pos = nx.spring_layout(G, k=0.8, iterations=50)
    plt.figure(figsize=(14, 10))
    labels = nx.get_node_attributes(G, "label")
    node_colors = []
    for node in G.nodes(data=True):
        t = node[1].get("type", "unknown")
        if t == "switch":
            node_colors.append("#e74c3c")   # 红色-交换机
        elif t == "windows_server":
            node_colors.append("#3498db")   # 蓝色-域控/Windows
        elif t == "router":
            node_colors.append("#f39c12")   # 橙色-路由器
        elif t == "linux_server":
            node_colors.append("#2ecc71")   # 绿色-Linux
        else:
            node_colors.append("#95a5a6")   # 灰色-未知

    nx.draw(G, pos, labels=labels, node_color=node_colors,
            node_size=2500, font_size=8, font_color="white",
            edge_color="#34495e", width=1.5, alpha=0.9)
    plt.title("Internal Network Topology - Key Nodes Highlighted")
    plt.tight_layout()
    plt.savefig("topology_map.png", dpi=150)
    print("[+] 拓扑图已保存: topology_map.png")

# ========== 主流程 ==========
if __name__ == "__main__":
    # 1. 扫描存活主机(使用nmap快速探测)
    import nmap
    nm = nmap.PortScanner()
    nm.scan(hosts=TARGET_NETWORK, arguments="-sn -T4")
    live_hosts = [host for host in nm.all_hosts() if nm[host].state() == "up"]
    print(f"[*] 发现存活主机: {len(live_hosts)} 台")

    # 2. 对每台主机执行SNMP探测(仅保留有响应的设备)
    snmp_devices = []
    for ip in live_hosts:
        info = snmp_probe(ip)
        if info.get("sysName") or info.get("sysDescr"):
            snmp_devices.append(ip)
            print(f"[+] SNMP响应: {ip} -> {info.get('sysName', 'N/A')} ({info['type']})")

    if not snmp_devices:
        print("[-] 未发现SNMP设备,请检查团体字或网络权限")
        exit(1)

    # 3. 构建拓扑
    G = build_topology(snmp_devices)
    print(f"[*] 拓扑节点数: {G.number_of_nodes()}, 边数: {G.number_of_edges()}")

    # 4. 识别关键节点(度中心性最高的节点)
    degree_centrality = nx.degree_centrality(G)
    key_nodes = sorted(degree_centrality.items(), key=lambda x: x[1], reverse=True)[:3]
    print("\n[+] 关键节点识别(按连接数排序):")
    for node, score in key_nodes:
        node_info = G.nodes[node]
        print(f"    - {node_info.get('label', node)} (中心性: {score:.3f})")

    # 5. 可视化
    draw_topology(G)

运行方式

# 安装依赖
pip install python-nmap easysnmp networkx matplotlib

# 运行脚本(需root权限用于traceroute)
sudo python3 topology_probe.py

验证方法与判定标准

  • 存活探测验证:脚本输出“发现存活主机”数量应与 nmap -sn 手动扫描结果一致。
  • SNMP 响应验证:至少发现 1 台设备返回 sysName 或 sysDescr,且设备类型判断合理(如 Windows 主机显示 windows_server)。
  • 拓扑图验证:打开生成的 topology_map.png,应看到节点间存在连接边,且关键节点(红色交换机或蓝色域控)位于拓扑中心位置,与 degree_centrality 输出一致。
  • 关键节点判定:若演练环境包含域控(通常为 Windows Server),该节点应出现在“关键节点识别”列表中,且其连接数高于普通终端。

常见问题

  • SNMP 无响应:确认目标设备已开启 SNMP 服务且团体字正确;部分设备默认禁用 UDP 161,需在演练前配置。
  • traceroute 超时:内网设备可能过滤 ICMP,可改用 -T -p 80(TCP SYN)或 -U -p 53(UDP DNS)进行探测。
  • 拓扑图节点过少:若只有少量 SNMP 设备,可结合 nmap 的 -O 系统指纹结果补充非 SNMP 节点,但需手动标注类型。

本案例输出 topology_map.png 即为最终交付物,图中红色节点为交换机、蓝色为 Windows 服务器(域控候选),通过节点连接密度可快速定位内网核心区域。