naabu源码分析

参数解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
使用方法:
./naabu-2.1.1 [flags]

Flags:
INPUT:
-host string[] 扫描目标, 支持域名或IP地址
-list, -l string 从文件读取扫描目标 (file)
-exclude-hosts, -eh string 排除扫描目标, 以逗号分隔
-exclude-file, -ef string 从文件读取要排除的扫描目标 (file)

PORT:
-port, -p string 指定扫描端口 (80,443, 100-200)
-top-ports, -tp string 默认top100 (default 100)
-exclude-ports, -ep string 需要排除的扫描端口, 以逗号分隔
-ports-file, -pf string 从文件读取扫描端口 (file)
-port-threshold, -pts int 跳过主机端口扫描的端口阈值 (暂不知道作用)
-exclude-cdn, -ec 跳过 CDN 的完整端口扫描 (只检查 80,443)
-display-cdn, -cdn 展示正在使用的cdn

RATE-LIMIT:
-c int general internal worker threads (default 25)
-rate int packets to send per second (default 1000)

OUTPUT:
-o, -output string 指定结果输出文件 (可选参数)
-json 以json格式输出结果
-csv 以csv格式输出结果

CONFIGURATION:
-scan-all-ips, -sa 扫描与dns记录的所有ip
-ip-version, -iv string[] 指定ipv4还是ipv6 - (default 4)
-scan-type, -s string 扫描类型, 默认syn半连接 (SYN/CONNECT)
-source-ip string 来源IP端口 (x.x.x.x:yyy)
-interface-list, -il 列出可用网卡和公共IP
-interface, -i string 指定用于端口扫描的网卡
-nmap 直接调用namp扫描, 需注意已标位弃用 (nmap must be installed) - Deprecated
-nmap-cli string 对找到的结果运行nmap命令 (example: -nmap-cli 'nmap -sV')
-r string 自定义域名解析 (comma separated or from file)
-proxy string socks5 代理 (ip[:port] / fqdn[:port]
-proxy-auth string socks5 代理认证 (username:password)
-resume 通过resume.cfg恢复扫描
-stream 流模式 (disables resume, nmap, verify, retries, shuffling, etc)
-passive 使用shodan的数据接口显示被动开放端口
-irt, -input-read-timeout duration 输入超时时间 (default 3m0s)
-no-stdin 禁用标准处理

HOST-DISCOVERY:
-sn, -host-discovery 仅执行主机探活
-Pn, -skip-host-discovery 跳过主机探活
-ps, -probe-tcp-syn string[] TCP SYN Ping (host discovery needs to be enabled)
-pa, -probe-tcp-ack string[] TCP ACK Ping (host discovery needs to be enabled)
-pe, -probe-icmp-echo ICMP echo request Ping (host discovery needs to be enabled)
-pp, -probe-icmp-timestamp ICMP timestamp request Ping (host discovery needs to be enabled)
-pm, -probe-icmp-address-mask ICMP address mask request Ping (host discovery needs to be enabled)
-arp, -arp-ping ARP ping (host discovery needs to be enabled)
-nd, -nd-ping IPv6 Neighbor Discovery (host discovery needs to be enabled)

OPTIMIZATION:
-retries int 端口扫描重试次数 (default 3)
-timeout int 超时等待时间, 单位为毫秒 (default 1000)
-warm-up-time int 扫描阶段之间的时间 (default 2)
-ping 进行ping探活
-verify 使用tcp再次验证端口

DEBUG:
-health-check, -hc run diagnostic check up
-debug 显示调试信息
-verbose, -v 展示详细输出
-no-color, -nc 在cli输出禁用着色
-silent 在输出中仅展示结果
-version 显示naabu的版本
-stats 显示运行扫描的状态
-si, -stats-interval int 显示统计信息更新之间等待的秒数 (default 5)

目录文件说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
├─cmd
│ ├─functional-test
│ │ │ main.go
│ │ │ run.sh
│ │ │ testcases.txt
│ │ │
│ │ └─test-data
│ │ request.txt
│ │
│ ├─integration-test
│ │ integration-test.go
│ │ library.go
│ │
│ └─naabu
│ main.go

├─internal
│ └─testutils
│ integration.go

└─pkg
├─israce // 竞争检测
│ norace.go
│ race.go

├─privileges // 权限判定, 在windows, linux, darwin下是否具有相应的权限, syn扫描或全连接扫描
│ privileges.go
│ privileges_darwin.go
│ privileges_linux.go
│ privileges_win.go

├─result
│ results.go // 定义Result结构体
│ results_test.go

├─routing // 根据不同的操作系统选择不同的路由
│ router.go
│ router_darwin.go
│ router_linux.go
│ router_windows.go

├─runner
│ banners.go // 控制台输出运行信息, 网络环境, 扫描类型及网卡信息
│ banners_test.go
│ default.go // 定义运行时的一些默认值, 如超时, 速率, 重试次数等
│ healthcheck.go // 进行naabu锁依赖的环境运行检查
│ ips.go // ip地址解析, 排除不在设置范围内的
│ ips_test.go
│ nmap.go // 解析参数(nmap-cli), 并调用nmap
│ nmap_test.go
│ options.go // 命令行参数解析到结构体对象Options
│ output.go // 定义Result结构体及将结果写文件保存的相关方法
│ output_test.go
│ ports.go // 定义常见端口, 以及解析端口的方法
│ ports_test.go
│ resume.go // 定义结构体ResumeCfg, 以及保存/读取/删除恢复配置文件
│ runner.go // 定义结构体Runner, 运行调度的核心, 还包括结果的处理等
│ targets.go // 目标对象解析,添加处理等, 保存到runner结构体对象中
│ targets_test.go
│ util.go // 主要包括host转ip方法, 以及判断os是否支持的函数
│ util_test.go
│ validate.go // 参数校验认证
│ validate_test.go

├─scan
│ arp.go // arp报文组织发送
│ cdn.go // 检查传入的IP是否为cdn
│ cdn_test.go
│ connect.go // 建立完整的tcp连接, 用于判断端口是否开放
│ connect_test.go
│ externalip.go // 通过公共api获取自己的ip出口地址
│ externalip_test.go
│ icmp.go // 几种类型的icmp报文组织
│ ndp.go // ndp报文类型组织
│ option.go // 扫描的配置(超时时间,重试,速率,输出等配置)
│ ping.go // ping报文组织及发送
│ ping_test.go
│ scan.go // 定义scanner结构体, 另外包括各个扫描部分的调度
│ scan_unix.go // 在linux下
│ tcpsequencer.go //
│ tcpsequencer_test.go

└─utils
util.go

源码分析

先看下项目依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require (
github.com/google/gopacket v1.1.19 // libpcap的go实现
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 // 获取一个可用于tcp的端口
github.com/projectdiscovery/blackrock v0.0.0-20220628111055-35616c71b2dc // 伪随机的方式访问空间中的元素一次
github.com/projectdiscovery/cdncheck v0.0.4-0.20220322144854-b2d8ce308abb // 检查给定IP是否属于已知cnd范围内
github.com/projectdiscovery/clistats v0.0.8 // 命令行统计信息展示
github.com/projectdiscovery/fdmax v0.0.3 // 动态修改最大文件描述符
github.com/projectdiscovery/fileutil v0.0.3 // 文件处理工具
github.com/projectdiscovery/goflags v0.1.1 // 命令行参数解析
github.com/projectdiscovery/gologger v1.1.4 // 日志处理
github.com/projectdiscovery/iputil v0.0.0-20220712175312-b9406f31cdd8 // 用于处理ips和cidr
github.com/projectdiscovery/networkpolicy v0.0.2-0.20220525172507-b844eafc878d // 网络代理
github.com/remeh/sizedwaitgroup v1.0.0 // 速率控制
//go.uber.org/ratelimit v0.2.0 // indirect // 速率控制
golang.org/x/net v0.1.0 // go网络库的补充
golang.org/x/sys v0.1.0 // 与操作系统低级别交互的go补充包
)

程序入口

cmd/naabu/main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
func main() {
// 1. 解析命令行参数或从配置文件读取
options := runner.ParseOptions()

// 2. 新建runner
naabuRunner, err := runner.NewRunner(options)
if err != nil {
gologger.Fatal().Msgf("Could not create runner: %s\n", err)
}

// 3. 读取退出信号, 优雅退出
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for range c {
// 退出时打印结果
naabuRunner.ShowScanResultOnExit()
gologger.Info().Msgf("CTRL+C pressed: Exiting\n")
// 若是配置了恢复文件, 则进行恢复
if options.ResumeCfg.ShouldSaveResume() {
gologger.Info().Msgf("Creating resume file: %s\n", runner.DefaultResumeFilePath())
err := options.ResumeCfg.SaveResumeConfig()
if err != nil {
gologger.Error().Msgf("Couldn't create resume file: %s\n", err)
}
}
// 退出时关闭runner
naabuRunner.Close()
os.Exit(1)
}
}()

// 4. 扫描核心
err = naabuRunner.RunEnumeration()
if err != nil {
gologger.Fatal().Msgf("Could not run enumeration: %s\n", err)
}

// 5. 完成后清除重启配置
// on successful execution remove the resume file in case it exists
options.ResumeCfg.CleanupResumeConfig()
}

