sma11sCan:基于 Go 的高并发端口扫描与资产探测工具开发记录

最近用 Go 写了一个安全工具项目:sma11sCan

这是一个偏命令行风格的资产探测工具,第一版主要聚焦在端口扫描、Banner 抓取、HTTP 探测、Web 指纹识别、Favicon 识别、服务识别、子域名收集、CDN 检测以及源站 IP 找回这些能力上。

这一版还没有接入 Gin,也没有做 Web API、任务队列、MySQL 入库、Redis 缓存这些后端平台化能力。它更像是一个单机 CLI 工具:用户通过命令行传入目标,程序完成扫描、探测、识别并在终端输出结果。

项目地址:

1
https://github.com/shen060606/sma11sCan

一、为什么写这个工具

一开始写这个项目的原因很简单:想把平时学到的一些安全开发能力串起来。

单独写一个端口扫描器并不难,单独写一个 HTTP Title 获取器也不难,单独做 CNAME 判断 CDN 也不复杂。但是如果把这些能力组合起来,就会涉及到很多工程问题:

比如:

1
2
3
4
5
6
7
1. 如何控制扫描并发,避免 goroutine 无限增长?
2. 如何处理域名、IP、CIDR 网段三种不同输入?
3. 域名使用 CDN 时,怎么判断 CDN 厂商?
4. 如果目标在 CDN 后面,能不能尝试找回源站 IP?
5. HTTP 探测时,如果 TCP 连的是 IP,TLS 握手怎么保证 SNI 是原域名?
6. Banner、HTTP Header、Title、Favicon 如何结合起来做服务识别?
7. 子域名收集后,如何联动端口扫描?

所以 sma11sCan 的目标不是只写一个简单端口扫描 demo,而是尝试做成一个较完整的资产探测工具。


二、项目整体能力

目前第一版主要支持这些功能:

1
2
3
4
5
6
7
8
9
10
11
端口扫描
Banner 抓取
HTTP/HTTPS 探测
Web 指纹识别
Favicon 指纹识别
服务识别
子域名收集
CDN 检测
源站 IP 找回
CIDR 网段扫描
域名自动解析

命令行使用方式大概是:

1
go run ./cmd/sma11scan/ -ip 192.168.1.1 -module fast

如果要开启 Banner 和指纹识别:

1
go run ./cmd/sma11scan/ -ip 192.168.1.1 -module top -banner

如果输入的是域名,程序会先尝试解析域名,同时进行 CDN 检测:

1
go run ./cmd/sma11scan/ -ip www.example.com -module fast -banner

如果目标是网段:

1
go run ./cmd/sma11scan/ -ip 192.168.1.0/24 -banner

子域名收集:

1
go run ./cmd/sma11scan/ -domain example.com

ip

三、端口扫描设计

端口扫描部分是整个工具的基础能力。

目前支持三种扫描模式:

1
2
3
fast:常用端口
top:Top 1000 端口
full:1-65535 全端口

端口扫描采用的是 TCP Connect 方式,也就是通过 net.DialTimeout 尝试建立 TCP 连接。

为了避免每个端口都启动一个 goroutine 导致资源失控,项目中使用了 Worker Pool 模型进行并发控制。扫描任务通过 channel 分发给固定数量的 Worker,目前默认是 100 个 Worker 并发执行。

大致流程是:

1
2
3
4
5
6
7
8
9
10
11
生成扫描任务

写入 task channel

多个 Worker 并发消费

尝试 TCP 连接

连接成功则记录开放端口

可选执行 Banner/HTTP 探测

这种设计的好处是并发可控,不会因为全端口扫描而瞬间创建几万个 goroutine。

同时,为了让扫描过程更直观,项目中还使用原子计数器统计进度,并在终端实时刷新扫描进度。


四、域名、IP、CIDR 的统一处理

工具支持三种输入形式:

1
2
3
单个 IP
域名
CIDR 网段

如果用户输入的是 IP,直接扫描该 IP。

如果用户输入的是 CIDR,例如:

1
192.168.1.0/24

程序会先解析出网段内的所有 IP,再逐个执行扫描。

如果用户输入的是域名,例如:

1
www.example.com

程序会先通过 DNS 解析得到 IP,再进行扫描。

这里有一个细节:如果目标是域名,HTTP/HTTPS 探测时不能简单地把域名解析成 IP 后就完全丢掉原域名。因为 HTTPS 请求涉及 TLS SNI,如果 TLS 握手时没有设置正确的 ServerName,很多站点会返回错误证书或握手失败。

所以项目里处理域名扫描时,会保留原始域名。在 TCP 层连接 IP,但 TLS 握手时仍然使用原域名作为 SNI。

简单说就是:

1
2
3
TCP 连接:连 IP
TLS SNI:用域名
HTTP Host:用域名

