Add files via upload

This commit is contained in:
公明 authored and GitHub committed 2026-08-18 20:52:02 +08:00
1 parent 286511d0a8
commit 5ccabdc3e4
4 files changed
+36 -256

No files matched your search

+11 -1
View File
@@ -16,7 +16,7 @@ args:
# ==================================================
# Quake API基础URL
base_url = "https://quake.360.cn/api/v3/search/quake_service"
base_url = "https://quake.360.net/api/v3/search/quake_service"
# 解析参数(从JSON字符串或命令行参数)
def parse_args():
@@ -178,6 +178,16 @@ args:
sys.exit(1)
results = result_data.get("data", [])
if isinstance(results, dict):
nested = None
for key in ("data", "items", "matches", "results", "list", "records"):
value = results.get(key)
if isinstance(value, list):
nested = value
break
results = nested if nested is not None else []
if not isinstance(results, list):
results = []
meta = result_data.get("meta", {})
pagination = meta.get("pagination", {}) if isinstance(meta, dict) else {}
-13
View File
@@ -25,7 +25,6 @@ parameters:
required: true
flag: "-a"
format: "flag"
# -p, --ports;范围请用 range (-r),勿把 1-1000 填进 ports
- name: "ports"
type: "string"
@@ -36,7 +35,6 @@ parameters:
required: false
flag: "-p"
format: "flag"
# -r, --range(与 ports 列表二选一或按官方说明组合)
- name: "range"
type: "string"
@@ -47,7 +45,6 @@ parameters:
required: false
flag: "-r"
format: "flag"
# -u, --ulimit
- name: "ulimit"
type: "int"
@@ -56,7 +53,6 @@ parameters:
flag: "-u"
format: "flag"
default: 5000
# --scripts:none | default | custom(CLI 默认 default)
- name: "scripts"
type: "string"
@@ -73,7 +69,6 @@ parameters:
- "none"
- "default"
- "custom"
# -g, --greppable:仅端口列表,无 Nmap 输出
- name: "greppable"
type: "bool"
@@ -82,7 +77,6 @@ parameters:
flag: "-g"
format: "flag"
default: false
# -b, --batch-size [default: 4500]
- name: "batch_size"
type: "int"
@@ -90,7 +84,6 @@ parameters:
required: false
flag: "-b"
format: "flag"
# -t, --timeout ms [default: 1500]
- name: "timeout_ms"
type: "int"
@@ -98,7 +91,6 @@ parameters:
required: false
flag: "-t"
format: "flag"
# --scan-order serial | random [default: serial]
- name: "scan_order"
type: "string"
@@ -109,7 +101,6 @@ parameters:
options:
- "serial"
- "random"
# --top:top 1000 端口
- name: "top_ports"
type: "bool"
@@ -118,7 +109,6 @@ parameters:
flag: "--top"
format: "flag"
default: false
# -e, --exclude-ports
- name: "exclude_ports"
type: "string"
@@ -126,7 +116,6 @@ parameters:
required: false
flag: "-e"
format: "flag"
# -x, --exclude-addresses
- name: "exclude_addresses"
type: "string"
@@ -134,7 +123,6 @@ parameters:
required: false
flag: "-x"
format: "flag"
# --tries [default: 1]
- name: "tries"
type: "int"
@@ -142,7 +130,6 @@ parameters:
required: false
flag: "--tries"
format: "flag"
- name: "additional_args"
type: "string"
description: |
+13 -240
View File
@@ -2,296 +2,70 @@ name: "virustotal_search"
command: "python3"
args:
- "-c"
- |
import sys
import json
import requests
import os
import time
# ==================== VirusTotal 配置 ====================
# 请在此处配置您的 VirusTotal API 密钥
# 您也可以在环境变量中设置:VT_API_KEY
# enable 默认为 false,需开启才能调用该MCP
VT_API_KEY = "" # 请填写您的 VirusTotal API 密钥
# =======================================================
# VirusTotal API 基础 URL
BASE_URL = "https://www.virustotal.com/api/v3"
def parse_args():
"""解析命令行参数"""
# 尝试从第一个参数读取 JSON 配置
if len(sys.argv) > 1:
try:
arg1 = str(sys.argv[1])
config = json.loads(arg1)
if isinstance(config, dict):
return config
except (json.JSONDecodeError, TypeError, ValueError):
pass
# 传统位置参数方式
config = {}
if len(sys.argv) > 1:
config['domain'] = str(sys.argv[1])
if len(sys.argv) > 2:
try:
config['limit'] = int(sys.argv[2])
except (ValueError, TypeError):
pass
if len(sys.argv) > 3:
config['include_ips'] = sys.argv[3].lower() in ('true', '1', 'yes')
return config
def query_virustotal_subdomains(domain, api_key, limit=100, include_ips=False):
"""
查询 VirusTotal 的子域名信息
Args:
domain: 要查询的域名
api_key: VirusTotal API 密钥
limit: 返回结果数量限制
include_ips: 是否包含 IP 地址信息
Returns:
dict: 包含查询结果的字典
"""
# 构建 API 请求 URL
url = f"{BASE_URL}/domains/{domain}/subdomains"
headers = {
"x-apikey": api_key,
"accept": "application/json"
}
params = {
"limit": min(limit, 40) # API 限制最大 40
}
all_results = []
next_url = None
try:
# 处理分页
while True:
if next_url:
response = requests.get(next_url, headers=headers, timeout=30)
else:
response = requests.get(url, headers=headers, params=params, timeout=30)
response.raise_for_status()
data = response.json()
# 提取子域名数据
if 'data' in data and data['data']:
for item in data['data']:
if 'id' in item:
subdomain_info = {
'subdomain': item['id'],
'type': item.get('type', 'domain'),
}
# 如果 include_ips 为 True,尝试获取解析 IP
if include_ips and 'attributes' in item:
attributes = item.get('attributes', {})
# 这里简化处理,实际可能需要额外的 API 调用
subdomain_info['last_dns_records'] = attributes.get('last_dns_records', [])
all_results.append(subdomain_info)
# 检查是否有下一页
if 'links' in data and 'next' in data['links'] and len(all_results) < limit:
next_url = data['links']['next']
# 避免请求过快
time.sleep(0.5)
else:
break
else:
break
# 如果已达到限制,停止获取
if len(all_results) >= limit:
break
# 处理返回结果
if all_results:
return {
"status": "success",
"domain": domain,
"total_found": len(all_results),
"results": all_results[:limit],
"message": f"成功获取 {len(all_results[:limit])} 个子域名"
}
else:
return {
"status": "success",
"domain": domain,
"total_found": 0,
"results": [],
"message": f"未找到 {domain} 的子域名"
}
except requests.exceptions.RequestException as e:
error_msg = str(e)
error_result = {
"status": "error",
"message": f"API 请求失败: {error_msg}",
"suggestion": "请检查网络连接、API 密钥是否正确,或 VirusTotal API 服务是否可用"
}
# 处理特定 HTTP 状态码
if hasattr(e, 'response') and e.response:
status_code = e.response.status_code
if status_code == 401:
error_result["message"] = "API 密钥无效或未授权"
error_result["suggestion"] = "请检查 VirusTotal API 密钥是否正确,或在 https://www.virustotal.com/ 获取有效密钥"
elif status_code == 429:
error_result["message"] = "API 请求频率超限"
error_result["suggestion"] = "请稍后再试,VirusTotal API 有严格的速率限制(免费版每分钟4次)"
elif status_code == 404:
error_result["message"] = f"域名 '{domain}' 不存在或未找到"
return error_result
try:
config = parse_args()
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 密钥(从配置或环境变量)
api_key = os.getenv('VT_API_KEY', VT_API_KEY).strip()
if not api_key:
error_result = {
"status": "error",
"message": "缺少 VirusTotal API 密钥",
"required_config": ["VT_API_KEY"],
"note": "请在 YAML 文件的 VT_API_KEY 配置项中填写您的 VirusTotal API 密钥,或在环境变量 VT_API_KEY 中设置。API 密钥可在 https://www.virustotal.com/ 注册获取"
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
# 获取必需参数
domain = config.get('domain', '').strip()
if not domain:
error_result = {
"status": "error",
"message": "缺少必需参数: domain(要查询的域名)",
"required_params": ["domain"],
"examples": [
"example.com",
"google.com",
"baidu.com"
]
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
# 获取可选参数
limit = config.get('limit', 100)
try:
limit = int(limit)
if limit < 1:
limit = 100
elif limit > 1000:
limit = 1000 # 限制最大 1000
except (ValueError, TypeError):
limit = 100
include_ips = config.get('include_ips', False)
if isinstance(include_ips, str):
include_ips = include_ips.lower() in ('true', '1', 'yes')
# 执行查询
result = query_virustotal_subdomains(domain, api_key, limit, include_ips)
# 输出结果
print(json.dumps(result, ensure_ascii=False, indent=2))
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)
- "import sys\nimport json\nimport requests\nimport os\nimport time\n\n# ==================== VirusTotal 配置 ====================\n# 请在此处配置您的 VirusTotal API 密钥\n# 您也可以在环境变量中设置:VT_API_KEY\n# enable 默认为 false,需开启才能调用该MCP\nVT_API_KEY = \"\" # 请填写您的 VirusTotal API 密钥\n# =======================================================\n\n# VirusTotal API 基础 URL\nBASE_URL = \"https://www.virustotal.com/api/v3\"\n\ndef parse_args():\n \"\"\"解析命令行参数\"\"\"\n # 尝试从第一个参数读取 JSON 配置\n if len(sys.argv) > 1:\n try:\n arg1 = str(sys.argv[1])\n config = json.loads(arg1)\n if isinstance(config, dict):\n return config\n except (json.JSONDecodeError, TypeError, ValueError):\n pass\n\n # 传统位置参数方式\n config = {}\n if len(sys.argv) > 1:\n config['domain'] = str(sys.argv[1])\n if len(sys.argv) > 2:\n try:\n config['limit'] = int(sys.argv[2])\n except (ValueError, TypeError):\n pass\n if len(sys.argv) > 3:\n config['include_ips'] = sys.argv[3].lower() in ('true', '1', 'yes')\n return config\n\ndef query_virustotal_subdomains(domain, api_key, limit=100, include_ips=False):\n \"\"\"\n 查询 VirusTotal 的子域名信息\n \n Args:\n domain: 要查询的域名\n api_key: VirusTotal API 密钥\n limit: 返回结果数量限制\n include_ips: 是否包含 IP 地址信息\n \n Returns:\n dict: 包含查询结果的字典\n \"\"\"\n # 构建 API 请求 URL\n url = f\"{BASE_URL}/domains/{domain}/subdomains\"\n \n headers = {\n \"x-apikey\": api_key,\n \"accept\": \"application/json\"\n }\n \n params = {\n \"limit\": min(limit, 40) # API 限制最大 40\n }\n \n all_results = []\n next_url = None\n \n try:\n # 处理分页\n while True:\n if next_url:\n response = requests.get(next_url, headers=headers, timeout=30)\n else:\n response = requests.get(url, headers=headers, params=params, timeout=30)\n \n response.raise_for_status()\n data = response.json()\n \n # 提取子域名数据\n if 'data' in data and data['data']:\n for item in data['data']:\n if 'id' in item:\n subdomain_info = {\n 'subdomain': item['id'],\n 'type': item.get('type', 'domain'),\n }\n \n # 如果 include_ips 为 True,尝试获取解析 IP\n if include_ips and 'attributes' in item:\n attributes = item.get('attributes', {})\n # 这里简化处理,实际可能需要额外的 API 调用\n subdomain_info['last_dns_records'] = attributes.get('last_dns_records', [])\n \n all_results.append(subdomain_info)\n \n # 检查是否有下一页\n if 'links' in data and 'next' in data['links'] and len(all_results) < limit:\n next_url = data['links']['next']\n # 避免请求过快\n time.sleep(0.5)\n else:\n break\n else:\n break\n \n # 如果已达到限制,停止获取\n if len(all_results) >= limit:\n break\n \n # 处理返回结果\n if all_results:\n return {\n \"status\": \"success\",\n \"domain\": domain,\n \"total_found\": len(all_results),\n \"results\": all_results[:limit],\n \"message\": f\"成功获取 {len(all_results[:limit])} 个子域名\"\n }\n else:\n return {\n \"status\": \"success\",\n \"domain\": domain,\n \"total_found\": 0,\n \"results\": [],\n \"message\": f\"未找到 {domain} 的子域名\"\n }\n \n except requests.exceptions.RequestException as e:\n error_msg = str(e)\n error_result = {\n \"status\": \"error\",\n \"message\": f\"API 请求失败: {error_msg}\",\n \"suggestion\": \"请检查网络连接、API 密钥是否正确,或 VirusTotal API 服务是否可用\"\n }\n \n # 处理特定 HTTP 状态码\n if hasattr(e, 'response') and e.response:\n status_code = e.response.status_code\n if status_code == 401:\n error_result[\"message\"] = \"API 密钥Line truncated
enabled: false
short_description: "VirusTotal 子域名查询工具,通过 VirusTotal API 被动收集域名子域名"
description: |
VirusTotal 子域名查询工具,利用 VirusTotal 聚合的历史 DNS 数据来发现目标域名的子域名。
**主要功能:**
- 被动子域名收集:从 VirusTotal 历史 DNS 数据中检索子域名
- 分页查询:支持大量子域名的获取
- IP 关联:可选包含 DNS 解析记录
- 去重处理:自动去重返回结果
**使用场景:**
- 安全测试前期信息收集
- 企业网络资产发现
- 攻击面分析
- 威胁情报收集
- 渗透测试信息收集
**数据来源:**
VirusTotal 聚合了来自多个来源的 DNS 数据,包括:
- 历史 DNS 解析记录
- 被动 DNS 数据库
- 证书透明度日志
- 安全扫描数据
**注意事项:**
- **API 密钥必需**:需要在 VirusTotal 注册账号并获取 API 密钥
- **速率限制**:免费版 API 每分钟限制 4 次请求
- **数据时效性**:数据基于历史扫描记录,可能不是实时的
- **使用授权**:仅允许对您拥有合法授权的目标进行查询
- **配额限制**:免费版每月有查询配额限制
parameters:
- name: "domain"
type: "string"
description: |
要查询的目标域名(必需)。
**格式要求:**
- 仅输入主域名,不要包含协议头(http://)或路径
- 支持二级域名查询
**示例值:**
- "example.com"
- "google.com"
- "baidu.com"
- "github.com"
**注意事项:**
- 域名格式必须正确
- 查询结果可能包含跨域子域名
required: true
position: 2
format: "positional"
- name: "limit"
type: "int"
description: |
返回结果数量限制(可选)。
**说明:**
- 默认值:40
- 最大值:1000(API 限制)
- 建议值:100-500
**注意事项:**
- 设置过大的值可能导致请求超时
- API 单次返回限制为 40 条,超过会自动分页
@@ -299,20 +73,19 @@ parameters:
position: 3
format: "positional"
default: 40
- name: "include_ips"
type: "bool"
description: |
是否包含 IP 地址信息(可选)。
**说明:**
- true:在结果中包含 DNS 解析记录
- false:仅返回子域名列表
**注意事项:**
- 包含 IP 信息会增加 API 调用次数
- 可能包含历史解析 IP,不一定准确
required: false
position: 4
format: "positional"
default: false
default: false
+12 -2
View File
@@ -17,7 +17,7 @@ args:
# ==================================================
# ZoomEye API基础URL
base_url = "https://api.zoomeye.org/v2/search"
base_url = "https://api.zoomeye.ai/v2/search"
# 解析参数(从JSON字符串或命令行参数)
def parse_args():
@@ -78,7 +78,7 @@ args:
"status": "error",
"message": "缺少ZoomEye配置: api_key(ZoomEye API密钥)",
"required_config": ["api_key"],
"note": "请在YAML文件的ZOOMEYE_API_KEY配置项中填写您的API密钥,或在环境变量ZOOMEYE_API_KEY中设置。API密钥可在ZoomEye个人中心获取: https://www.zoomeye.org/user"
"note": "请在YAML文件的ZOOMEYE_API_KEY配置项中填写您的API密钥,或在环境变量ZOOMEYE_API_KEY中设置。API密钥可在ZoomEye个人中心获取: https://www.zoomeye.ai/user"
}
print(json.dumps(error_result, ensure_ascii=False, indent=2))
sys.exit(1)
@@ -174,6 +174,16 @@ args:
# 格式化输出结果
# ZoomEye API 返回的数据在 data 字段中
data_list = result_data.get('data', [])
if isinstance(data_list, dict):
nested = None
for key in ('data', 'items', 'matches', 'results', 'list', 'records'):
value = data_list.get(key)
if isinstance(value, list):
nested = value
break
data_list = nested if nested is not None else []
if not isinstance(data_list, list):
data_list = []
output = {
"status": "success",
"query": result_data.get('query', query),