pkg/runner/runner.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
func (r *Runner) RunEnumeration() error {
...
// 1.如果支持syn半连接扫描
if privileges.IsPrivileged $$ r.options.ScanType == SynScan {...}
...
// 2.设置了steam, 没太看懂这个有什么作用, 个人实际运行中添加了该参数会报错
if r.options.Stream{...}
...
// 3. 设置并发控制
r.wgscan = sizedwaitgroup.New(r.options.Rate)
r.limiter = ratelimit.New(context.Background(), int64(r.options.Rate), time.Second)
...
// 4. 若设置了主机探活检测及判断是否支持发送原始数据包
if shouldDiscoverHosts && shouldUseRawPackets {...}
...
// 5. 选择是否为流模式和显示被动开放端口
switch {
case r.options.Stream && !r.options.Passive:
...
case r.options.Stream && r.options.Passive:
...
default:
...
// 获取目标IP
targets, targetsV4, targetsv6, err := r.GetTargetIps(ipsCallback)
...
// 若设置了查看进度

// 处理核心
if shouldUseRawPackets {
r.RawSocketEnumeration(ip, port)
} else {
r.wgscan.Add()
go r.handleHostPort(ip, port)
}
}

}

主机探活

探活的顺序为:

  • Icmp Echo Request
  • Tcmp Timestamp Reqeust
  • Icmp Netmask Request
  • ARP Scan
  • Syn Probes
  • Ack Probes
  • IPv6 NDP