这个细节对安全探测工具很重要。
cidr

五、Banner 抓取和服务识别

开启 -banner 参数后,工具会对开放端口进一步探测。

Banner 抓取逻辑主要是:

1
2
3
4
5
建立 TCP 连接
设置超时时间
读取服务初始响应
清洗不可见字符和二进制数据
根据关键词识别服务

例如 SSH 服务通常会返回类似:

1
SSH-2.0-OpenSSH_8.2

MySQL、Redis、RDP 等服务也可能通过 Banner 或端口特征进行识别。

服务识别采用了三层策略:

1
2
3
第一层:Banner 关键词匹配
第二层:常见端口服务映射
第三层:无法识别则标记 Unknown

这样即使某些服务没有返回明显 Banner,也可以通过端口表进行兜底判断。


六、HTTP/HTTPS 探测

对于 Web 服务,工具会发起 HTTP/HTTPS 请求,提取更多信息:

1
2
3
4
5
6
状态码
Server Header
Set-Cookie
网页 Title
响应内容
Favicon

HTTP 探测中模拟了常见浏览器请求头,例如:

1
2
3
User-Agent
Accept
Accept-Language

这样可以减少部分站点因为请求头过于简单而返回异常页面的情况。

Title 提取通过正则匹配 HTML 中的 <title> 标签完成。虽然这不是最复杂的 HTML 解析方式,但对资产探测工具来说已经比较实用。


七、Web 指纹识别

项目中实现了一个 Web 指纹匹配模块。

识别目标包括:

1
2
3
4
5
6
7
Web Server
CMS
Framework
OA 系统
CDN
DevOps 工具
常见后台系统

指纹匹配的位置包括:

1
2
3
4
5
header
body
cookie
title
meta

为了降低误报,项目不是简单命中一个关键词就直接判定,而是做了一个权重累积机制。

大致分为:

1
2
3
铁证规则:权重 90-100,单条命中即可基本确认
正文规则:权重 70-85,可信度较高
辅助规则:权重 25-60,需要多个信号叠加

这样做的好处是可以避免一些泛关键词造成误报。

例如单纯在页面里看到某个通用词,不一定能说明目标使用了某个 CMS;但如果 Header、Title、Body 多个位置同时命中,就更可信。


八、Favicon 指纹识别

很多 Web 系统的 favicon 比页面内容更稳定,所以项目也加入了 Favicon 指纹识别。

流程大概是:

1
2
3
4
5
6
7
8
9
从 HTML 中提取 favicon 路径

处理相对路径和绝对路径

下载 favicon

计算 MD5 哈希

和指纹库匹配

这里有一个细节:favicon 的路径不一定是完整 URL,可能是:

1
<link rel="icon" href="/favicon.ico">

也可能是:

1
<link rel="shortcut icon" href="static/favicon.ico">

所以项目里使用了 url.ParseResolveReference 来正确拼接 favicon 地址。

拿到 favicon 内容后,计算 MD5 值,再和内置的 Favicon 指纹库进行匹配。


九、CDN 检测

CDN 检测主要基于 CNAME 规则。

当输入目标是域名时,程序会通过 net.LookupCNAME 获取 CNAME 记录,然后和内置 CDN 规则库进行匹配。

目前覆盖了多家国内外主流 CDN,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Cloudflare
Akamai
AWS CloudFront
Fastly
阿里云 CDN
腾讯云 CDN
百度云加速
Azure CDN
华为云 CDN
七牛云
又拍云
网宿科技
蓝汛
白山云
Bilibili CDN
Incapsula

例如目标 CNAME 中包含:

1
cloudflare.net

就可以判断目标大概率使用了 Cloudflare。

这个检测方式不是绝对准确,但对多数常见 CDN 场景比较有效。


十、源站 IP 找回

CDN 检测之后,项目还尝试实现了源站 IP 找回。

当前版本主要通过 VirusTotal API 获取历史 DNS 解析记录。

使用前需要设置环境变量:

1
export VT_API_KEY="your-api-key"

Windows PowerShell:

1
$env:VT_API_KEY = "your-api-key"

流程大概是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
检测目标是否使用 CDN

如果使用 CDN,查询 VirusTotal 历史解析 IP

拿到候选 IP 列表

对候选 IP 发起 HTTP/HTTPS 请求

Host 头仍然设置为原域名

TLS SNI 也设置为原域名

比较 Title + Favicon Hash

判断是否疑似源站 IP

这里不能只判断候选 IP 是否开放 80/443,因为很多无关服务器也可能开放 Web 服务。

所以项目里使用了两个维度做验证:

1
2
网页 Title
Favicon 哈希

如果候选 IP 返回的 Title 和 Favicon 与原始域名访问结果一致,则认为它更可能是源站 IP。

