端口暴露自查镜 —— 你的端口在「本机视角」和「外部视角」是两张不一样的图。porteye 把两张图摆在一起,差集就是问题。
零依赖单文件 CLI,Python 3 标准库,拷走就能跑。macOS / Linux / Windows 通吃。
- 防火墙挡住了你以为开着的端口 → 本机 netstat 显示监听 ≠ 外部可达,安全自查最经典的错觉
- 新起一个服务绑了
0.0.0.0→ 没人知道暴露面变了,diff 基线一跑就响(退出码 0/2,直接接 cron/CI) nmap只给外面视角、netstat只给本机视角 → expose 子命令做差集:幻影端口(挡住了)、可见端口(真暴露)、外部端口(NAT/容器映射进来的)三分类- 扫描脚本随手写越界端口 → fail-loud 拒绝,
0和65536直接报错不静默跳过
python3 porteye.py local # 本机监听全景(含进程名)
python3 porteye.py target <host> # 外部视角:扫 TOP103 端口
python3 porteye.py expose <本机内网IP> # 招牌命令:双视角差集报告
python3 porteye.py baseline # 保存当前暴露面为基线
python3 porteye.py diff 基线.json # 对比,新增暴露退出码 2
| 你想 | 你跑 |
|---|---|
| 看本机有哪些端口在听、谁在听 | local(wildcard 端口带 ! 标记) |
| 检查服务器从外面能看到什么 | target <host>(或 --ports 80,443,8000-8100) |
| 我这些监听端口外面到底能不能打通 | expose <本机内网IP> |
| 以后暴露面一变我就要知道 | baseline 存基线 → cron 挂 diff,退出码 2=变了 |
| 全量扫描 65535 个端口 | 加 --full(约几分钟,256 并发) |
== porteye expose report ==
local listening : 37 ports (wildcard 16)
remote visible : 0 ports
[VISIBLE] 外部可达的本地服务 ← 真暴露面,攻击者眼里只有这些
[PHANTOM] 本机在听, 外部不可达 ← 幻影端口:防火墙/安全组/绑定策略挡住了
[OUTSIDE] 远端开放但本机未见 ← NAT/容器端口映射进来的,注意背后是谁
一句话:对着 netstat 自我感觉良好,是暴露面管理的第一大错觉。 以 VISIBLE 为准。
- fail-loud:端口规格越界直接 ValueError,不静默跳过——漏扫一个端口比报错更危险
- 探前必并:expose 自动把本机 wildcard 监听并进探测清单,幻影无从漏报
- 退出码即接口:diff 退出码 0(无变化)/ 2(有变化),shell/cron/CI 直接用,
--json出机读格式 - 只读探测:全程只发 TCP connect 和读系统表,不改任何配置
- 四条不变量:INV1 幻影⊆本机监听 / INV2 diff 双向互斥完备 / INV3 端口域 [1,65535] / INV4
--json输出可解析且含固定键——tests/test_porteye.py17 项自测逐条验证
| 平台 | 来源 |
|---|---|
| macOS | lsof -nP -iTCP -sTCP:LISTEN(带进程名),netstat 兜底 |
| Linux | 直接读 /proc/net/tcp{,6},不需要 netstat 二进制 |
| Windows | netstat -ano -p tcp |
python3 tests/test_porteye.py
# TOTAL 17 PASS 17 FAIL 0 (含真实端口起服→探测→diff 检出的全链路实测)
expose 的「外部视角」需要一个另一台机器的位置来探测——在同机回环上跑,防火墙视角是缺席的。跨机跑(同网段另一台机器/跳板机)才是它的完整形态。TCP 全连接扫描对IDS明显的目标会留日志,生产环境先打报告再动手。
MIT