让fscan再次伟大
2026.08.13,README 补充带认证 socks5 代理的用法说明(socks5://用户名:密码@ip:端口),详见第 9 节使用案例(5)。此前代码已支持账号密码认证,但文档一直没写,容易误以为不支持。
2026.06.10,修复socks5+nmap库的探测逻辑下对不开放的端口误报为开放,原因是新增加的超时逻辑处忘记判断是否处于代理模式下;修复netbios信息收集的一处报错。
2026.02.07,①发现探测一些端口时gonmap错误判断为关闭,原因是在前n个指定探针发送后都无法探测到协议,达到了设置的gonmap整体单端口探测时间,直接返回了端口是关闭的状态。现在修复是采用超时后再使用tcp连接一次判断真实开放情况,后续有空再直接拿到中间探测的开放状态,以节省这种超时状态下的最后建立一次tcp连接的时间。②协程池相关调用的健壮性修复。
2026.02.26,修复ftp协议匿名登录时可能卡住程序的问题,复现在部分非ftp协议但监听在21端口的服务,卡住的原因是当前的ftp底层库仅在连接时设置了超时。
2026.04.24,修复扫描插件调用的逻辑问题,原先在设置-nmap时如果端口存在默认调用插件,将不进行协议比对直接调用,这样没法覆盖例如“目标端口是3306但实际运行的协议的rdp“的情况,会对rdp使用mysql的探测逻辑,这是不对的。哦,我真的遇到了把rdp端口监听到3306的奇葩情况。。
本次更新有较大改动,因此重新编写readme,做一次升级总结与使用案例总结。
前情提要:fscanx在很久以前添加了kscan的”web指纹“、gonmap的”协议指纹“识别,在此基础上,最近我又加入了-std选项以支持从标准输入来直接扫描ip段、ip:port、域名、url、masscan的输出内容等,这极大提升了扫描到有效资产的概率,比如使用”masscan | fscanx“ 或”fscanx -hf url.txt“这样的命令,扫描的都是已探测到存活的ip和端口,此时就暴露了以往不曾发现的问题->内存占用起飞,在1000并发下可以达到1.8G以上。
经排查锁定了是kscan的指纹识别相关代码、icmp扫描和端口扫描逻辑部分的设计问题、gonmap库的指纹正则表达式相关问题、http报文读取和keepalivel连接复用问题等等。
既然已经发现了问题,只好努力改正,毕竟我们这些臭穷鬼,哪里舍得买4核4G的服务器,只能用1G内存的垃圾VPS,如果程序Out of memory被kill掉,会很头疼,只好压榨一下程序。后续我还想要单独开一个分支改成通过websocket/http接收扫描任务,再推送扫描结果或写入数据库,从而能被其他的测绘系统调用,不再需要开启进程来调用本程序。
这里给出本程序的定位:内外兼修、轻车重炮。可以理解为象棋盘中的“炮”和“车”,高架炮需要炮台,而这个炮台就是代理,一炮打到目标内网。“车”就是横行直入,可以投递到目标内网的机器运行。为什么轻车而重炮?因为“代理”这颗棋子随时可以舍弃甚至牺牲,维护一个代理或隧道工具的免杀也比较容易,代价较低,而fscanx这枚炮是比较重的,代价较高。当然你也可以把它当作“车”来用,直接深入腹地能提供更快的侦察信息(icmp扫描)。本人比较习惯把命令控制、代理通道、侦察这几个活动隔离开来,防止牵一发动全身,一杀全挂。基于这种惯性,通常我会把它当炮使,而fscanx专门优化了socks代理下的端口探测、协议爆破、poc扫描等,支持通过代理探测tcp/udp协议。既能对公网资产进行信息收集(例如把收集到的目标ip/ip段/ip:port/域名/url整理一个txt直接用-hf探测),也能测绘内网资产,这是内外兼修。
下面依次阐述更新的功能,最后给出使用案例和推荐命令。
本次升级添加了大网段智能探测功能,简单来说就是把A段、B段这种大网段,优先使用“策略”先探测和筛选出存活的子网段,再继续原先的探测方式进一步扫描。而这里的“策略”,就是对每个子C段的特定位置ip进行tcp+icmp扫描验活,默认我们针对每个C段的1、2、253、254这四个IP位置进行80端口tcp探测以及icmp扫描验证存活,如果80端口开放或者icmp有响应,都认为这个C段是存活的。并且添加了命令参数,可以控制对C段的哪些ip位置(-ai)、对哪些端口(-ap)进行扫描。
默认情况下,对ip位置1、2、253、254进行探测,是因为1、253、254在实际网络工程实践中都极可能是网关位置,而2这个位置一般是dhcp分配的第一个ip位,极可能有存活资产。tcp默认探测80端口,是因为网关大概率开放80端口的web管理界面。
当然,如果你根据现有点位,已经摸清楚目标内网的网络结构,比如你明确知道网关部署在192.168.1.253,然后还有一台防火墙部署在192.168.1.128,那么你可以设置 "-ai 253,128" 从而探测这两个ip位,如果你知道网关开放了22端口,你还可以设置 "-ap 22,80"从而支持tcp扫描22端口。
相关参数列表:
| 参数 | 含义 | 例子 |
|---|---|---|
| -auto | 开启智能预扫描。 | -auto |
| -am | 智能扫描阶段使用哪些探测方法,目前支持tcp、icmp。默认值是tcp,icmp | -am tcp,icmp |
| -ap | 智能扫描阶段对哪些tcp端口进行开放探测。默认值是80 | -ap 22,80 |
| -ai | 智能扫描阶段对C段的哪些ip位置进行tcp/icmp探测。默认值是 1,2,253,254 | -ai 1,254 |
| -atime | 智能扫描阶段的tcp扫描的建立连接超时设置。默认值是3 | -atime 2 |
目前该功能只支持-h输入为Cidr格式的/8、/16,以及“10”、“172”、“192”,其他输入不支持。例如下面的例子都是可以的。
速度方面,如果使用fscanx -h 10 -auto -t 1000,这表示智能扫描阶段的协议采用tcp+icmp双重扫描、tcp端口仅扫描80、tcp默认超时3s、icmp默认超时5秒、端口扫描并发1000,并对10.0.0.0/8这个A段做扫描,这个情况下,实测的智能验活阶段的耗时为15分钟(不含后续的存活C段常规端口扫描),其中tcp扫描占用13分钟,icmp扫描占用2分30秒,程序内存占用为120MB左右。如果是一个B段,智能扫描的耗时加起来才11秒,非常快。对于内网场景下的3个私网段(A段10、B段192.168、小B段172.16-172.31)应该是绰绰有余了。
运行截图如下:
如果开启了智能扫描,就只会输出存活网段,而不打印icmp/tcp扫描存活的ip地址,因为那样比较废屏幕。
这里改动比较大,主要有tcp的dialer配置、http的keepalive设置、gonmap库指纹优化(仅保留http、https、ssl、Kerberos、smb、rdp、snmp、socks5、http-proxy、mssql、tomcat、memcache、redis、ftp、ssh、ldap、imap、smtp、pop3、oracle、mongodb、mysql等等协议的探针和指纹)、web指纹库改用 https://github.com/chainreactors/fingers 、扫描逻辑优化(能够使用生产/消费的流式传输的地方都替换为通道流式传输了,比如ip地址生成、ip:port生成、icmp扫描衔接端口扫描的地方)、icmp扫描使用布隆过滤器优化内存占用(主要是过滤icmp监听的杂包)、正则表达式预编译、网络io读写buff(http只读取前192KB报文)、sync.Pool防止频繁的对象内存分配等等措施。
目前对于内存占用,有两种典型的场景,我们分别来测试。
(1)场景一:端口扫描(1000并发)
#内存占用89MB左右。扫描192.168.0.0/16+端口80,不使用icmp验活,tcp超时默认6s
fscanx -h 192 -np -t 1000 -p 80
#开启nmap协议识别功能,内存占用稳定在154MB上下。总耗时6分36秒
fscanx -h 192 -np -t 1000 -nmap
所以通常来说,在探测的资产大多数不存在时,内存稳定在90-150MB左右.
如果资产大量存活,那么读取报文和正则匹配识别指纹将会占用更多的内存,但通常在1000并发下不会超过500MB,随着主机内存的减少,GC会更频繁。
任何情况下,如果短时间探测到大量存活资产(特别是http资产),内存会继续上升(达到450MB然后又降低很多又升高一些),虽然实际占用不到,但golang会预留和申请部分内存留存以提高性能,在内存充裕的情况下,不会这么快还给操作系统,而是等待一定时间后触发垃圾回收或是内存上升到某个动态阈值时回收,这是golang为了减轻垃圾回收机制的负担而特意设计的。实测在1核1G的vps上使用4000并发也没有问题,但1核心的垃圾CPU处理指纹识别的速度很慢(比在我的笔记本慢了1倍多),所以探测时间长了一倍...
另外,如果fscanx当前目录下有纯真ip库qqwry.dat文件,fscanx会使用纯真ip库识别ip归属地和性质信息,打印到http探测的输出结果中,加载qqwry.dat会额外多占用30MB的内存。ip定位最新添加的一个功能。
(2)场景二:url扫描(1000并发)
这是比较极端的一种场景,所扫描的是url,并且大部分url目标都是存活的,程序将要面对最激烈的io碰撞和指纹识别考验。
我们直接对比参考一下fscanx、httpx、spray这三者的表现。
命令分别为:
| fscanx | httpx | spray | |
|---|---|---|---|
| 存活url | 1472个 | 1405个 | 1714个 |
| 耗时 | 1分21秒 | 41秒 | 8分03秒 |
| 内存占用 | 峰值区间450MB-620MB | 峰值区间420MB-680MB | 峰值区间725MB-1291MB |
这里说“峰值区间”是因为进行了多次测试,每次测试结果差距较大(100M的波动),这应该是每次网络拥塞的情况不同导致的差异,在网络ok的情况下,拿到更多报文并处理时,内存占用会更大。实际上,多次测试中,fscanx、httpx的内存占用基本都在400-600MB范围内浮动。
三个工具的侧重点和逻辑处理不同,有些具备web指纹识别、有些具备dns探测、ip归属地识别、有的具备http重试功能等等,每一种功能都有其自身使用场景和牺牲,比如指纹识别是有cpu和时间损耗的,解析域名到ip也是有时间损耗的,侧重点不同,所以这里并没有论谁高谁低的意思,只是想看看fscanx的性能是否在正常范围内,排查有没有特别明显的性能异常问题。
这里让我惊讶的是spray的耗时长达8分钟,几乎是两外两个的8倍。而实际上,spray的默认超时是5s,并且http重试功能也是默认不开启,理伦上设置了更短http超时的spray应该更早完成任务,但不清楚为什么时间拉的那么长,这里也并没有用spray做目录扫描。至于它的内存占用,大概率有复用http连接的问题,这很可能是golang标准库的坑,这一点我倒是不惊讶,当然应该还有其他地方比较占用内存。
认识spray是我在调研新的web指纹库时,发现了gogo作者的仓库,有幸阅读了博客全文,感觉作者团队是非常厉害的、很有想法的、少见的具备匠心精神的团队(在网安圈里),fscanx也采用了该作者的fingers这款指纹库及识别框架,目前已出舱,状态很好。
最后说说httpx。如果开启了httpx的重试功能,比如-retries 设置为1,失败重试一次,那么存活资产会多3分之一(仅代表本人的测试),这一点无疑是非常棒的功能,这种重试可能还有指数退避休眠或者其他的一些很棒的重试策略,能尽可能规避网络问题造成的资产丢失,以后有空我会研究一下。比如之前尝试过把net/http替换为fasthttp,内存直接炸了,所以还是要多研究一下,不能轻易动。
(1)协议指纹方面
对gonmap的逻辑进行了优化,这里不展开了。同时,对探针、指纹进行了优选,仅保留http、https、ssl、Kerberos、smb、rdp、snmp、socks5、http-proxy、mssql、tomcat、memcache、redis、ftp、ssh、telnet、ldap、imap、smtp、pop3、oracle、mongodb、mysql、vnc、svn、rsync、rpc、netbios等协议的探针和指纹。我想这足够应付大部分场景。你也可以修改nmap-service-probes.go添加新的探针和识别指纹,但我设置了每个端口的nmap探测超时只足够尝试4个探针,你需要同步修改这部分。
另外探针和指纹我也添加了一些nmap遗漏的,比如rdp的探针和指纹补充等等。
(2)web指纹方面
替换为 https://github.com/chainreactors/fingers 的web指纹识别引擎和指纹库。如需添加新的指纹,你需要先解压\mylib\finger\resources目录下的fingers_http.json.gz、goby.json.gz,然后编辑json文件去添加指纹,编辑完后使用gzip -c fingers_http.json > fingers_http.json.gz这样的命令来重新并替换原先的指纹压缩包文件,重新go build程序就可以了。
指纹编写参考 https://wiki.chainreactors.red/libs/fingers/sdk/#%E5%8A%A8%E6%80%81%E6%B3%A8%E5%86%8C 。
目前fscanx中只加载fingers、goby这两个指纹引擎,需要的可以自行修改。对了,goby有很多垃圾指纹导致误报,我修改了一部分已经不那么严重,当然还会有,以后遇到了会继续修改。
我们看看新指纹库的识别效果,还是不错的,重点资产还会标记出来:
文件输入选项 -hf 现在增强了,
目前支持处理文件中包含的ip、ip:port、cidr、cidr:port、url、纯域名、masscan重定向(>)保存的文本输出内容,以上这些格式。
这意味着做公网信息收集的时候,你可以一股脑的把收集到的目标ip、ip段、url、子域名,每行一个,全部放到一个txt文件,然后直接扫描。没错,这就是一个懒人至上功能,我承认,人类文明进步的原动力正是懒惰。
长久以来,对于一个新目标的信息收集,往往是这里找到一个ip,那里扫到一批子域名,这边又在google抓到一个新的种子域名,然后又又又又在小程序找到一个新域名,又又又在官网找到一个新的子系统url,又又又在fofa找到一批url,于是想要做主动信息探测的时候,就要自己处理一下,什么ip、域名、解析、url提取,挺麻烦的。所以就增强了-hf的功能。
让我解释一下这些格式的输入分别会怎么被扫描。
这里有一个新参数 -pd 来控制是否要将url、纯域名所提取出的C段添加到端口扫描任务,如果设置了-pd就会提取。如果你仅仅想要扫描大量的纯url做web探测,就不需要开启-pd,因为这将对url或域名的对应ip的c段做扫描,可能并不是你想要的。另外,此处解析域名并没有做并发处理,所以不适合超大量的域名,不过一般情况下,一个目标大概率也没有多少个子域名,几十个就不错了,所以问题不大。
总结:对于一个新目标的公网信息收集,可使用 fscanx -hf target.txt -pd 做快速的信息收集。这里之所以要把域名的ip所在C段一起探测,也是符合一般信息收集的逻辑——总要对同C段做探测吧,看是否有归属于目标的其他资产。大概只有一种情况下,域名解析的ip不需要探测,那就是这个ip是cdn节点的ip,此时探测c段就没必要了,除此之外,这个ip不论是独立ip还是云服务器,它所在c段都有可能部署了目标组织的其他业务,有必要一探。
添加了ip定位识别功能,使用的是纯真ip库,你可以在这里下载qqwry.dat文件到fscanx同目录下,https://github.com/metowolf/qqwry.dat 。
当运行fscanx时,如果检测到该文件就会加载ip库,并在web扫描时打印出来。例如:
去掉了 -nopoc、-nobr选项,这两个本来是控制不扫描poc、不爆破弱口令协议的。由于我很少扫描poc和爆破弱口令,导致每次都要加这两个选项比较麻烦,因此就去掉了,并添加了对应的两个新选项为 -poc、-br,分别表示是否做poc扫描和弱口令爆破,如果设置了才会做poc扫描和弱口令协议爆破。
修改了一下该脚本,以支持新的fscanx输出格式。
如果你曾经使用socks5代理来驱动一些常见工具,那么你可能或多或少都遇到过误报的情况,比如探测的端口全部显示开放,但这明明是不可能的!甚至有时候,你换个代理,或在另一种场景下使用,有时候又变得准确了?太奇怪了。
这是个很困扰的谜题,探究它的过程非常的有意思。 我们依然是通过抓包来分析,一切网络问题最终都是抓包一见分晓。
下图是fscanx使用本机clash的socks5代理进行探测的抓包截图,连接目标是一个确认不开放的ip+端口,但clash的socks5服务却响应了 05 00 ,这表示的是“连接建立成功”!在RFC标准中,socks5服务应该在前一步接收到需要连接的ip:port后,尝试建立连接,并在这一步中告诉客户端是否成功,可明明是不开放的端口,怎么会响应成功呢?
我们简单一思考,就可以推测出问题的原因。因为clash主机并不是最终访问目标端口的那个主机啊!clash只是一个中间商,为你提供了一个本地的socks5服务去使用外层节点的网络,但最终直接访问了目标主机+端口的其实是你购买的X场节点啊,clash与节点之间通过各种隧道来传输数据,最终还是到外部节点去请求!所以clash提供的socks5服务并不是标准的socks5 server,至少它不一定能知道,到底目标端口是否成功建立连接了,这也取决于clash这款软件和X场节点上运行的程序它们是怎么设计的,它们之间的交互又是怎么样的。从目前的抓包情况看,clash与其节点之间并没有设计这种交互逻辑,从而让clash上提供的socks5服务能感知到目标端口的开放情况,它只是汇报给客户端,连接成功,让你下一步自己尝试发包/收包,成就成,不成拉到,这么一个设计。
所以,究竟会不会误报,取决于你使用了什么样的socks5代理工具(主要是服务端),它是否执行标准的socks5服务逻辑,简单来看的话,如果这个socks5是直接在目标内网建立的,然后你通过端口转发或其他的隧道转发到一个自己能访问到的地方,最后你通过socks5去请求,那大概率是不会误报的,因为最终的源头还是socks5服务,不像clash一样,最终去访问目标端口的不是socks5服务本身,而是节点上的其他应用程序。
比如,如果你使用iox作为socks5服务端,那么就不会有误报问题,如果目标端口连接不成功,iox的socks5服务会直接响应不成功给客户端,这就避免了误报问题。
当然,fscanx对于clash、v2ray这样的代理工具提供的socks5也做了优化,从而避免误报。这是怎么做的呢?
很简单,fscanx在程序运行的一开始,先检测一下socks5代理服务是否是正常的socks5代理,如果不正常,那么后续在socks5通信建立后,先发送最简单的http探针报文 "GET / HTTP/1.0\r\n\r\n" ,然后尝试读取响应,但凡有一个字节回来,那么目标端口肯定是开放的,此时会重新建立一个全新连接返回给需要使用连接的函数。为什么用http呢?因为http探针可以探测绝大多数常见协议,例如http/https/ftp/ssh/mysql/telnet/pop3/imap等,都是会响应http请求的。并且如果用户设置了-nmap,使用gonmap去探测时也可以识别到真实的开放情况,因为gonmap会发送探针,同时我做了优化,会知道是否有内容读取到,在检测到socks5服务非标准时会判断为端口不开放。因此fscanx可以兼容标准socks5和非标准的socks5代理,在两种情况下都可以准确探测。
(1)大网段快速扫描。可开启-auto智能预扫描加快速度,并设置-np表示在后续的存活c段的端口扫描中不要再使用icmp扫描,因为有些ip只开放端口但ping不到
fscanx -h 192.168.0.0/16 -auto -nmap -t 1000 -np
(2)对一个新目标的公网资产做信息收集,整理ip、ip段、子域名、url到一个txt,使用-hf统一探测,并将url、域名解析到的ip所在c段一并扫描
fscanx -hf target.txt -pd -nmap -np
注:-pd 表示将url、域名中的ip或解析出的ip所在c段添加到端口扫描
(3)对批量url做web扫描。可通过-tn参数控制web扫描的并发数量,该参数默认值是60
fscanx -hf url.txt -tn 200
(4)配合masscan极速扫描。在-std模式下,将忽略-tn(web扫描并发数),由-t参数(端口扫描并发数)控制所有并发数,保持简单
masscan --rate 200 -p 80,443 192.168.0.0/16 | fscanx -std -nmap -t 200
(5)代理模式下,高架炮远程扫描目标内网的192 B段。
fscanx -socks5 socks5://127.0.0.1:1080 -h 192.168.1.1/16 -auto
注:-socks5 支持端口扫描+url扫描全部。还有一个-proxy选项,是设置http代理,它只支持“url目标”(例如-hf文件中的url,-std标准输入的url),不支持用于端口扫描,毕竟http代理本身就不能直接路由tcp/udp流量。
如果 socks5 服务端需要账号密码认证(自建的 gost、3proxy 等工具常开启认证),把账号密码写进地址即可:
fscanx -socks5 socks5://user:[email protected]:1080 -h 192.168.1.1/16 -auto
格式为 socks5://用户名:密码@ip:端口,端口扫描、协议识别、web扫描、poc、爆破均支持带认证的 socks5。密码里含有 @、: 等特殊字符时一般可以直接写,如果遇到解析问题再用 URL 转义写法(@ 写成 %40,: 写成 %3A)。
(6)漏洞扫描
fscanx -socks5 socks5://127.0.0.1:1080 -h 192.168.1.1/24 -poc -p 80,443,8080
(7)协议弱口令爆破
fscanx -socks5 socks5://127.0.0.1:1080 -h 192.168.1.1/24 -br -p 21,22,445,3306,6379
fscanx -socks5 socks5://127.0.0.1:1080 -h 192.168.1.1/24 -br -p 445 -user administrator,admin -pwd 123456,admin
2025.10.11。删除了一些在实战中遇到的垃圾指纹;修复了一个原版fscan就存在的bug,探测时会漏掉部分http响应体为空的资产。
2025.09.14。增加与masscan高速扫描联动的功能,具体如下:
增加-std选项。使用后可从标准输入中获取masscan的输出结果,解析ip和端口直接进行端口扫描、协议识别、密码爆破、poc测试等。斗宗强者masscan恐怖如斯,不接受反驳,底层无状态异步(收发双工)遥遥领先,Feistel网络做海量扫描地址和端口的随机化,根本不会遗漏资产且性能消耗极低,懂的都懂。网上现在有一些不靠谱的测试甚嚣尘上,实际上测试方法有问题,有时间我写篇文章为masscan正名。
使用方法,①下载我修改的masscan程序,h
暂无开放 Issues,或尚未同步最近议题。