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 服务器(域控候选),通过节点连接密度可快速定位内网核心区域。