sx 是一款遵循 UNIX 哲学的命令行网络扫描器。
该项目的目标是创建一款拥有整洁简单代码的最快网络扫描器。
最简单的方法是从 GitHub Releases 下载并将可执行文件放到 PATH 中。
要求:
在 macOS 上,首先安装 Xcode 命令行工具,以便 CGO 可以链接系统自带的 libpcap:
xcode-select --install
在源码树的根目录下运行:
go build
以下是一些快速示例,展示如何使用 sx 扫描网络。
扫描本地网络并显示已连接设备的 IP 地址、MAC 地址及其对应的硬件厂商:
sx arp 192.168.0.1/24
示例输出:
192.168.0.1 b0:be:76:40:05:8d TP-LINK TECHNOLOGIES CO.,LTD.
192.168.0.111 80:c5:f2:0b:02:e3 AzureWave Technology Inc.
192.168.0.171 88:53:95:2d:3c:af Apple, Inc.
使用 JSON 输出:
sx arp --json 192.168.0.1/24
示例输出:
{"ip":"192.168.0.1","mac":"b0:be:76:40:05:8d","vendor":"TP-LINK TECHNOLOGIES CO.,LTD."}
{"ip":"192.168.0.111","mac":"80:c5:f2:0b:02:e3","vendor":"AzureWave Technology Inc."}
{"ip":"192.168.0.171","mac":"88:53:95:2d:3c:af","vendor":"Apple, Inc."}
等待 5 秒再退出,以接收延迟的回复报文,默认 sx 等待 300 毫秒:
sx arp --exit-delay 5s 192.168.0.1/24
每 10 秒重新扫描网络的实时扫描模式:
sx arp 192.168.0.1/24 --live 10s
与 nmap 及其他隐式进行 ARP 请求以在实际扫描前将 IP 地址解析为 MAC 地址的扫描器不同,sx 显式地使用 ARP 缓存 概念。ARP 缓存文件是一个简单的文本文件,每行包含一个 JSON 字符串(JSONL 文件),其 JSON 字段与上文描述的 ARP 扫描 JSON 输出相同。TCP、UDP 等高层协议的扫描从标准输入读取 ARP 缓存文件,然后开始实际扫描。
这不仅简化了程序的设计,还加速了扫描过程,因为无需每次都执行 ARP 扫描。
假设实际的 ARP 缓存位于 arp.cache 文件中。我们可以手动创建它,或如下所示使用 ARP 扫描:
sx arp 192.168.0.1/24 --json | tee arp.cache
一旦有了 ARP 缓存文件,就可以运行高层协议的扫描,例如 TCP SYN 扫描:
cat arp.cache | sx tcp -p 1-65535 192.168.0.171
示例输出:
192.168.0.171 22
192.168.0.171 443
这里发现端口 22 和 443 是开放的。
如果在大型扫描期间看到 send: No buffer space available,通常表示发送方超过了内核/原始套接字缓冲区的承受能力。第一解决方案是添加 --rate 并降低发送突发速率,例如 -r 100000/s 或根据需要更低。在某些系统上,调整操作系统的套接字/缓冲区限制也有帮助,但速率限制是主要的修复手段。
使用 JSON 输出的扫描:
cat arp.cache | sx tcp --json -p 1-65535 192.168.0.171
示例输出:
{"scan":"tcpsyn","ip":"192.168.0.171","port":22}
{"scan":"tcpsyn","ip":"192.168.0.171","port":443}
扫描多个端口范围:
cat arp.cache | sx tcp -p 1-23,25-443 192.168.0.171
或单个端口:
cat arp.cache | sx tcp -p 22,443 192.168.0.171
或使用 --ports-file 选项指定一个文件,文件每行包含一个端口或端口范围。
从文件中扫描 IP/端口对,并使用 JSON 输出:
cat arp.cache | sx tcp --json -f ip_ports_file.jsonl
输入文件的每一行都是一个 JSON 字符串,其中必须包含 ip 和 port 字段。
示例输入文件:
{"ip":"10.0.1.1","port":1080}
{"ip":"10.0.2.2","port":1081}
可以使用 -a 或 --arp-cache 选项指定 ARP 缓存文件:
sx tcp -a arp.cache -p 22,443 192.168.0.171
或通过标准输入重定向:
sx tcp -p 22,443 192.168.0.171 < arp.cache
也可以使用 tcp syn 子命令代替 tcp:
cat arp.cache | sx tcp syn -p 22 192.168.0.171
除非传递了 --flags 选项(参见下文),否则 tcp 子命令只是 tcp syn 子命令的简写。
sx 支持使用虚拟网络接口(wireguard、openvpn 等)进行扫描,这种情况下不需要使用 ARP 缓存,因为这些接口需要原始 IP 报文而非以太网帧作为输入。例如,扫描 VPN 网络上的一个 IP 地址:
sx tcp 10.1.27.1 -p 80 --json
大多数网络扫描器都会尝试解释扫描结果。例如,它们会说“该端口已关闭”,而不是“我收到了一个 RST”。有时它们是对的,有时不对。这对初学者来说更容易理解,但当你清楚自己在做什么时,你会不断尝试从程序的解释中推断真实情况,尤其是在使用更高级的扫描技术时。
sx 试图克服这些问题。对于 TCP FIN、NULL、Xmas 和自定义 TCP 扫描,它返回所有回复报文的信息。这些信息包括 IP 地址、TCP 端口以及回复报文中设置的所有 TCP 标志。
TCP FIN 扫描及其变种(NULL 和 Xmas)利用了 RFC793 第 3.9 节:
SEGMENT ARRIVES
If the state is CLOSED (i.e., TCB does not exist) then
all data in the incoming segment is discarded. An incoming segment containing a RST is discarded. An incoming segment not containing a RST causes a RST to be sent in response. The acknowledgment and sequence field values are selected to make the reset sequence acceptable to the TCP that sent the offending segment.
因此,关闭的端口应返回带有 RST 标志的报文。
该节还指出:
If the state is LISTEN then
...
Any other control or text-bearing segment (not containing SYN) must have an ACK and thus would be discarded by the ACK processing. An incoming RST segment could not be valid, since it could not have been sent in response to anything sent by this incarnation of the connection. So you are unlikely to get here, but if you do, drop the segment, and return.
关键短语是:drop the segment, and return。因此,在大多数操作系统上,开放的端口将丢弃包含除 SYN、ACK 和 RST 之外任何标志的 TCP 报文。
现在,用 TCP FIN 扫描扫描某个关闭的端口:
cat arp.cache | sx tcp fin --json -p 23 192.168.0.171
示例输出:
{"scan":"tcpfin","ip":"192.168.0.171","port":23,"flags":"ar"}
flags 字段包含回复报文中的全部 TCP 标志,每个字母代表一个 TCP 标志:
s - SYN 标志a - ACK 标志f - FIN 标志r - RST 标志p - PSH 标志u - URG 标志e - ECE 标志c - CWR 标志n - NS 标志这里发现端口 23 发送了带有 ACK 和 RST 标志的回复报文(根据 RFC793,这是关闭端口的典型响应)。
如果扫描开放的端口,则不会得到响应(除非防火墙伪造了响应)。
其他类型的 TCP 扫描可类似进行。
TCP NULL 扫描:
cat arp.cache | sx tcp null --json -p 23 192.168.0.171
TCP Xmas 扫描:
cat arp.cache | sx tcp xmas --json -p 23 192.168.0.171
可以使用 --flags 选项发送带有自定义 TCP 标志的 TCP 报文。
例如,发送带有 SYN、FIN 和 ACK 标志的 TCP 报文以对远程操作系统进行指纹识别:
cat arp.cache | sx tcp --flags syn,fin,ack --json -p 23 192.168.0.171
Windows 和 MacOS 不会响应此报文,但 Linux 会发送带有 RST 标志的回复报文。
--flags 选项的可能参数:
syn - SYN 标志ack - ACK 标志fin - FIN 标志rst - RST 标志psh - PSH 标志urg - URG 标志ece - ECE 标志cwr - CWR 标志ns - NS 标志sx 可以帮助调查开放的 UDP 端口。UDP 扫描利用了 RFC1122 第 4.1.3.1 节:
If a datagram arrives addressed to a UDP port for which there is no pending LISTEN call, UDP SHOULD send an ICMP Port Unreachable message.
与 TCP 扫描类似,sx 返回 UDP 扫描的所有回复 ICMP 报文的信息,包括 IP 地址、ICMP 报文类型和回复报文中的代码。
例如,要检测主机上的 DNS 服务器,运行:
cat arp.cache | sx udp --json -p 53 192.168.0.171
示例输出:
{"scan":"udp","ip":"192.168.0.171","icmp":{"type":3,"code":3}}
这里发现主机发送了 ICMP 回复报文,类型为 Destination Unreachable,代码为 Port Unreachable(根据 RFC1122,这是关闭端口的典型响应)。防火墙通常会设置与 Port Unreachable 不同的 ICMP 代码,因此可以很容易地被检测出来。
有时需要限制生成报文的发送速度。可以通过 --rate 选项实现。
例如,将速度限制为每 5 秒 1 个报文:
cat arp.cache | sx tcp --rate 1/5s --json -p 22,80,443 192.168.0.171
有时需要排除某些 IP 地址和子网进行扫描。可以通过 --exclude 选项实现。它指定一个文件,文件中每行包含要排除的 IP 地址或 CIDR 子网。
例如,要排除 RFC 1918 地址,创建文件 ips.txt,内容如下:
10.0.0.0/8
172.16.0.0/16
192.168.0.0/16
也可以插入注释和空行:
# exclude RFC 1918 addresses
10.0.0.0/8 # comment 1
172.16.0.0/12 # comment 2
192.168.0.0/16 # comment 3
0.0.0.0/8 # used in initialization procedures (RFC 6890)
# exclude RFC 5735 addresses
127.0.0.0/8 # loopback address
192.0.0.0/24 # reserved block for IETF protocol assignments
224.0.0.0/4 # allocated for use in IPv4 multicast address assignments
240.0.0.0/4 # reserved for future use
# exclude Amazon network
3.0.0.0/8
# ip addresses are valid as well
1.1.1.1
然后使用 --exclude ips.txt 选项运行扫描。
作为扫描组合的示例,可以将 ARP 扫描和 TCP SYN 扫描结合起来,创建一个定期扫描整个局域网的实时 TCP 端口扫描器。
启动实时 ARP 扫描并将结果保存到 arp.cache 文件:
sx arp 192.168.0.1/24 --live 10s --json | tee arp.cache
在另一个终端中启动 TCP SYN 扫描:
while true; do sx tcp -p 1-65535 -a arp.cache -f arp.cache; sleep 30; done
sx 可以检测活跃的 SOCKS5 代理。要扫描,必须指定一个 IP 范围或包含 IP/端口对的 JSONL 文件。
例如,IP 范围扫描:
sx socks -p 1080 10.0.0.1/16
从文件中扫描 IP/端口对,并使用 JSON 输出:
sx socks --json -f ip_ports_file.jsonl
输入文件的每一行都是一个 JSON 字符串,其中必须包含 ip 和 port 字段。
示例输入文件:
{"ip":"10.0.1.1","port":1080}
{"ip":"10.0.2.2","port":1081}
也可以指定要扫描的端口范围:
sx socks -p 1080-4567 -f ips_file.jsonl
这种情况下,只会从文件中提取 IP 地址,不再需要 port 字段。
sx help
欢迎贡献、提交 issue 和功能需求。
Logo 由 mikhailtsoy.com 设计。
本项目采用 MIT 许可证授权。完整的许可证文本请参阅 LICENSE 文件。