IngressNightmare 批量安全检测工具
CVE-2025-1974 (IngressNightmare) 漏洞利用验证脚本
一个专为安全研究人员和运维团队设计的Python自动化工具,用于批量检测CVE-2025-1974 IngressNightmare漏洞。该工具通过发送构造的JSON payload到目标IP列表,自动收集响应并生成结构化的XML报告,极大提升了漏洞验证效率。
:sparkles: 功能特性
- 批量并发检测:支持从文件中读取大量IP地址,自动化顺序发送HTTPS请求
- 智能响应分类:基于HTTP状态码和curl返回码自动将请求分类为成功、失败、超时或错误
- 双报告输出:分别生成成功请求XML报告和失败请求XML报告,便于后续分析和漏洞确认
- 进度条清理:自动过滤curl输出的进度信息,保留纯净的错误和调试日志
- 灵活配置:支持自定义JSON payload文件、目标端口、请求超时、请求间隔延迟等参数
- 详细日志输出:实时显示每个IP的请求状态、HTTP响应码和错误信息,便于现场调试
- 零依赖设计:仅依赖Python标准库和系统curl命令,无需额外安装第三方包
:package: 安装指南
系统要求
- Python 3.6 或更高版本
- curl 命令行工具 (通常Linux/macOS预装,Windows需单独安装)
- 网络访问权限(需能访问目标IP的HTTPS端口)
安装步骤
- 克隆代码仓库
git clone https://github.com/abrewer251/CVE-2025-1974_IngressNightmare_PoC.git
cd CVE-2025-1974_IngressNightmare_PoC
- 赋予脚本执行权限 (Linux/macOS)
chmod +x poc.py
- 准备IP列表文件
创建一个文本文件,每行一个目标IP地址:
echo "192.168.1.10" > ips.txt
echo "192.168.1.20" >> ips.txt
-
准备JSON Payload文件 (默认为
poc.json)
根据CVE-2025-1974漏洞特性构造对应的JSON请求体,保存为 poc.json。
:rocket: 使用说明
基础用法
python3 poc.py ips.txt
使用默认配置:读取 ips.txt 中的IP列表,发送 poc.json payload到端口 8443,超时时间15秒,请求间隔1秒,生成 4-3_Success.xml 和 4-3_Failure.xml 报告。
命令行参数
| 参数 | 简写 | 说明 | 默认值 |
|---|---|---|---|
--json |
-j |
JSON payload文件路径 | poc.json |
--port |
-p |
目标HTTPS端口 | 8443 |
--timeout |
-t |
每个请求超时时间(秒) | 15 |
--delay |
-d |
请求间延迟时间(秒) | 1.0 |
--success |
-s |
成功报告文件名 | 4-3_Success.xml |
--failure |
-f |
失败报告文件名 | 4-3_Failure.xml |
--help |
-h |
显示帮助信息 | — |
典型使用场景
场景一:自定义Payload和端口
python3 poc.py -j custom_payload.json -p 9443 ips.txt
场景二:快速扫描模式 (缩短超时和延迟)
python3 poc.py -t 5 -d 0.5 ips.txt
场景三:自定义输出报告名称
python3 poc.py -s success_scan.xml -f failed_scan.xml ips.txt
场景四:大规模IP列表扫描 (使用较慢间隔避免被限制)
python3 poc.py -d 3.0 -t 30 ip_list_large.txt
输出示例
控制台实时输出:
[INFO] Processing IP: 192.168.1.10
[SUCCESS] IP: 192.168.1.10 | HTTP Code: 200
[INFO] Processing IP: 192.168.1.20
[FAILED] IP: 192.168.1.20 | HTTP Code: 405
[INFO] Processing IP: 192.168.1.30
[TIMEOUT] IP: 192.168.1.30 | Request timed out
生成的XML报告文件包含每个请求的详细数据:IP地址、状态、HTTP状态码、响应体、stderr日志和执行的curl命令。
:laptop: 核心代码
1. curl请求执行与响应捕获
def run_curl(ip, json_file, port, timeout):
"""
执行curl命令向目标IP发送JSON payload并捕获响应
核心功能:
- 构建HTTPS URL: https://{ip}:{port}
- 使用curl发送POST请求,包含JSON文件内容
- 捕获HTTP状态码(通过-w %{http_code})
- 分离响应体和状态码(响应体末尾三位为状态码)
- 清理stderr中的进度信息(保留错误日志)
- 处理超时异常并返回统一格式的字典
参数:
ip: 目标IP地址
json_file: JSON payload文件路径
port: 目标端口
timeout: 请求超时时间(秒)
返回:
dict: 包含ip、status、stdout、stderr、returncode、http_code、command
"""
url = f"https://{ip}:{port}"
curl_command = [
"curl",
"--insecure", # 忽略SSL证书验证(测试环境)
"-k", # 等同于--insecure
"-v", # 详细输出模式,便于调试
"-w", "%{http_code}", # 在输出末尾追加HTTP状态码
"-H", "Content-Type: application/json", # 设置JSON Content-Type
"--data", f"@{json_file}", # 从文件读取JSON数据
url
]
try:
result = subprocess.run(
curl_command,
capture_output=True,
text=True,
timeout=timeout
)
full_output = result.stdout
http_code = full_output[-3:] if len(full_output) >= 3 else "000"
response_body = full_output[:-3].strip() + "\n"
# 分类逻辑:返回码为0且HTTP状态码不等于405(Method Not Allowed)视为成功
status = "success" if result.returncode == 0 and http_code != "405" else "failed"
return {
"ip": ip,
"status": status,
"stdout": response_body,
"stderr": clean_stderr(result.stderr), # 清理进度信息
"returncode": result.returncode,
"http_code": http_code,
"command": " ".join(curl_command)
}
except subprocess.TimeoutExpired:
return {
"ip": ip,
"status": "timeout",
"stdout": "",
"stderr": "Request timed out\n",
"returncode": None,
"http_code": "000",
}
2. curl输出清理函数
def clean_stderr(stderr_text):
"""
清理curl的stderr输出,移除进度表和速度信息
功能说明:
- 使用正则表达式匹配并移除curl的进度百分比行
- 移除包含"Dload Upload Total Spent Left Speed"的标题行
- 保留错误信息、警告和调试输出
正则说明:
- progress_pattern: 匹配进度百分比行,如 " % Total % Received ..."
- skip_line: 匹配速度标题行,完全匹配"Dload Upload Total Spent Left Speed"
参数:
stderr_text: curl的原始stderr输出
返回:
str: 清理后的stderr内容,末尾添加换行符
"""
lines = stderr_text.splitlines()
cleaned_lines = []
progress_pattern = re.compile(r"\s*% Total\s+% Received.*|^\s*\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+.*")
skip_line = re.compile(r"Dload\s+Upload\s+Total\s+Spent\s+Left\s+Speed")
for line in lines:
if not progress_pattern.match(line.strip()) and not skip_line.match(line.strip()):
cleaned_lines.append(line)
return "\n".join(cleaned_lines).strip() + "\n"
3. XML报告生成器
def generate_xml_report(results, output_file, report_type):
"""
生成XML格式的详细报告文件
功能说明:
- 创建符合标准XML格式的报告
- 包含每个请求的完整元数据:IP、状态、HTTP状态码、返回码、响应体、stderr、curl命令
- 使用CDATA块包裹特殊字符,确保XML合法性
- 自动在根节点下添加成功或失败的分类
参数:
results: 请求结果字典列表
output_file: 输出XML文件路径
report_type: 报告类型(用于根节点命名)
XML结构:
<IngressNightmare>
<{report_type}>
<Request>
<IP>192.168.1.1</IP>
<Status>success</Status>
<HTTP_Code>200</HTTP_Code>
<ReturnCode>0</ReturnCode>
<Stdout><![CDATA[...]]></Stdout>
<Stderr><![CDATA[...]]></Stderr>
<Command><![CDATA[...]]></Command>
</Request>
</{report_type}>
</IngressNightmare>
"""
root = ET.Element("IngressNightmare")
type_elem = ET.SubElement(root, report_type)
for res in results:
req_elem = ET.SubElement(type_elem, "Request")
# 添加每个字段到XML节点
ip_elem = ET.SubElement(req_elem, "IP")
ip_elem.text = res["ip"]
status_elem = ET.SubElement(req_elem, "Status")
status_elem.text = res["status"]
http_code_elem = ET.SubElement(req_elem, "HTTP_Code")
http_code_elem.text = res["http_code"]
returncode_elem = ET.SubElement(req_elem, "ReturnCode")
returncode_elem.text = str(res["returncode"]) if res["returncode"] is not None else "None"
# 使用CDATA保留特殊字符
stdout_elem = ET.SubElement(req_elem, "Stdout")
stdout_elem.text = ET.CDATA(res["stdout"]) if res["stdout"] else ""
stderr_elem = ET.SubElement(req_elem, "Stderr")
stderr_elem.text = ET.CDATA(res["stderr"]) if res["stderr"] else ""
command_elem = ET.SubElement(req_elem, "Command")
command_elem.text = ET.CDATA(res["command"])
tree = ET.ElementTree(root)
ET.indent(tree, space=" ", level=0) # Python 3.9+ 格式化输出
tree.write(output_file, encoding="utf-8", xml_declaration=True)
该工具仅供安全研究和授权测试使用,请在遵守当地法律法规的前提下运行。
6HFtX5dABrKlqXeO5PUv/0dvXZ3Wu5ra76/yvJK4cgDsSrZ4GOh0xdw32tRHi65AR+gJterSJ1a5DajvLlcXdw==