1. 为什么FOFA的“官方能力”在实战中不够用
1.1 从一次资产梳理说起
先讲个我自己的真实经历。去年做一次授权范围内的资产盘点,甲方给了大概四千多个IP段,要求把其中开放了Web服务的资产全部找出来,并且关联对应的域名、标题、指纹、中间件版本。我第一反应就是打开FOFA,把IP段拆开一条条查。结果查了不到两个小时就放弃了——不是FOFA查不到,而是Web端这个交互形态根本就不是为“大批量处理”设计的。
单页最多显示几十条,翻页翻到手指发酸;每换一个IP段就要重新输入语法、重新筛选;好不容易把结果存下来,发现导出的时候还有条数上的硬限制;想要把多个查询结果合并成一张表,还得靠手工去重。整个过程里,真正花在分析上的时间可能只有两成,剩下八成全在重复劳动。
这不是FOFA本身不好用,而是它的产品定位决定了:Web端优先解决的是“查得准”,而不是“查得多”。批量场景天然需要一个中间层,把查询、导出、筛选这些动作做成可编排、可复用、可断点续传的流水线。我当时就意识到,如果想要舒服地把FOFA用好,必须有个能站在FOFA肩膀上的工具层。
1.2 官方API的条数限制与成本门槛
很多人一提到批量查询就想直接调FOFA API,这个思路没问题,但实际操作会撞上几堵墙。
第一堵墙是套餐额度。FOFA的API调用是按积分或者按条数计费的,不同会员等级能拉取的总条数差得很远。一个普通账号可能分分钟就把额度耗尽,而真正高质量的资产测绘往往需要几万条、几十万条的返回量。第二堵墙是单次查询的上限。API单次返回的记录数有上限,超出部分并不是“一次查完”,而是需要“翻页拉取”。翻页本身有频率限制,如果拉得太快,服务端会直接拒绝响应。
这两堵墙叠加起来,就出现了一个很尴尬的局面:明明FOFA的数据库里躺着大量有价值的测绘数据,但在批量场景下你一次能真正拿出来的只是很小一部分。市面上后来出现的各种“FOFA导出工具”,本质上都是在解决同一个问题——怎么在合规、限速的前提下,把尽量多的查询结果完整地落到本地。
1.3 FOFATOTO这个工具解决的正是这一层需求
FOFATOTO这个名字听起来像个玩具,但其实是“FOFA Toolkit for Operation”的缩写拼出来的,当初起名的时候我差点把它叫成“FOFA Helper”,后来觉得太普通,就用了TOTO这个小后缀。
这个工具定位非常明确:它是FOFA的增强层,不是替代品。它不改变FOFA的查询语法,也不替你做安全分析,它只做三件事——深度导出、批量查询模式、查询结果的批量筛选。换句话说,FOFA负责“搜出来”,FOFATOTO负责“把搜出来的东西完整、高效、干净地整理好”。
这篇内容我打算从原理到实操完整讲一遍,包括它内部的导出策略是怎么设计的、批量查询的并发节奏怎么调、筛选模块是怎么处理脏数据的。我不会只贴几个命令就完事,而是把每个设计背后的考量和踩过的坑都讲清楚。不管你是做攻防演练、日常巡检还是外部资产暴露面排查,这套思路都能直接借鉴。
2. 深度导出的核心逻辑:翻页、断点续传与增量合并
2.1 单页查询的边界与翻页机制的实现
在深入FOFATOTO的导出模块之前,先搞清楚一个基础问题:FOFA的查询结果到底是怎么分页的。
Web端和API端的逻辑一致,每次查询都会返回一个总数,然后是当前页的记录列表。如果你在Web端一页页点,浏览器发出的就是带有页码参数的请求。FOFATOTO做的事情和手动翻页本质相同,但最大的区别在于:工具会动态解析返回结果里的total字段,自动计算出总页数,然后一次性把后续页面的请求任务挂进队列。
这里有一个容易被忽略的陷阱:如果查询条件在翻页过程中没有固定下来,而是模板里有变量(比如批量查询IP段时把IP作为变量嵌进去),那么每一页请求的返回总量可能是不同的。实际开发中我见过不少人在写这类工具时,直接用第一个请求的total去做循环,结果数据量一大,后面的页就出现重复或缺失。
FOFATOTO的做法是:每翻一页都重新读取一次当前查询条件下的返回结果,校验当前累计条数是否和第一次拉取时的total一致,不一致就自动调整页数。这种“实时校验”的逻辑在初期增加了不少代码量,但换来了导出数据的完整性。
2.2 断点续传:批量任务中断之后不用从头再来
深度导出的第二个痛点就是跑到一半断了。断的原因很多:网络抖动、目标服务器限流、本地内存不够、甚至笔记本合盖休眠。如果每次中断都要重新开始,那几千页的查询任务几乎不可能稳定跑完。
FOFATOTO在任务编排上引入了一个类似“书签”的机制。每个查询任务在启动的时候,会生成一个任务ID,同时把任务状态写入本地数据库。每成功拉取一页,就记录下当前页码、已获取条数、下一条游标位置。任务中断后重新启动,工具会自动读取任务状态,接着上次的位置继续拉取,而不是从第1页重新开始。
这个设计在长周期任务里特别重要。我自己排过一个大任务,要拉取一个行业所有开放SSH服务的资产,总量接近二十万条。中间因为网络问题断了五六次,但因为有断点续传,最终没有多花一次重复请求的代价。你可以算一笔账:如果每次中断都从头再来,浪费的不只是时间,还有API额度。
2.3 导出格式与字段选择的经验
深度导出的最终出口是文件。很多初学工具的人觉得导出就是把结果打印成表格而已,但一旦数据量达到几万条之后,文件格式和字段选择就变成影响下游分析效率的关键因素。
FOFATOTO默认支持JSON、CSV两种导出格式。JSON适合程序化的后续处理,CSV适合直接打开Excel做筛选。但有两个细节是我建议所有使用者特别留意的:
第一,字段不是越多越好。FOFA的返回字段里有ip、port、protocol、domain、title、cert、icp、fid等等,全量导出会让文件体积暴涨。比如cert和icp这类长字段在CSV里还会引入换行符,导致表格错位。更好的做法是先导出核心字段,确认没有遗漏后再按需补齐扩展字段。
第二,建议固定一个“导出规范”:第一列永远是IP,第二列永远是端口,第三列协议,后面才是域名、标题、指纹等信息。这样无论导多少次,合并起来都干净。FOFATOTO在导出模块里预置了几套字段模板,但我也强烈建议你根据自己的业务场景自定义一套,长期用下来省心得多。
我个人的习惯是:资产发现阶段只导核心字段,确认目标范围后再跑一轮带指纹和证书信息的深度导出。这个节奏比一次性导全量要稳,因为后期筛选时你会发现,字段太多反而不容易聚焦。
3. 批量查询模式:不止是“把多个查询拼在一起”
3.1 批量输入的几种常见形态
批量查询听起来简单,无非就是把多个查询语句循环发送。但在真实场景里,输入源很少是同一形态,FOFATOTO针对几个高频场景分别做了处理。
第一种是IP段列表。用户提供一段段CIDR,工具自动扩展成单IP,再拼接到查询语句里。这里有个性能优化点:FOFA支持CIDR形式的查询语法(比如ip="1.2.3.0/24"),完全没有必要把每个IP单独拆出来查一次,直接保留CIDR可以让查询参数缩短一个量级,返回速度也更快。
第二种是URL/域名列表。批量核查自己资产库里的域名时,会有一个和IP查询完全不同的语法组装方式。FOFA对域名可以做host或domain字段的精确匹配,但“裸域名”和“带子域名的完整域名”使用的语法不同,工具必须自动识别输入项的格式,才能生成正确的查询表达式。
第三种是“从已有导出文件反查”,也就是把上一个任务的CSV结果读进来,提取出符合条件的项作为下一次查询的输入。这个场景在做“一层资产到二层资产”的关联分析时非常有用。比如你从证书指纹里提取了一堆序列号,然后拿这批序列号去反查IP,工具只需要解析原始文件的某个列,就能生成批量查询任务。
3.2 任务编排中的并发节奏控制
批量查询最大的坑不是语法写错,而是请求频率失控。FOFA服务端对频率是有限制的,一旦短时间内请求量过大,不管你是Web端还是API端,都会返回异常或者直接封禁一段时间。市面上很多批量工具被诟病“用一次就废”,多半就是并发控制没做好。
FOFATOTO在并发设计上走的是保守路线:默认并发数只有3到5个线程,每个线程执行完一次查询后,会随机休息200到800毫秒。你可能觉得这太慢了,但拉长到几万条数据的维度去看,在稳定性和速度之间,稳定永远排第一。
更细一点的节奏控制是对“大查询”和“小查询”区别对待。查询返回总量大的任务(比如几千页),单次请求耗时本身就是秒级,并发调高一两个问题不大;而返回量小的任务,一眨眼就查完了,反而要在任务与任务之间加一个稍长的间隔,避免连续快节奏请求触发限流。
我自己的经验是:宁可让一个一万条的任务跑二十分钟,也不愿意被封掉三小时。做这类外部数据拉取,第一原则永远是“慢就是快”。
3.3 任务的可视化与失败重试
批量任务的另一个隐藏需求是“看得见进度”。一个任务有几万条数据,一个循环在后台跑着,如果界面上一片死寂,用户根本不敢安心离开电脑。FOFATOTO在任务列表里维护了清晰的状态机:等待中、运行中、部分完成、已完成、失败、已取消。
每个任务运行时会显示当前累计获取条数、总条数、剩余页数、平均请求耗时、最近一次成功时间。出现异常时,工具会区分是“可重试的临时错误”还是“不可恢复的参数错误”。临时错误自动进入重试队列,采用指数退避的策略,第一次失败等2秒,第二次4秒,第三次8秒,最多重试5次。而参数错误直接标记为失败,同时把原始查询语句和错误信息一并展示,让你能迅速定位是哪一条查询写错了。
这个失败重试机制在长周期任务里起到了关键作用。有一次我在批量查询过程中遇到过目标服务端短暂抖动,连续三个请求返回超时。如果没有重试机制,这三个查询就会静默丢失,最终导出的数据就是一个残缺集,而你甚至不知道丢在哪里。
4. 批量筛选:从“查得到”到“筛得对”
4.1 筛选操作的四个维度
FOFA查询语法可以做很多前置过滤,但有些筛选动作必须在拿到结果之后再做。原因有两个:一是某些字段的正则提取在查询阶段做不到(比如从title里提取特定版本号),二是有些筛选依赖于多条结果合并之后才能判断(比如跨IP去重)。
FOFATOTO把批量筛选拆成四个维度,对应不同的使用场景:
- 内容维度:基于返回字段的内容做过滤。比如标题中包含“登录”或“管理”的资产单独抽出来;端口为443或8443的归为一组;状态码(如果存在)为200/301/302的优先关注。
- 正则维度:用一个自定义正则去匹配文本字段,匹配成功的留下,不成功的剔除。这在提取特定类型的URL路径或从title中识别设备型号时非常实用。
- 属性维度:比如按IP是否属于内网段来区分资产,或者按证书有效期判断是否即将过期。属性维度本质上是“对FOFA返回数据做二次分类”。
- 去重维度:对IP+端口做精确去重,对IP做单条保留,或者对域名做主域名去重。
四个维度可以叠加使用,形成一条筛选流水线。实操中我最常用的是“内网误报剔除 + IP去重 + 端口/协议聚合”的组合,基本能覆盖大多数资产盘点的需求。
4.2 去重逻辑与资产归属判定
去重这件事比想象中复杂。FOFA同一台服务器如果开放多个端口,会有多条记录;同一IP对应多个域名,也会有多条记录。如果只是简单地对“IP”这个字段去重,会丢掉大量有效资产;如果只对“IP+端口”去重,域名信息合并又需要额外处理。
FOFATOTO内置了几种去重级别,从粗到细分别是:IP级别去重(同一个IP只保留一条)、IP+端口级别去重(同一个服务的多条记录合并)、主域名级别去重(多个子域名归并为一个主域名)。
按照实际使用频率排序,IP+端口级别去重用得最多,因为它在“保留关键资产”和“减少重复”之间最平衡。主域名级别去重更多用在报告阶段,用来向上层汇报“我们总共发现了多少个独立站点”。
关于资产归属判定,我的做法是会结合FOFA的domain字段和cert里的CN字段做一次交叉验证:如果一条记录的domain为空,但cert的CN字段里包含一个和已发现主域名相似的域名,那大概率属于同一个组织的资产。这种“弱关联”逻辑虽然不能100%准确,但在快速扩展资产范围时能帮上大忙。
4.3 筛选结果集的标记与标签体系
筛选如果只有“留下”“剔除”二选一,很多场景就不够用。实际做安全测试时,更需要的是“标记不同重要程度”,而不是简单删掉某条数据。
FOFATOTO的筛选模块支持自定义标签规则。比如你可以设定:title包含“后台”或者“admin”的资产自动打上“后台管理”标签;端口为22或3389的记录标记为“远程管理入口”;标题包含“test”“dev”“demo”等关键词的资产标记为“疑似测试环境,需人工确认”。这些标签会写进导出文件里,作为单独的一列存在。
整套标签体系相当于构建了一个简易的资产分类器。你不必在拿到导出数据后再去Excel里写VLOOKUP,也不用到Burp或者Nuclei里再去筛选目标范围。查询结果落地的那一刻,资产已经按照你的规则分好了类。这一点在时间紧迫的攻防任务里价值极高。
5. 一次完整实战:从批量查询到清洗入库的标准流程
5.1 任务设计与语法拆解
空讲功能太抽象,我拿一个典型需求串一遍整个流程:甲方给了一个“某支付行业”的关键词列表,要求梳理出所有疑似相关并且暴露在公网的Web资产。
第一步,把关键词列表扩展成FOFA查询语句。关键词可以是“支付”“结算”“清分”“收单”等;每条语句用title或body字段匹配,同时加一层|或的关系。FOFATOTO的批量任务界面允许每一行一条独立查询,也支持把多行查询组合成一个任务组。我用的是“每行一条独立查询、全部输出后统一合并”的方式。
第二步,为每个查询设定返回条数上限。默认不设上限,但考虑到整体任务量,我会先把每个关键词的上限定在5000条,跑完看数据分布后再决定是否扩量。
第三步,配置导出字段。这轮阶段我只要ip、port、protocol、host、title、domain六个字段,不做证书和指纹采集,因为第一步只是想快速圈定范围。
5.2 清洗、合并与最终的字段归一化
批量查询跑完,会得到若干份结果文件。这里面重复情况非常严重,同一套系统可能被多个关键词同时命中。这时候就需要进入FOFATOTO的批量筛选模块做数据清洗。
清洗步骤我一般固定走四步:
- 剔除状态码异常的资产记录(401、403这类先保留但打上标签)。
- 对IP+端口做精确去重,把重复项合并,保留所有命中的关键词。
- 对标题和域名做一次正则过滤,剔除明显的广告、验证码、CDN拦截页面。
- 对最终结果做主域名归类,输出一份按主域名维度统计的概览表。
字段归一化是最后一步。来自多条查询语句的数据,可能有的字段值为空、有的带空格、有的换行符混入,全部清洗成统一格式后再入库。放进数据库后,我习惯建一个“原始记录表”和一个“有效资产表”,“有效资产表”里的行才作为后续渗透测试或安全检查的目标集合。
5.3 验证结果质量的两个小方法
数据清洗完,别急着用,先花五分钟验证质量。
第一个方法:抽出20条结果,人工用FOFA Web端重新查询验证。如果每一条都能在Web端找到对应记录,说明导出和筛选中没有丢数据或改数据。
第二个方法:拿清洗后的有效资产表里的IP集合,和甲方提供的已知资产库做交叉比对,看覆盖率。如果覆盖率偏低,多半是查询关键词覆盖不够,需要补充查询语句;如果覆盖率超过了甲方已知资产库,那说明你的资产梳理比他们还全,这份数据可以直接反哺给甲方。
这两种验证手段虽然原始,但非常有效。完成这一步后,这批数据才真正进入到“可用”状态。
6. 踩过的坑、边界条件与最重要的三条提醒
6.1 最大的一次翻车:忘了限速导致账号被限制
我早期用批量工具的时候翻过一次大车。当时排了一个夜间任务,本地放着跑了整晚,第二天早上起来一看——前二十分钟数据正常,二十分钟后所有请求返回异常,账号被临时限制了。原因很简单:并发调得太高,请求间隔设得太短,加上任务量集中在同一时段爆发式增长,直接把频率限制顶穿了。
那次之后我总结了两条铁律:第一,所有批量任务必须默认走慢速模式,除非你有十足的把握知道当前账号的限速阈值;第二,每次大规模拉取前先跑一个100条的小任务试试水温,确认稳定后再上量。这两条规则现在写进了我自己所有自动化脚本的最前面。
6.2 数据污染问题:别把CDN、云厂商的跳转页面当真实资产
FOFA返回的数据里,有很大一部分是云厂商的默认页面、CDN的拦截页、WAF的验证页面。这些记录虽然在测绘结果里,但它并不代表真实业务系统。如果直接拿这些记录去做后续测试,会浪费大量时间在无效目标上。
处理方式是在筛选模块里维护一个“常见中间页指纹库”,把市面上主流CDN、云WAF、安全厂家的拦截响应特征加进去。每次清洗数据时,命中指纹库的自动标记为“疑似云防护或CDN页面”,需要人工复查。拿真实业务资产的准确率,主要就是靠这一步提升的。
6.3 合规边界与本工具的正确打开方式
最后必须提一句合规。FOFATOTO这类工具的价值在于让资产测绘更高效,但前提是查询目标必须是自己有权限测试的资产,或者经过了明确授权的资产范围。无论是FOFA本身的使用,还是周边工具的批量拉取,都应当遵守目标平台的服务条款和当地法律法规。
使用这类工具时,我建议在本地保留完整的任务日志,包括查询语句、查询时间、导出记录。这不仅是自己复盘的需要,在特殊情况下也是证明操作合规的重要依据。做安全这行,能力越强越要谨慎,工具本身是中性的,但操作者必须清楚边界在哪里。
如果能把批量查询的节奏控好、筛选规则沉淀成自己的方法论、并且始终守住合规底线,FOFATOTO这样的工具会成为你日常工作中很顺手的一环。它不复杂,但确实能让测绘这件事从“手动搬砖”变成“半自动流水线”。