Add files via upload

This commit is contained in:
公明
2025-12-07 15:13:51 +08:00
committed by GitHub
parent 1a2e14aa88
commit 7243b79877

285
tools/fofa_search.yaml Normal file
View File

@@ -0,0 +1,285 @@
name: "fofa_search"
command: "python3"
args:
- "-c"
- |
import sys
import json
import base64
import requests
import os
# ==================== FOFA配置 ====================
# 请在此处配置您的FOFA账号信息
# 您也可以在环境变量中设置FOFA_EMAIL 和 FOFA_API_KEY
FOFA_EMAIL = "" # 请填写您的FOFA账号邮箱
FOFA_API_KEY = "" # 请填写您的FOFA API密钥
# ==================================================
# FOFA API基础URL
base_url = "https://fofa.info/api/v1/search/all"
# 解析参数从JSON字符串或命令行参数
def parse_args():
# 尝试从第一个参数读取JSON配置
if len(sys.argv) > 1:
try:
config = json.loads(sys.argv[1])
return config
except (json.JSONDecodeError, TypeError):
# 如果不是JSON使用传统的位置参数方式
pass
# 传统位置参数方式(向后兼容)
# 注意email 和 api_key 已从参数中移除,现在从配置中读取
config = {}
if len(sys.argv) > 1:
config['query'] = sys.argv[1]
if len(sys.argv) > 2:
try:
config['size'] = int(sys.argv[2])
except (ValueError, TypeError):
pass
if len(sys.argv) > 3:
try:
config['page'] = int(sys.argv[3])
except (ValueError, TypeError):
pass
if len(sys.argv) > 4:
config['fields'] = sys.argv[4]
if len(sys.argv) > 5:
val = sys.argv[5]
if isinstance(val, str):
config['full'] = val.lower() in ('true', '1', 'yes')
else:
config['full'] = bool(val)
return config
try:
config = parse_args()
# 从配置或环境变量获取email和api_key
email = os.getenv('FOFA_EMAIL', FOFA_EMAIL).strip()
api_key = os.getenv('FOFA_API_KEY', FOFA_API_KEY).strip()
query = config.get('query', '').strip()
if not email:
error_result = {
"status": "error",
"message": "缺少FOFA配置: emailFOFA账号邮箱",
"required_config": ["email", "api_key"],
"note": "请在YAML文件的FOFA_EMAIL配置项中填写您的FOFA账号邮箱或在环境变量FOFA_EMAIL中设置"
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
if not api_key:
error_result = {
"status": "error",
"message": "缺少FOFA配置: api_keyFOFA API密钥",
"required_config": ["email", "api_key"],
"note": "请在YAML文件的FOFA_API_KEY配置项中填写您的API密钥或在环境变量FOFA_API_KEY中设置。API密钥可在FOFA个人中心获取: https://fofa.info/userInfo"
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
if not query:
error_result = {
"status": "error",
"message": "缺少必需参数: query搜索查询语句",
"required_params": ["query"],
"examples": [
'app="Apache"',
'title="登录"',
'domain="example.com"',
'ip="1.1.1.1"',
'port="80"',
'country="CN"',
'city="Beijing"'
]
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
# 构建请求参数
params = {
'email': email,
'key': api_key,
'qbase64': base64.b64encode(query.encode('utf-8')).decode('utf-8')
}
# 可选参数
if 'size' in config and config['size'] is not None:
try:
size = int(config['size'])
if size > 0:
params['size'] = size
except (ValueError, TypeError):
pass
if 'page' in config and config['page'] is not None:
try:
page = int(config['page'])
if page > 0:
params['page'] = page
except (ValueError, TypeError):
pass
if 'fields' in config and config['fields']:
params['fields'] = str(config['fields']).strip()
if 'full' in config and config['full'] is not None:
full_val = config['full']
if isinstance(full_val, bool):
params['full'] = 'true' if full_val else 'false'
elif isinstance(full_val, str):
params['full'] = 'true' if full_val.lower() in ('true', '1', 'yes') else 'false'
elif isinstance(full_val, (int, float)):
params['full'] = 'true' if full_val != 0 else 'false'
# 发送请求
try:
response = requests.get(base_url, params=params, timeout=30)
response.raise_for_status()
result_data = response.json()
# 检查FOFA API返回的错误
if result_data.get('error'):
error_result = {
"status": "error",
"message": f"FOFA API错误: {result_data.get('errmsg', '未知错误')}",
"error_code": result_data.get('error'),
"suggestion": "请检查API密钥是否正确或查询语句是否符合FOFA语法"
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
# 格式化输出结果
output = {
"status": "success",
"query": query,
"size": result_data.get('size', 0),
"page": result_data.get('page', 1),
"total": result_data.get('total', 0),
"results_count": len(result_data.get('results', [])),
"results": result_data.get('results', []),
"message": f"成功获取 {len(result_data.get('results', []))} 条结果,共 {result_data.get('total', 0)} 条"
}
print(json.dumps(output, ensure_ascii=False, indent=2))
except requests.exceptions.RequestException as e:
error_result = {
"status": "error",
"message": f"请求失败: {str(e)}",
"suggestion": "请检查网络连接或FOFA API服务状态"
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
except Exception as e:
error_result = {
"status": "error",
"message": f"执行出错: {str(e)}",
"type": type(e).__name__
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
enabled: true
short_description: "FOFA网络空间搜索引擎支持灵活的查询参数配置"
description: |
FOFA是一个网络空间测绘搜索引擎可以通过多种查询条件搜索互联网资产。
**主要功能:**
- 支持多种查询语法app、title、domain、ip、port、country、city等
- 灵活的字段返回配置
- 分页查询支持
- 完整数据模式full参数
**使用场景:**
- 资产发现和枚举
- 漏洞影响范围评估
- 安全态势感知
- 威胁情报收集
- Bug bounty信息收集
**查询语法示例:**
- `app="Apache"` - 搜索Apache应用
- `title="登录"` - 搜索标题包含"登录"的页面
- `domain="example.com"` - 搜索特定域名
- `ip="1.1.1.1"` - 搜索特定IP
- `port="80"` - 搜索开放80端口的资产
- `country="CN"` - 搜索中国境内的资产
- `city="Beijing"` - 搜索北京的资产
- `app="Apache" && country="CN"` - 组合查询
**注意事项:**
- API调用有频率限制请合理使用
- 查询结果数量受账户权限限制
- full参数需要高级权限
parameters:
- name: "query"
type: "string"
description: |
FOFA查询语句必需
搜索查询语句支持FOFA查询语法。
示例:
- app="Apache"
- title="登录"
- domain="example.com"
- ip="1.1.1.1"
- port="80"
- country="CN"
- app="Apache" && country="CN"
required: true
position: 2
format: "positional"
- name: "size"
type: "int"
description: |
返回结果数量(可选)
每页返回的结果数量默认100。
取值范围1-10000受账户权限限制。
required: false
position: 3
format: "positional"
default: 100
- name: "page"
type: "int"
description: |
页码(可选)
要返回的页码从1开始默认1。
用于分页查询大量结果。
required: false
position: 4
format: "positional"
default: 1
- name: "fields"
type: "string"
description: |
返回字段列表(可选)
需要返回的字段,多个字段用逗号分隔。
默认字段ip,port,domain
可用字段host,title,ip,domain,port,protocol,country,province,city,server,icp,header,banner,body,as_number,as_organization
required: false
position: 5
format: "positional"
default: "ip,port,domain"
- name: "full"
type: "bool"
description: |
是否返回完整数据(可选)
设置为true时返回完整数据需要高级权限。
默认false返回基础数据。
required: false
position: 6
format: "positional"
default: false