适用场景
SNMP 加固主要针对三类资产,这三类在实际渗透中是最容易被忽略的暴露面:
- 云主机与自建物理服务器:安装 net-snmp 后默认监听 0.0.0.0:161/udp,安全组仅放行了常用端口时,161 往往被遗漏;
- 网络设备与安全设备:交换机、路由器、防火墙出厂默认启用 SNMPv1/v2c,团体字常保留为
public、private; - 监控体系:Zabbix、Prometheus snmp_exporter、PRTG 等平台在接入设备时通常沿用 v2c 团体字,导致全网设备长期使用同一明文凭据。
SNMP 加固的目标很明确:消除明文团体字、关闭不必要的版本、把 161/udp 收敛到可信源,同时保证监控平台仍能正常取数。
前置条件
- 目标主机安装 net-snmp(
snmpd),具备 root 权限; - 有可用的内网管理地址段或监控服务器固定 IP 列表;
- 防火墙使用 nftables 或 iptables,可在 INPUT 链上追加规则;
- 客户端侧安装 net-snmp-utils,用于执行
snmpwalk、snmpget验证; - 若使用 SNMPv3,需提前确定认证口令与加密口令(建议各 16 位以上随机串)。
原理说明
v1/v2c 的两个致命属性
- 凭据明文:团体字(community string)以明文封装在 UDP 报文中,链路上任意一跳都可抓取;攻击者拿到一次抓包即可长期复用,且社区词典对
public、private、设备型号名的命中率极高。 - 无来源校验:协议本身不校验来源 IP,只要网络可达且团体字正确,任何地址都能读取信息,因此暴露在公网的 161/udp 等同于匿名信息接口。
信息泄露面有多大
默认视图下,一次 snmpwalk 可读出的内容远超运维预期:
- 系统描述、主机名、运行时长、内核版本(
.1.3.6.1.2.1.1); - 完整网卡清单与 IP、掩码、MAC(
.1.3.6.1.2.1.2、.1.3.6.1.2.1.4.20); - 路由表与 ARP 表(
.1.3.6.1.2.1.4.21、.1.3.6.1.2.1.4.22); - 已安装软件包与进程列表(
.1.3.6.1.2.1.25.6、.1.3.6.1.2.1.25.4),可据此定位存在已知漏洞的版本; - TCP 监听端口与连接状态(
.1.3.6.1.2.1.6.13),相当于免费给出内网资产测绘结果。
反射放大风险
161/udp 无连接状态,攻击者可伪造源 IP 发送单条 GetBulk 请求,触发数十倍于请求体积的响应,形成 SNMP 反射放大。加固后若 161 仅对少量监控源开放,该风险随之消除。
操作步骤
步骤一:现状自查
# 1) 确认是否在监听以及监听范围
ss -lunp | grep -w 161
# 出现 0.0.0.0:161 或 [::]:161 即为全网开放
# 2) 确认版本与团体字配置
grep -Ev '^\s*#|^\s*$' /etc/snmp/snmpd.conf | grep -Ei 'community|agentaddress|createUser|rouser'
# 3) 从外部机器探测是否可达(用真实公网 IP 替换示例地址)
nmap -sU -p 161 -sV --script snmp-info 203.0.113.10
# 4) 快速验证默认团体字是否生效(应失败)
snmpget -v2c -c public 203.0.113.10 1.3.6.1.2.1.1.1.0 -t 2
如果第 4 步返回了系统描述,说明当前处于高危暴露状态,需立即执行后续步骤。
步骤二:重写 snmpd.conf
先备份原配置,再按最小视图原则重写。以下配置默认不提供任何 v1/v2c 只读权限,仅启用 SNMPv3。
cp /etc/snmp/snmpd.conf /etc/snmp/snmpd.conf.bak-$(date +%F)
cat > /etc/snmp/snmpd.conf <<'EOF'
# ---- 监听范围:只绑内网管理口,不监听 0.0.0.0 ----
agentaddress udp:10.0.0.12:161
# ---- 视图:仅暴露基础系统与网卡信息 ----
view sysonly included .1.3.6.1.2.1.1
view sysonly included .1.3.6.1.2.1.2
view sysonly included .1.3.6.1.2.1.4.20
view sysonly included .1.3.6.1.2.1.25.1
view sysonly included .1.3.6.1.2.1.31.1.1
# ---- 基础标识 ----
sysLocation "CN-IDC-A-Rack12"
sysContact "ops@example.com"
sysServices 72
# ---- v3 只读用户:SHA-256 认证 + AES 加密 ----
rouser monuser authpriv -V sysonly
# ---- 关闭写入与陷阱,减少攻击面 ----
# (不配置 rwuser / rwcommunity / trapsink 即为关闭)
# ---- 不加载扩展代理与主机资源限制之外的模块 ----
EOF
关键点说明:
- agentaddress 由
udp:161改为绑定内网 IP,从网络层消除公网暴露; - view sysonly 把可读 OID 收敛到 5 个子树,进程列表、软件包列表、路由表均不在其中;
- 配置中完全没有
rocommunity与rwcommunity,即 v1/v2c 探测一律拒绝。
步骤三:创建 SNMPv3 用户
不要手写 createUser 到配置文件(重启后可能失效)。使用发行版自带工具创建,它会自动把用户持久化到 /var/lib/snmp/snmpd.conf(部分发行版为 /var/lib/net-snmp/snmpd.conf)。
systemctl stop snmpd
# Debian / Ubuntu
/usr/sbin/net-snmp-create-v3-user -ro \
-a SHA-256 -A 'Auth-Pass-9x2Q7m4Z' \
-x AES -X 'Priv-Pass-3k8N2p6R' \
monuser
# RHEL / CentOS 若命令不存在,手工写入持久化文件
# echo 'createUser monuser SHA-256 "Auth-Pass-9x2Q7m4Z" AES "Priv-Pass-3k8N2p6R"' \
# >> /var/lib/net-snmp/snmpd.conf
systemctl start snmpd
systemctl enable snmpd
口令要求:认证口令与加密口令必须不同,长度 16 位以上,包含大小写字母与数字,禁止使用设备名或域名作为口令来源。
步骤四:systemd 服务加固
mkdir -p /etc/systemd/system/snmpd.service.d
cat > /etc/systemd/system/snmpd.service.d/hardening.conf <<'EOF'
[Service]
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID CAP_DAC_READ_SEARCH
EOF
systemctl daemon-reload
systemctl restart snmpd
systemctl status snmpd --no-pager
注意:ProtectSystem=strict 会使文件系统只读,若 snmpd 需要写持久化用户文件必须保留对应 ReadWritePaths。首次加固后请立即执行步骤五的验证,确认服务未因权限收紧而无法启动。若使用了自定义扩展脚本,需按需增加 CAP_SYS_PTRACE 等能力,不要直接放弃整个加固项。
步骤五:防火墙限制来源
# nftables 方案
cat > /etc/nftables.d/snmp.nft <<'EOF'
table inet snmpguard {
set snmp_src {
type ipv4_addr
elements = { 10.0.0.21, 10.0.0.22 }
}
chain input {
type filter hook input priority -5; policy accept;
ip saddr @snmp_src udp dport 161 ct state new limit rate 20/second accept
udp dport 161 drop
ip6 saddr ::/0 udp dport 161 drop
}
}
EOF
nft -f /etc/nftables.d/snmp.nft
# 若仍在使用 iptables
iptables -A INPUT -p udp --dport 161 -s 10.0.0.21 -m limit --limit 20/s -j ACCEPT
iptables -A INPUT -p udp --dport 161 -s 10.0.0.22 -m limit --limit 20/s -j ACCEPT
iptables -A INPUT -p udp --dport 161 -j DROP
ip6tables -A INPUT -p udp --dport 161 -j DROP
同时清理云平台安全组中 161/udp 的 0.0.0.0/0 放行项,这是公网暴露的常见根因。
步骤六:确无需求时直接停用
systemctl disable --now snmpd
apt purge -y snmpd snmp # 或 yum remove -y net-snmp
若主机不使用 SNMP 监控,卸载是最彻底的加固方式。优先顺序为:不需要就卸载 > 需要则只开 v3 且限源 > 保底才用 v2c 强团体字加 ACL。
配置验证
第一步,验证 v3 可正常取数(视图内 OID 应返回结果):
snmpwalk -v3 -l authPriv -u monuser -a SHA-256 -A 'Auth-Pass-9x2Q7m4Z' \
-x AES -X 'Priv-Pass-3k8N2p6R' 10.0.0.12 1.3.6.1.2.1.1
snmpget -v3 -l authPriv -u monuser -a SHA-256 -A 'Auth-Pass-9x2Q7m4Z' \
-x AES -X 'Priv-Pass-3k8N2p6R' 10.0.0.12 1.3.6.1.2.1.1.3.0
第二步,验证视图外 OID 被拒绝(应返回 No Such Object 或 noSuchInstance):
snmpwalk -v3 -l authPriv -u monuser -a SHA-256 -A 'Auth-Pass-9x2Q7m4Z' \
-x AES -X 'Priv-Pass-3k8N2p6R' 10.0.0.12 1.3.6.1.2.1.25.4
第三步,验证 v1/v2c 已彻底关闭(三条命令都应超时或返回 authorizationError):
snmpget -v2c -c public 10.0.0.12 1.3.6.1.2.1.1.1.0 -t 2
snmpget -v2c -c private 10.0.0.12 1.3.6.1.2.1.1.1.0 -t 2
snmpget -v1 -c public 10.0.0.12 1.3.6.1.2.1.1.1.0 -t 2
第四步,从外部网络确认 161 已不可达:
nmap -sU -p 161 --script snmp-brute 203.0.113.10
# 预期:161/udp open|filtered 或 closed,且 snmp-brute 无任何命中
第五步,验证非授权内网地址被防火墙丢弃并记录:
# 在非白名单主机上发起
snmpget -v2c -c public 10.0.0.12 1.3.6.1.2.1.1.1.0 -t 2
# 在目标主机上确认命中 drop 规则
nft list ruleset | grep -A6 snmpguard
dmesg | tail -5
常见问题
FAQ 1:切到 SNMPv3 后,Zabbix / snmp_exporter 取不到数据了,怎么排查?
按以下顺序定位,可覆盖绝大多数失败场景:
- 认证级别参数:Zabbix 中「安全级别」必须与用户配置一致,使用 authPriv 时需同时填写认证协议(SHA-256)与加密协议(AES),任一项留空都会握手失败;
- 引擎 ID(engineID):SNMPv3 的凭据绑定设备引擎 ID,若目标主机重装或 snmpd 数据目录被清空,引擎 ID 会变化,需在模板中删除对应 discovery 条目重新发现;
- 视图限制:若监控模板需要读取
.1.3.6.1.2.1.25.4(进程)或.1.3.6.1.2.1.31(网卡扩展),需把这些子树加入view;返回noSuchObject通常不是权限问题而是视图未包含; - 监听地址:
agentaddress改为内网 IP 后,监控端必须指向该 IP,指向其他网卡地址会直接超时。
FAQ 2:怎么确认 161 没有被暴露到公网?
三个维度交叉确认:
- 监听维度:
ss -lunp | grep -w 161输出应只有内网 IP,若出现0.0.0.0或::说明监听范围仍过大; - 主机防火墙维度:
nft list ruleset或iptables -L INPUT -n --line-numbers确认 161 的 DROP 规则排在放行之前; - 外部探测维度:用一台公网主机执行
nmap -sU -p 161 目标公网IP,结果为open|filtered或closed才是安全的,出现open说明仍有路径可达,需检查云安全组与上游网络 ACL。
FAQ 3:业务必须保留 v2c(老旧设备不支持 v3),最小风险配置是什么?
把团体字长度提到 20 位以上并随机生成,同时叠加三重约束:一是用 rocommunity 后跟允许来源网段,只读且限定网段;二是用 -V sysonly 绑定最小视图;三是在防火墙侧只放行监控服务器单一 IP 并对 161 做速率限制。示例:
rocommunity 9f2a1c7e5d3b8064 10.0.0.0/24 -V sysonly
即使如此,团体字在链路上仍是明文,因此建议同时规划设备替换或增加 SNMP 代理(proxy)把 v2c 收敛到内网一跳范围内。
总结
- 先关闭不需要的:不使用 SNMP 的主机直接卸载 snmpd,一步消除风险;
- 版本收敛到 v3:认证用 SHA-256、加密用 AES,认证口令与加密口令必须不同且长度 16 位以上;
- 视图最小化:只暴露
.1.3.6.1.2.1.1/2/4.20/25.1/31.1.1等必要子树,把进程、软件包、路由表移出可读范围; - 网络面收敛:
agentaddress绑定内网 IP,防火墙只放行监控源并对 161 限速,同步清理云安全组的全网放行项; - 可验证:从监听、防火墙、外部探测三个维度验证,并用
snmpwalk确认视图内外 OID 的返回差异。
整套改造在一台主机上通常 15 分钟内可完成,且对监控平台只涉及一次凭据配置调整,是性价比很高的加固项,建议纳入服务器安全基线与等保测评前的自查清单。