Add files via upload

This commit is contained in:
公明
2025-12-19 23:39:59 +08:00
committed by GitHub
parent c7e8f3a42b
commit df47fe258e

358
tools/zoomeye_search.yaml Normal file
View File

@@ -0,0 +1,358 @@
name: "zoomeye_search"
command: "python3"
args:
- "-c"
- |
import sys
import json
import base64
import requests
import os
# ==================== ZoomEye配置 ====================
# 请在此处配置您的ZoomEye账号信息
# 您也可以在环境变量中设置ZOOMEYE_API_KEY
ZOOMEYE_API_KEY = "" # 请填写您的ZoomEye API密钥
# ==================================================
# ZoomEye API基础URL
base_url = "https://api.zoomeye.org/v2/search"
# 解析参数从JSON字符串或命令行参数
def parse_args():
# 尝试从第一个参数读取JSON配置
if len(sys.argv) > 1:
try:
# 确保 sys.argv[1] 是字符串
arg1 = str(sys.argv[1])
# 尝试解析为JSON
config = json.loads(arg1)
# 确保返回的是字典类型
if isinstance(config, dict):
return config
except (json.JSONDecodeError, TypeError, ValueError):
# 如果不是JSON使用传统的位置参数方式
pass
# 传统位置参数方式(向后兼容)
# 参数位置query=1, page=2, pagesize=3, fields=4, sub_type=5
config = {}
if len(sys.argv) > 1:
config['query'] = str(sys.argv[1])
if len(sys.argv) > 2:
try:
config['page'] = int(sys.argv[2])
except (ValueError, TypeError):
pass
if len(sys.argv) > 3:
try:
config['pagesize'] = int(sys.argv[3])
except (ValueError, TypeError):
pass
if len(sys.argv) > 4:
config['fields'] = str(sys.argv[4])
if len(sys.argv) > 5:
config['sub_type'] = str(sys.argv[5])
return config
try:
config = parse_args()
# 确保 config 是字典类型
if not isinstance(config, dict):
error_result = {
"status": "error",
"message": f"参数解析错误: 期望字典类型,但得到 {type(config).__name__}",
"type": "TypeError"
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
# 从配置或环境变量获取api_key
api_key = os.getenv('ZOOMEYE_API_KEY', ZOOMEYE_API_KEY).strip()
query = config.get('query', '').strip()
if not api_key:
error_result = {
"status": "error",
"message": "缺少ZoomEye配置: api_keyZoomEye API密钥",
"required_config": ["api_key"],
"note": "请在YAML文件的ZOOMEYE_API_KEY配置项中填写您的API密钥或在环境变量ZOOMEYE_API_KEY中设置。API密钥可在ZoomEye个人中心获取: https://www.zoomeye.org/user"
}
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)
# 构建请求参数ZoomEye API 使用 POST 请求,参数在请求体中)
# ZoomEye API 使用 qbase64 参数base64编码的查询
data = {
'qbase64': base64.b64encode(query.encode('utf-8')).decode('utf-8')
}
# 可选参数
if 'page' in config and config['page'] is not None:
try:
page = int(config['page'])
if page > 0:
data['page'] = page
except (ValueError, TypeError):
pass
if 'pagesize' in config and config['pagesize'] is not None:
try:
pagesize = int(config['pagesize'])
if pagesize > 0 and pagesize <= 10000:
data['pagesize'] = pagesize
except (ValueError, TypeError):
pass
if 'fields' in config and config['fields']:
data['fields'] = str(config['fields']).strip()
if 'sub_type' in config and config['sub_type']:
sub_type = str(config['sub_type']).strip().lower()
if sub_type in ('v4', 'v6', 'web'):
data['sub_type'] = sub_type
if 'facets' in config and config['facets']:
data['facets'] = str(config['facets']).strip()
if 'ignore_cache' in config and config['ignore_cache'] is not None:
ignore_cache_val = config['ignore_cache']
if isinstance(ignore_cache_val, bool):
data['ignore_cache'] = ignore_cache_val
elif isinstance(ignore_cache_val, str):
data['ignore_cache'] = ignore_cache_val.lower() in ('true', '1', 'yes')
elif isinstance(ignore_cache_val, (int, float)):
data['ignore_cache'] = ignore_cache_val != 0
# 设置请求头ZoomEye API 使用 API Key 认证)
headers = {
'API-KEY': api_key,
'Content-Type': 'application/json'
}
# 发送请求ZoomEye API 使用 POST 方法)
try:
response = requests.post(base_url, json=data, headers=headers, timeout=30)
response.raise_for_status()
result_data = response.json()
# 检查ZoomEye API返回的错误
# ZoomEye API 成功时 code 为 60000
if result_data.get('code') != 60000:
error_code = result_data.get('code', 'unknown')
error_msg = result_data.get('message', '未知错误')
error_result = {
"status": "error",
"message": f"ZoomEye API错误: {error_msg}",
"error_code": error_code,
"suggestion": "请检查API密钥是否正确或查询语句是否符合ZoomEye语法"
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
# 格式化输出结果
# ZoomEye API 返回的数据在 data 字段中
data_list = result_data.get('data', [])
output = {
"status": "success",
"query": result_data.get('query', query),
"total": result_data.get('total', 0),
"page": result_data.get('page', 1),
"pagesize": result_data.get('pagesize', 10),
"results_count": len(data_list),
"results": data_list,
"message": f"成功获取 {len(data_list)} 条结果"
}
print(json.dumps(output, ensure_ascii=False, indent=2))
except requests.exceptions.RequestException as e:
error_result = {
"status": "error",
"message": f"请求失败: {str(e)}",
"suggestion": "请检查网络连接或ZoomEye 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: "ZoomEye网络空间搜索引擎支持灵活的查询参数配置"
description: |
ZoomEye钟馗之眼是一个网络空间测绘搜索引擎可以通过多种查询条件搜索互联网资产。
**主要功能:**
- 支持多种查询语法app、title、domain、ip、port、country、city等
- 灵活的字段返回配置
- 分页查询支持最多10000条/页)
- 支持IPv4、IPv6和Web资产搜索
- 支持统计项facets查询
- 丰富的资产信息返回
**使用场景:**
- 资产发现和枚举
- 漏洞影响范围评估
- 安全态势感知
- 威胁情报收集
- Bug bounty信息收集
**查询语法说明:**
**搜索规则:**
- 搜索范围包括设备IPv4、IPv6和网站域名
- 搜索字符串不区分大小写,使用引号包裹(如 `"Cisco System"`
- 使用 `==` 进行精确匹配(区分大小写)
**逻辑运算符:**
- `=` - 搜索包含关键词的资产,如 `title="knownsec"`
- `==` - 精确匹配(区分大小写),如 `title=="knownsec"`
- `||` - 或运算,如 `service="ssh" || service="http"`
- `&&` - 与运算,如 `device="router" && after="2020-01-01"`
- `!=` - 非运算,如 `country="US" && subdivisions!="new york"`
- `()` - 优先级处理,如 `(country="US" && port!=80) || (country="US" && title!="404 Not Found")`
- `*` - 模糊搜索,如 `title="*google"`
**语法关键字:**
**地理位置:** `country="CN"`、`subdivisions="beijing"`、`city="changsha"`
**IP/域名:** `ip="8.8.8.8"`、`cidr="52.2.254.36/24"`、`domain="baidu.com"`、`hostname="google.com"`、`port=80`、`asn=42893`、`org="Stanford University"`、`isp="China Mobile"`
**指纹识别:** `app="Cisco ASA SSL VPN"`、`service="ssh"`、`device="router"`、`os="RouterOS"`、`title="Cisco"`、`product="Cisco"`、`protocol="TCP"`、`industry="government"`、`is_honeypot="True"`
**证书搜索:** `ssl="google"`、`ssl.cert.fingerprint="..."`、`ssl.jarm="..."`、`ssl.ja3s=...`、`ssl.version="TLSv1.3"`、`ssl.cert.subject.cn="example.com"`
**HTTP相关** `banner="FTP"`、`http.header="http"`、`http.header_hash="..."`、`http.header.server="Nginx"`、`http.header.status_code="200"`、`http.body="document"`、`http.body_hash="..."`
**时间搜索:** `after="2020-01-01"`、`before="2020-01-01"`(需与其他条件组合使用)
**其他:** `dig="baidu.com 220.181.38.148"`、`iconhash="f3418a443e7d841097c714d69ec4bcb8"`、`filehash="0b5ce08db7fb8fffe4e14d05588d49d9"`、`is_ipv4=true`、`is_ipv6=true`、`is_domain=true`
**查询示例:**
- `port=80 && service="http"` - 搜索80端口运行HTTP服务的网络设备
- `city=nagoya && port=443 && service=ssl` - 搜索名古屋443端口运行SSL的设备
- `country=us && os=windows` - 搜索美国运行Windows系统的设备
- `app="Microsoft NTP"` - 搜索运行Microsoft NTP应用的设备
- `city=tokyo && device=webcam` - 搜索东京的摄像头
- `after="2020-01-01" && port=50050` - 搜索2020-01-01之后索引的50050端口资产
- `os=linux && port=22 && country=PL` - 搜索波兰Linux系统22端口资产
- `service=ftp && hostname=example` - 搜索主机名为example的FTP服务
- `http.body="Knownsec"` - 搜索body中包含"Knownsec"的资产
**注意事项:**
- API调用有频率限制请合理使用
- 查询结果数量受账户权限限制
- 需要有效的ZoomEye API密钥
parameters:
- name: "query"
type: "string"
description: |
ZoomEye查询语句必需
搜索查询语句支持ZoomEye查询语法。使用引号包裹字符串使用逻辑运算符连接条件。
示例:
- app="Apache"
- title="登录"
- domain="example.com"
- ip="1.1.1.1"
- port=80
- country="CN"
- app="Apache" && country="CN"
- service="ssh" || service="http"
- port=80 && service="http"
required: true
position: 1
format: "positional"
- name: "page"
type: "int"
description: |
页码(可选)
要返回的页码从1开始默认1。
用于分页查询大量结果。
required: false
position: 2
format: "positional"
default: 1
- name: "pagesize"
type: "int"
description: |
每页结果数量(可选)
每页返回的结果数量默认10。
取值范围1-10000受账户权限限制。
required: false
position: 3
format: "positional"
default: 5
- name: "fields"
type: "string"
description: |
返回字段列表(可选)
需要返回的字段,多个字段用逗号分隔。
默认字段ip,port,domain,update_time
可用字段ip,port,domain,url,hostname,os,service,title,version,device,rdns,product,header,body,banner,update_time,country,province,city,isp,organization,asn,protocol,ssl,ssl.jarm,ssl.ja3s等
required: false
position: 4
format: "positional"
default: "ip,port,domain,update_time"
- name: "sub_type"
type: "string"
description: |
数据类型(可选)
指定搜索的数据类型。
可选值v4IPv4设备默认、v6IPv6设备、web网站资产
required: false
position: 5
format: "positional"
default: "v4"
- name: "facets"
type: "string"
description: |
统计项(可选)
需要统计的字段,多个字段用逗号分隔。
支持country、subdivisions、city、product、service、device、os、port
required: false
position: 6
format: "positional"
- name: "ignore_cache"
type: "bool"
description: |
是否忽略缓存(可选)
设置为true时忽略缓存获取最新数据。
默认false支持商业版及以上用户。
required: false
position: 7
format: "positional"
default: false