ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

网络空间测绘入门:FOFA语法、API调用与实战查询技巧

网络空间测绘入门:FOFA语法、API调用与实战查询技巧 1. 网络空间测绘到底在测什么很多人第一次听到网络空间测绘这个词脑子里浮现的是地图、卫星、经纬度那一套。其实它跟地理测绘的逻辑很像只不过测绘的对象从山川河流变成了互联网上的设备、服务和资产。地理测绘告诉你哪座山有多高、哪条河有多宽网络空间测绘告诉你某个网段里有多少台主机在线、开了哪些端口、跑着什么服务、用的什么中间件、证书是谁签发的。FOFA 就是做这件事的工具之一。它的核心能力是你给它一个查询条件它从持续扫描和采集的海量数据里把匹配的资产信息返回给你。这些信息包括 IP、端口、协议、banner、标题、证书、favicon 哈希等等。对于做资产梳理、攻击面管理、漏洞影响面评估的人来说这玩意儿用好了能省掉大量手工探测的时间。但我要先把话说在前面FOFA 这类工具的使用必须建立在合法合规的前提下。你只能查你自己拥有的资产或者你获得了明确书面授权的目标。未经授权对他人资产进行扫描和探测在很多地方是明确违法的。这篇文章讲的是技术方法和使用思路前提是你有正当的授权和合法的使用场景。这篇文章适合谁看如果你是刚接触网络空间测绘的安全从业者、运维人员、或者做资产管理的技术人员之前只用过简单的 IP 查询对 FOFA 的语法体系和 API 调用还比较陌生那这篇内容会对你有直接帮助。我会从语法规则讲到实战查询再讲到 API 集成和 favicon 这类进阶用法中间穿插我自己踩过的坑和实际验证过的技巧。2. FOFA 语法体系的底层逻辑2.1 字段、运算符与查询表达式的基本构成FOFA 的查询语法本质上是一套结构化的表达式系统。它的基本单元是字段值这样的键值对比如domainexample.com、port443、title登录。多个条件之间用逻辑运算符连接表示与||表示或!表示不等于。这套语法设计得很直观学过任何一门编程语言的人都能快速上手。但直观归直观真正用好需要理解每个字段的语义边界。举个例子domain字段匹配的是资产关联的域名它跟host字段不是一回事。host匹配的是主机名或 IP而domain更偏向于从证书、页面内容等维度提取出来的域名信息。你在查domainedu.com的时候返回的结果可能包含各种子域名下的资产但如果你用hostedu.com匹配逻辑就完全不同了。再比如status_code字段它对应的是 HTTP 响应状态码。status_code200只返回正常响应的资产这个在过滤无效目标时非常有用。但要注意不是所有资产都会返回 HTTP 状态码非 HTTP 服务比如纯 TCP 服务就不会有这个字段的值。我整理了一个常用字段的对照表方便你快速查阅字段名含义典型用法注意事项domain关联域名domainexample.com匹配范围比 host 广host主机名或 IPhost192.168.1.1精确匹配主机标识port端口号port80,443支持逗号分隔多端口protocol协议类型protocolhttps如 http、https、ssh 等title页面标题title管理后台仅对 HTTP 类服务有效headerHTTP 响应头headerServer: nginx匹配响应头内容body页面正文bodyPowered by数据量大时慎用cert证书信息certexample.com匹配 SSL 证书内容icon_hashfavicon 哈希icon_hash-123456用于识别特定应用status_codeHTTP 状态码status_code200仅 HTTP 服务有值2.2 逻辑组合与括号优先级单个条件谁都会写真正体现水平的是多条件组合。FOFA 支持、||和括号()来控制逻辑优先级。这里有个很多人容易忽略的点括号的使用会直接影响查询结果的准确性。举个实际例子。假设你想查某个域名下所有返回 200 状态码的 80 或 443 端口资产。如果你写成domainexample.com status_code200 port80 || port443按照逻辑运算符的优先级的优先级高于||所以这个表达式实际等价于(domainexample.com status_code200 port80) || (port443)这意味着所有 443 端口的资产都会被返回不管它是不是属于 example.com也不管状态码是不是 200。这显然不是你想要的结果。正确的写法应该是domainexample.com status_code200 (port80 || port443)提示写多条件查询时只要涉及||就养成加括号的习惯。宁可多写几个括号也不要让逻辑优先级替你做决定。2.3 引号、转义与常见语法错误FOFA 的字符串值需要用双引号包裹。如果你的查询值本身包含双引号就需要转义。比如查标题里带引号的内容写法是titlesay \hello\。这个在构造 API 请求的时候尤其要注意因为 JSON 本身也有转义规则两层转义叠加很容易出错。另一个常见错误是字段名拼写。FOFA 的字段名是固定的拼错了不会报错只会返回空结果。比如把status_code写成statuscode查询不会报语法错误但结果一定是空的。这种静默失败最坑人因为你以为是目标不存在实际上是查询写错了。我的建议是每次写复杂查询之前先在 FOFA 的 Web 界面里测试一遍确认返回结果符合预期之后再把查询语句复制到 API 调用里。Web 界面有语法高亮和实时预览能帮你快速发现拼写和逻辑问题。3. 从零构造一条可用的查询语句3.1 明确查询目标你要找的到底是什么写查询语句之前先问自己一个问题我到底要找什么这个问题听起来很废话但我见过太多人上来就堆条件结果查出来的东西根本不是自己想要的。查询目标通常可以归为几类资产归属类查某个组织、某个域名下的所有资产。关键词是归属你需要用domain、cert、icon_hash这类字段来圈定范围。服务特征类查跑了某种特定服务的资产。比如所有开了 Redis 的、所有用了某版本 Nginx 的。关键词是特征你需要用protocol、header、body这类字段来匹配。漏洞影响类查受某个漏洞影响的资产范围。关键词是版本和组件通常需要结合body、header里的版本信息来匹配。暴露面类查不该暴露在公网上的管理后台、数据库、调试接口。关键词是敏感路径和默认页面用title、body来匹配。目标不同查询策略完全不同。资产归属类查询要尽量宽先把范围圈住再逐步收窄服务特征类查询要尽量准条件要卡死漏洞影响类查询要结合具体组件的版本特征暴露面类查询则要关注那些不该出现的页面特征。3.2 逐步收窄从宽泛到精确的查询策略我个人的习惯是先宽后窄。先用一个宽泛的条件把大范围圈出来看看结果数量和分布然后逐步加条件收窄。比如我要查某个学校域名下的所有 Web 资产。第一步domainxxx.edu.cn这一步会返回大量结果可能几千上万条。然后我加上状态码过滤domainxxx.edu.cn status_code200数量会降下来一些。再加端口限制domainxxx.edu.cn status_code200 (port80 || port443)再进一步如果我只关心有登录页面的资产domainxxx.edu.cn status_code200 (port80 || port443) (title登录 || title后台 || title管理)这样一步步收窄每一步都能看到结果数量的变化方便你判断条件是否合理。如果某一步加完之后结果直接变成 0那说明这个条件太苛刻了需要回退调整。注意FOFA 的免费账户有查询结果数量限制翻页也有次数限制。所以在做宽泛查询的时候先看结果总数如果数量太大直接加条件收窄不要浪费翻页次数。3.3 用 favicon 哈希锁定特定应用favicon 是网站标签页上那个小图标。很多应用框架、管理系统、甚至特定厂商的设备都会使用固定的 favicon。这就意味着favicon 的哈希值可以作为资产的指纹来使用。FOFA 的icon_hash字段就是干这个的。你只需要拿到目标应用的 favicon 哈希值就能在全球范围内找到所有使用同一个 favicon 的资产。这个技巧在资产梳理和影响面评估中非常实用。获取 favicon 哈希的方法有几种。最简单的是用 FOFA 自带的 favicon 计算工具上传图标文件就能得到哈希值。也可以用 Python 脚本自己算import mmh3 import requests import base64 def get_favicon_hash(url): response requests.get(url, timeout10) favicon base64.encodebytes(response.content) hash_value mmh3.hash(favicon) return hash_value # 示例 hash_val get_favicon_hash(https://example.com/favicon.ico) print(ficon_hash\{hash_val}\)拿到哈希值之后查询就很简单了icon_hash-1234567890这个查询会返回所有 favicon 哈希匹配的资产。我实测下来对于使用默认 favicon 的常见管理系统这个方法的召回率相当高。但要注意有些站点会动态生成 favicon或者 favicon 内容会随版本变化这种情况下哈希匹配就不太可靠了。另外favicon 哈希有正有负查询的时候要带上负号如果是负数的话。我见过有人把负号漏了结果查不到任何东西排查半天才发现是符号问题。4. API 调用把查询能力集成到工作流里4.1 API 认证与请求构造FOFA 提供了 RESTful API可以把查询能力集成到自己的脚本或平台里。API 调用需要认证认证方式是在请求头或请求参数里带上你的 API Key。具体用哪种方式取决于你调用的接口版本建议以官方文档为准。一个典型的查询请求核心参数包括qbase64Base64 编码后的查询语句fields指定返回哪些字段size每页返回数量page页码为什么查询语句要 Base64 编码因为查询语句里可能包含特殊字符引号、括号、空格等直接放在 URL 参数里容易出问题。Base64 编码之后就是一串安全的字符不会跟 URL 语法冲突。用 Python 构造请求的基本流程import base64 import requests query domainexample.com status_code200 qbase64 base64.b64encode(query.encode()).decode() params { qbase64: qbase64, fields: host,port,title,protocol, size: 100, page: 1 } headers { Authorization: Bearer YOUR_API_KEY } response requests.get(https://fofa.info/api/v1/search/all, paramsparams, headersheaders) data response.json() print(data)这段代码的关键点在于qbase64的构造。很多人第一次调 API 的时候直接把查询语句塞进参数里结果要么报错要么返回空。原因就是没有做 Base64 编码。4.2 分页、限流与错误处理API 调用有两个绕不开的问题分页和限流。分页方面FOFA 的 API 通常有单页最大返回数量的限制。你需要通过page参数逐页获取。我的做法是先用size1发一个请求从返回的 total 字段里拿到结果总数然后算出需要翻多少页再循环获取。限流方面不同账户等级的调用频率限制不同。免费账户的额度很有限付费账户会宽松一些。如果你在循环里连续发请求很容易触发限流返回 429 状态码。处理方式很简单在每次请求之间加一个time.sleep()给服务器一点喘息时间。import time all_results [] page 1 while True: params[page] page response requests.get(api_url, paramsparams, headersheaders) if response.status_code 429: print(触发限流等待 60 秒...) time.sleep(60) continue data response.json() results data.get(results, []) if not results: break all_results.extend(results) page 1 time.sleep(2) # 每页之间间隔 2 秒 print(f共获取 {len(all_results)} 条结果)错误处理方面除了 429 限流还要关注 401认证失败和 400请求参数错误。401 通常是 API Key 不对或过期了400 多半是查询语句编码有问题或者字段名写错了。建议在代码里把响应状态码和错误信息都打印出来方便排查。4.3 把 API 查询结果落地到本地分析拿到 API 返回的数据之后通常需要做进一步分析。我一般会把结果存成 CSV 或 JSON然后用 pandas 做统计和可视化。import pandas as pd # 假设 all_results 是 API 返回的结果列表 df pd.DataFrame(all_results, columns[host, port, title, protocol]) # 按端口统计 port_stats df[port].value_counts() print(port_stats) # 按协议统计 protocol_stats df[protocol].value_counts() print(protocol_stats) # 导出 df.to_csv(fofa_results.csv, indexFalse)这样做的好处是你可以把 FOFA 的查询结果跟其他数据源做关联分析。比如把资产列表跟漏洞库做匹配快速定位哪些资产可能受某个漏洞影响。或者把资产列表跟 CMDB 做比对找出未登记的影子资产。5. 实战场景拆解5.1 场景一给定目标的全量资产收集这是最常见的需求。你拿到了一个授权目标需要把它的所有相关资产梳理出来。第一步从主域名入手domaintarget.com第二步看看返回结果里有没有明显的子域名规律然后逐个扩展domaintarget.com || domainsub1.target.com || domainsub2.target.com第三步结合证书信息补充遗漏certtarget.com证书查询能覆盖到一些域名解析不直接指向目标、但证书里包含目标域名的资产。这一步经常能发现一些隐藏的资产。第四步如果目标有特定的 favicon用 icon_hash 再补一轮icon_hash-xxxxxxx把这四步的结果合并去重基本就能拿到比较完整的资产列表了。5.2 场景二特定组件的暴露面排查假设你需要排查某个内网管理系统的公网暴露情况。这个系统有固定的登录页面标题和 favicon。titleXX管理系统 status_code200或者icon_hash-xxxxxxx status_code200如果结果太多可以加上国家或地区限制如果 FOFA 支持的话或者加上端口限制缩小范围。这个场景的关键是特征要准。标题和 favicon 是最可靠的两个特征。如果标题可能被修改那就以 favicon 为准如果 favicon 也可能被替换那就结合 body 里的特定字符串来匹配。5.3 场景三证书与域名关联的资产发现证书是资产发现的一个金矿。很多资产可能没有直接解析到目标域名但它的 SSL 证书里包含了目标域名信息。通过cert字段查询能把这些资产挖出来。certtarget.com这个查询会返回所有证书中包含 target.com 的资产。包括那些用通配符证书的、用多域名证书的、甚至证书已经过期但服务还在跑的。我实际用下来证书查询的召回率比 domain 查询要高一些但精确度会低一点。因为有些证书可能被多个组织共用或者证书里的域名跟实际业务没有直接关系。所以证书查询的结果需要人工确认一遍。6. 那些文档里不会写的踩坑经验6.1 查询结果为空时的排查顺序查询返回空结果是最让人抓狂的情况。我的排查顺序是这样的检查字段名拼写。这是最高频的错误。status_code不是statuscodeicon_hash不是iconhash。检查引号。字符串值必须用双引号包裹漏了引号查询会失败。检查逻辑运算符。和||别写反了括号别漏了。检查值本身是否存在。比如你查port8080但目标根本没开 8080那结果当然是空的。先用宽泛条件确认目标有数据再逐步加条件。检查账户额度。免费账户有查询次数限制额度用完了也会返回空结果。这个顺序是我踩了无数次坑之后总结出来的按这个顺序排查90% 的问题都能快速定位。6.2 API 调用中的编码陷阱API 调用里最容易出问题的是编码。查询语句要 Base64 编码但 Base64 编码之前查询语句本身的字符集也要注意。如果你的查询语句里包含中文比如title登录要确保编码方式是 UTF-8。query title登录 qbase64 base64.b64encode(query.encode(utf-8)).decode(utf-8)如果漏了.encode(utf-8)Python 3 里会直接报错。如果用了其他编码方式服务端解码出来就是乱码查询结果自然不对。还有一个坑是 URL 编码。虽然qbase64已经是安全字符了但如果你手动拼接 URL还是要用requests的params参数来传让它自动处理 URL 编码。手动拼 URL 很容易漏掉某些字符的编码。6.3 免费额度与付费策略的取舍FOFA 免费账户的额度对于轻度使用是够的但如果你要做批量资产梳理免费额度很快就会用完。付费账户的额度更大API 调用频率也更高。我的建议是先用免费额度把查询语句调通确认逻辑没问题之后再用付费额度跑批量任务。不要一上来就用付费额度去试错那样很浪费。另外FOFA 的额度计算方式跟返回结果数量有关。你查一次返回 100 条结果和查一次返回 1 条结果消耗的额度可能不一样。所以在做宽泛查询之前先用size1探一下结果总数如果数量太大先加条件收窄避免浪费额度。7. 把 FOFA 变成日常工具的几个习惯我用 FOFA 这几年慢慢养成了几个习惯分享出来供参考。第一个习惯是建查询语句库。把常用的查询语句存到一个文本文件里按场景分类。比如资产归属类、服务特征类、暴露面类各存几条。下次遇到类似需求直接复制修改不用从头写。第二个习惯是先 Web 后 API。任何查询语句先在 Web 界面里跑一遍确认结果符合预期再放到 API 脚本里。Web 界面有实时预览能帮你快速发现语法问题。第三个习惯是结果落地。API 返回的数据不要只看一眼就完了存成 CSV 或 JSON方便后续做关联分析和趋势对比。我一般会按日期建目录每次查询的结果都存档时间长了就能看出资产的变化趋势。第四个习惯是定期复查。资产是动态变化的今天查的结果下个月可能就不一样了。对于重要的目标我会设置定期查询任务每周或每月跑一次对比结果差异及时发现新增或消失的资产。这些习惯看起来简单但坚持下来能显著提升效率。工具本身只是工具真正产生价值的是你围绕它建立起来的工作流。
返回列表