公网非标端口裸暴露被扫描收录与转卖 (Exposed Non-Standard Ports, Scanner Indexing and Resale)
1. 一句话
服务绑在 0.0.0.0(所有网卡)且主机防火墙没开时,这个端口会绕过 nginx、WAF、路径封禁等一切 web 层防护,直接暴露给全网扫描器。非标端口被扫不是偶发事件,而是每天持续的常态;一旦某次扫描被你注意到,通常是有人买走了你这条记录、在做批量攻击。
2. 现象与数据
本例(自建 AI 中转的 CPA,端口 8317)连续 22 天的 401 敲门次数:
Sep 03 2098 Sep 11 1629 Sep 19 266
Sep 04 2063 Sep 12 509 Sep 20 85253 ← 爆发
Sep 05 538 Sep 13 1034 Sep 21 237
Sep 06 232 Sep 14 591 Sep 22 215
Sep 07 666 Sep 15 319 Sep 23 218
Sep 08 2039 Sep 16 277 Sep 24 186
Sep 09 853 Sep 17 366
Sep 10 1096 Sep 18 446读法要点:
- 每天几百到两千次是基线。这种量级在 CPU 上完全看不出来(占不到 1%),所以长期无人察觉。
- 爆发日不是"刚开始被扫",是"突然被集中打"。本例 Sep 20 从 266 涨到 85253,涨了 320 倍。
- 爆发日的形态:
85012次请求 →85005个不同来源 IP → 其中84998个只出现一次。这是代理池 / 僵尸网络的指纹,不是单机脚本。 - 按小时看是三个台阶:13 点 15173 次,14 点 47261 次,15 点 22578 次。16 点后停。典型的租赁带宽按时段跑任务。
3. 为什么会被扫(灰产分工)
本节是解释性认识,来自机主转述与公开认知,不是本机日志能证明的部分;第 2 节的数据是本机实测。
公网 IP 是被无差别扫的。扫描器不问你是谁,全网段扫,看谁有响应。一旦某个 IP:端口 回应了,它就被写进数据库。
之后是分工:
| 环节 | 干什么 | 成本 |
|---|---|---|
| 扫描器 | 全网扫端口、探服务指纹、抄公开路由 | 极低,长期挂机 |
| 数据库 / 中间商 | 把 IP:端口:服务:路径 整理成表,卖给下游 | 近零边际成本 |
| 下游 | 买表后做批量打(撞密钥、刷注册、薅额度) | 按量租代理池 |
| 勒索方 | 打不动或打得动,都发信要"保护费"(常见 50U–100U) | 带宽成本高,故需变现 |
关键推论:你没有"暴露自己的 IP"这个动作。只要端口开着,收录是自动发生的。所以防护的重点不是藏 IP,而是别让端口有响应。
另一条:用开源组件时,你的攻击面是公开的。本例被扫的 /v0/management/api-keys 来自 CPA 控制面板的开源仓库——扫描器把源码里的路由全抄成清单,全网比对。配置里那行 panel-github-repository 就是线索。
4. 成因:一个二选一的组合
非标端口要能被打到,需要同时满足两条:
应用绑
0.0.0.0(或::、:port)。很多服务默认就是这样:- new-api:
srv := &http.Server{Addr: ":" + port, ...} - CPA:配置文件
host: "0.0.0.0"
- new-api:
- 主机防火墙没放行也没拒绝——即 ufw / firewalld 处于
inactive,或云安全组默认全开。
缺任何一条都打不到。本例两台机器的对照极干净:
| 应用绑定 | ufw 状态 | 8317 结果 | |
|---|---|---|---|
tx(腾讯云) | 0.0.0.0:8317 | inactive | 公网可直连 |
la(海外) | 0.0.0.0:8317 | active,默认 deny | 公网不可达 |
同款应用配置,只差防火墙一栏。
5. 排查(可复用,四步)
① 找出所有绑在非回环地址的监听
ss -tlnp | grep -v "127.0.0.1\|\[::1\]"输出里带 0.0.0.0: / *: 的,就是"应用愿意被外面访问"。注意 127.0.0.1: 开头的不是问题——那是本机服务,必须靠反代或隧道才能出去。
② 看防火墙默认策略,而不是只看放行列表
sudo ufw status verbose # 必须看 "Default: deny (incoming)" 这行
sudo iptables -L INPUT -n | head -3 # 策略应是 DROP,且第一条要能挂到 ufw 链只看到放行列表里没有这个端口是不够的——如果默认策略是 ACCEPT,没写 ≠ 拒绝。
③ 直接查 iptables 里有没有这个端口
sudo iptables-save | grep -c "8317" # 0 = 一条都没提,默认策略说了算④ 从干净网络实测(关键)
结论必须来自网络实测,不能只信配置。 且测试机必须没有开着 TUN 类代理:
# 必须先做对照:测一个确定没服务的端口
for p in 9999 8317 443; do
printf '%-5s : ' $p
timeout 8 bash -c "cat < /dev/null > /dev/tcp/<目标IP>/$p" 2>/dev/null && echo 通 || echo 不通
done若连 9999 都报"通",说明本机代理劫持了 TCP,全部结论作废。详见 new-api 批量注册刷号排查与处置 (Investigating Bulk Fake Registrations) 第 5 坑。
跨 GFW 场景下直连测 443 也可能不通(链路被挡,非 ufw),此时改用从第三方机器走 SOCKS 隧道做真实连接:
curl -s -m 15 --socks5-hostname 127.0.0.1:7890 -o /dev/null -w "HTTP %{http_code}\n" http://<目标IP>:8317/判读:443 回 302 而 8317 连接失败 → 链路正常,是 ufw 在挡。
6. 修法(按力度排序)
| 力度 | 做法 | 说明 |
|---|---|---|
| 最强 | 应用改绑 127.0.0.1,用 nginx 反代出去 | 结构性免疫。端口物理上不被外部看到,防火墙被误关也不怕 |
| 强 | ufw default deny incoming + 只放行 80/443/SSH | 主机层兜底,配置简单 |
| 中 | 云安全组只开必要端口 | 与主机防火墙叠加;但别把它当唯一防线 |
| 弱 | 改应用自带的 allow-remote / 加管理密钥 | 挡得住"进门",挡不住"敲门"(见下节) |
| 无效 | 只靠应用自带鉴权 | 见下节 |
改绑 127.0.0.1 前必须先确认一件事:有没有外部客户端直连这个端口。有的话改绑会把它们全断。查法:
journalctl -u <服务名> --since "7 days ago" | grep -v " 401 " | \
awk -F'|' '{gsub(/ /,"",$3); print $3}' | sort | uniq -c | sort -rn | head只看到 127.0.0.1 就说明全走反代,可以放心改绑。
7. 坑
① 自带鉴权 ≠ 防得住:这打的是 CPU,不是凭据
本例 CPA 确实有管理密钥,remote-management.secret 有值配置正确。85012 次敲门全部返回 401,历史上一共 0 次成功——密钥一次都没被猜中。
但 4 个核仍然被占满 100% 接近 2 小时。
为什么:鉴权只防止"东西被拿走",不防止"资源被打光"。海量无效请求本身就是一种 DoS。
别误判成"算密钥算得慢":本机实测该接口 401 耗时 0.6ms(外面看到的 71ms 是网络 RTT,攻击时的 260ms 是 100% CPU 下的排队延迟)。所以瓶颈是并发排队,不是密码学开销。
推论:"反正有鉴权" 不能作为端口继续裸暴露的理由。
② 绑 0.0.0.0 会让所有 web 层防护失效
nginx 的 IP 黑名单、UA 风控、路径封禁、限流——全部不生效,因为流量根本没经过 nginx。这条与 new-api 批量注册刷号排查与处置 (Investigating Bulk Fake Registrations) 第 4 坑同源,那篇讲的是注册场景,本篇讲的是扫描场景。
③ 本机开着 TUN 时,端口测试全是假阳性
见第 5 节 ④。加上对照端口是最省事的判据。
④ ufw status 的放行列表会骗人
只看列表里"没有 8317"就下结论是错的。要配合 Default: 那行和 iptables-save 一起看。另外宝塔面板等工具有自己的防火墙入口,可能绕过 ufw 直接改 iptables。
8. 验证
改完后按第 5 节 ①②③④ 各跑一遍,必须包含一个已知关闭端口的对照,且必须来自无 TUN 的干净网络。
同时确认纵向对比:改前 8317 通、改后不通,而 443 改前改后都通(排除"是网络挂了不是配置生效")。
9. 适用边界
- 非标端口(本例 8317)是重灾区:80/443 通常有 nginx 挡在前面,非标端口往往直接就是应用本体。
- 本节结论适用于任何绑
0.0.0.0的自建服务:new-api、CPA、Qdrant、MySQL(*:3306)、面板工具。 - "扫描量基线"的绝对值随 IP 归属、端口、服务指纹变化,量级关系(基线几百/天,爆发可达十万/天)可迁移,具体数字不可迁移。
- 云厂商的 NAT 型 VPS 上,端口是否公网可达还取决于服务商 NAT 面板的映射,主机层面看不出来,需人工确认。
10. 本次实例(tx / la,2026-09-24 排查)
- 发现路径:查 tx 的 CPU 历史(
sar,保留 7 天)时发现 Sep 20 有 4 核 100% 持续约 100 分钟,进而追到 cli-proxy-api 日志。 - 攻击:Sep 20 13:11–16:00,
/v0/management/api-keys(CPA 的"列出所有 API 密钥"接口)被敲 85012 次,来自 85005 个不同 IP,84998 个只出现一次。全部 401,0 次得手。 - 入口:tx 的 CPA 配置
host: "0.0.0.0"+remote-management.allow-remote: true,而 tx 的 ufw 是inactive。直接IP:8317可达,完全绕过 nginx(nginx 日志里 0 条相关记录)。 - 对照:la 上同名配置、同端口,但 ufw active + 默认 deny,8317 无任何放行规则 → 从 tx 走 SOCKS 隧道实测
443通(302)而8317不通。 - 误判纠正:初次用本机测报"8317 暴露",实为 Clash TUN 假阳性(连 9998/9999 也报"通")。关掉 TUN 后重测:tx 8317 真通,la 8317 不通,对照组 9999 正常不通。
- 加扫发现:la 上 CPA 历史 401 共 96 次,来源是
127.0.0.1(自己的面板走反代)与一个本人在 tx 上成功调用过的国内 IP,无第三方入侵痕迹。 - 决策:tx 即将下线,不改;la 已由 ufw 兜住,待评估是否再把 CPA 改绑
127.0.0.1做纵深防御。
11. 关联
- 爬虫与扫描器的边界 (Crawler vs Scanner Boundary) —— HTTP 层(80/443)的同类噪音识别;本篇是端口层。
- new-api 批量注册刷号排查与处置 (Investigating Bulk Fake Registrations) —— 同一台机器上的下游变现手法(刷注册薅额度),以及 TUN 假阳性坑的详细写法。
- Nginx 全量信任 X-Forwarded-For 使访问日志来源 IP 可伪造 (Blanket set_real_ip_from Makes Access-Log IPs Forgeable) —— 排查来源 IP 时的日志可信度前提。
- fail2ban 与 btmp 暴力破解追踪 (fail2ban and btmp Brute-Force Tracking) —— SSH 层的同款"高频无效尝试"识别。
- IDC 代理分销链路辨识 (IDC Reseller Chain Identification) —— 上游供给侧的链路辨识。
- HTTPS 协议、SSH 协议
本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Network