如果找回成功,工具会切换到源站 IP 继续扫描。
如果找回失败,也不会中断流程,而是继续扫描 CDN 节点。
cdn

十一、子域名收集

子域名模块目前支持两种方式:

1
2
DNS 字典爆破
crt.sh 证书透明度日志查询

字典爆破会读取默认字典文件:

1
subdomains.txt

然后拼接成:

1
2
3
4
www.example.com
api.example.com
admin.example.com
test.example.com

再并发进行 DNS 解析。

为了避免并发过高,子域名爆破使用了信号量限制并发,目前默认 50 并发。

子域名发现后,可以继续联动端口扫描和指纹识别。

例如:

1
go run ./cmd/sma11scan/ -domain example.com -module top -banner

这样可以先收集子域名,再对发现的子域名进行资产探测。
subdomain

十二、项目结构

当前项目结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
├── cmd/
│ └── sma11scan/
│ └── main.go
├── internal/
│ ├── scanner/
│ │ ├── scanner.go
│ │ └── scan_model.go
│ ├── banner/
│ │ └── banner.go
│ ├── cdn/
│ │ └── cdn.go
│ ├── fingerprint/
│ │ └── fingerprint.go
│ └── subdomain/
│ └── subdomain.go
├── go.mod
├── subdomains.txt
└── README.md

其中:

1
cmd/sma11scan

是命令行入口,负责解析参数和组织整体流程。

1
internal/scanner

负责端口扫描、Worker Pool、CIDR 解析、域名解析、结果输出。

1
internal/banner

负责 Banner 抓取和 HTTP/HTTPS 探测。

1
internal/cdn

负责 CDN 检测和源站 IP 找回。

1
internal/fingerprint

负责 Web 指纹和 Favicon 指纹识别。

1
internal/subdomain

负责子域名收集。

这种拆分方式比把所有逻辑写在 main.go 里更清晰,也方便后续升级成 Web 平台。


十三、第一版的不足

第一版虽然已经实现了不少扫描能力,但它还是一个 CLI 工具,还没有平台化。

目前存在这些不足:

1
2
3
4
5
6
7
8
1. 扫描结果只在终端输出,没有持久化入库
2. 没有任务系统,不能查询历史扫描任务
3. 没有 Web API,无法通过前端或其他系统调用
4. 没有 Redis 队列,扫描任务还不能异步调度
5. 没有用户体系和鉴权
6. 没有统一的 API 响应格式
7. 没有扫描结果分页查询
8. 没有任务失败重试机制

也就是说,当前版本更适合作为本地命令行工具使用。如果要做成更接近真实安全平台的项目,还需要继续工程化。


十四、后续计划:从 CLI 工具升级为扫描平台

下一步计划是把 sma11sCan 从 CLI 工具升级为一个后端扫描平台。

大致方向是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Gin 路由

参数校验和统一响应

创建扫描任务

MySQL 保存任务信息

Redis 队列异步调度

Worker 执行扫描

扫描结果入库

API 查询任务状态和扫描结果

计划中的接口包括:

1
2
3
POST /api/v1/tasks
GET /api/v1/tasks/:task_id
GET /api/v1/tasks/:task_id/results

创建任务时,API 不直接执行完整扫描,而是立即返回一个 task_id

1
2
3
4
{
"task_id": "task_xxx",
"status": "pending"
}

后台 Worker 再异步执行端口扫描、Banner 抓取、CDN 检测、指纹识别等任务。扫描完成后,把结果写入 MySQL。

这样可以避免 HTTP 请求长时间阻塞,也方便后续做并发控制、失败重试、任务状态查询和结果管理。


十五、总结

sma11sCan 第一版主要解决的是“扫描能力”的问题。

它实现了:

1
2
3
4
5
6
7
8
9
高并发端口扫描
Banner 抓取
HTTP/HTTPS 探测
Web 指纹识别
Favicon 识别
服务识别
子域名收集
CDN 检测
源站 IP 找回

这一版没有使用 Gin,也没有接入 MySQL、Redis、任务队列这些中间件。它更像是安全开发项目的第一阶段:先把扫描器核心能力做出来。

后续如果要投安全开发实习,仅仅有 CLI 工具还不够。更有竞争力的方向是把它升级成一个完整的资产扫描平台:

1
2
3
4
5
6
可提交任务
可异步执行
可保存结果
可查询状态
可控制并发
可统一部署

所以 sma11sCan 第一版只是开始。下一阶段会重点做 Gin API 层、任务队列、数据库持久化和扫描结果管理,把它从一个命令行工具逐步改造成一个真正的安全扫描平台。


安全声明

本项目仅用于授权资产探测、学习研究和安全开发实践。请勿用于未授权目标扫描或任何非法用途。使用者需要自行遵守当地法律法规,并对自己的行为负责。