lab 0.2.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lab.gemspec +1 -1
- data/lib/lab/controller/remote_esxi_controller.rb +1 -1
- data/lib/lab/controller/{remote_esx_controller.rb → vsphere_controller.rb} +2 -2
- data/lib/lab/controllers.rb +1 -0
- data/lib/lab/driver/fog_driver.rb +3 -63
- data/lib/lab/driver/vsphere_driver.rb +120 -0
- data/lib/lab/drivers.rb +1 -0
- data/lib/lab/modifier/meterpreter_modifier.rb +167 -0
- data/lib/lab/version.rb +1 -1
- data/lib/lab/vm.rb +33 -18
- data/lib/lab/vm_controller.rb +77 -49
- metadata +13 -12
- data/src/README.md +0 -80
data/lab.gemspec
CHANGED
@@ -9,7 +9,7 @@ module RemoteEsxiController
|
|
9
9
|
VIM_CMD = 'vim-cmd'.freeze
|
10
10
|
|
11
11
|
def self.dir_list(basepath=nil)
|
12
|
-
# Does this method really even make sense for
|
12
|
+
# Does this method really even make sense for esxi?
|
13
13
|
return "Unsupported :("
|
14
14
|
end
|
15
15
|
|
@@ -47,7 +47,7 @@ private
|
|
47
47
|
id_and_name = line.split('[datastore').first
|
48
48
|
id = id_and_name.split(' ').first
|
49
49
|
|
50
|
-
|
50
|
+
# TODO - there's surely a better way to do this.
|
51
51
|
name_array = id_and_name.split(' ')
|
52
52
|
name_array.shift
|
53
53
|
name = name_array.join(' ')
|
data/lib/lab/controllers.rb
CHANGED
@@ -4,5 +4,6 @@ require 'controller/fog_controller'
|
|
4
4
|
require 'controller/dynagen_controller'
|
5
5
|
require 'controller/remote_workstation_controller'
|
6
6
|
require 'controller/remote_esxi_controller'
|
7
|
+
require 'controller/vsphere_controller'
|
7
8
|
#require 'controller/qemu_controller'
|
8
9
|
#require 'controller/qemudo_controller'
|
@@ -17,7 +17,7 @@ class FogDriver < VmDriver
|
|
17
17
|
begin
|
18
18
|
require 'fog'
|
19
19
|
rescue LoadError
|
20
|
-
raise "WARNING: Library fog not found. Could
|
20
|
+
raise "WARNING: Library fog not found. Could not create driver"
|
21
21
|
end
|
22
22
|
|
23
23
|
if @fog_config['fog_type'] == "ec2"
|
@@ -44,18 +44,17 @@ class FogDriver < VmDriver
|
|
44
44
|
:aws_access_key_id => @aws_access_key_file,
|
45
45
|
:aws_secret_access_key => @aws_secret_access_key_file )
|
46
46
|
else
|
47
|
-
raise "Unsupported
|
47
|
+
raise "Unsupported fog type"
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
def start
|
52
52
|
ec2_settings = {
|
53
|
-
:image_id => @ec2_base_ami,
|
53
|
+
:image_id => @ec2_base_ami,
|
54
54
|
:flavor_id => @ec2_flavor,
|
55
55
|
:public_key_path => @ec2_instance_public_key_file,
|
56
56
|
:private_key_path => @ec2_instance_private_key_file,
|
57
57
|
:username => @ec2_user}
|
58
|
-
|
59
58
|
begin
|
60
59
|
@fog_server = @compute.servers.bootstrap(ec2_settings)
|
61
60
|
rescue Fog::Compute::AWS::Error => e
|
@@ -92,65 +91,6 @@ class FogDriver < VmDriver
|
|
92
91
|
raise "unimplemented"
|
93
92
|
end
|
94
93
|
|
95
|
-
=begin
|
96
|
-
|
97
|
-
def run_command(command)
|
98
|
-
## vm_driver will need a little patching for this to work, as
|
99
|
-
## amis use keys for auth. i think it's just a matter of not passing the
|
100
|
-
## password to ssh_exec. So maybe the thing to do is have a ssh_key_exec
|
101
|
-
## function in vm_driver.rb that does the right thing.
|
102
|
-
|
103
|
-
script_rand_name = rand(10000)
|
104
|
-
|
105
|
-
if @os == "windows"
|
106
|
-
local_tempfile_path = "/tmp/lab_script_#{script_rand_name}.bat"
|
107
|
-
remote_tempfile_path = "C:\\\\lab_script_#{script_rand_name}.bat"
|
108
|
-
remote_run_command = remote_tempfile_path
|
109
|
-
else
|
110
|
-
local_tempfile_path = "/tmp/lab_script_#{script_rand_name}.sh"
|
111
|
-
remote_tempfile_path = "/tmp/lab_script_#{script_rand_name}.sh"
|
112
|
-
remote_run_command = "/bin/sh #{remote_tempfile_path}"
|
113
|
-
end
|
114
|
-
|
115
|
-
# write out our script locally
|
116
|
-
File.open(local_tempfile_path, 'w') {|f| f.write(command) }
|
117
|
-
|
118
|
-
# since we can't copy easily w/o tools, let's just run it directly :/
|
119
|
-
if @os == "linux"
|
120
|
-
output_file = "/tmp/lab_command_output_#{rand(1000000)}"
|
121
|
-
|
122
|
-
scp_to(local_tempfile_path, remote_tempfile_path)
|
123
|
-
ssh_exec(remote_run_command + "> #{output_file}")
|
124
|
-
scp_from(output_file, output_file)
|
125
|
-
ssh_exec("rm #{output_file}")
|
126
|
-
ssh_exec("rm #{remote_tempfile_path}")
|
127
|
-
|
128
|
-
# Ghettohack!
|
129
|
-
string = File.open(output_file,"r").read
|
130
|
-
`rm #{output_file}`
|
131
|
-
|
132
|
-
else
|
133
|
-
raise "zomgwtfbbqnotools"
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def copy_from(from, to)
|
138
|
-
raise "unimplemented"
|
139
|
-
end
|
140
|
-
|
141
|
-
def copy_to(from, to)
|
142
|
-
raise "unimplemented"
|
143
|
-
end
|
144
|
-
|
145
|
-
def check_file_exists(file)
|
146
|
-
raise "unimplemented"
|
147
|
-
end
|
148
|
-
|
149
|
-
def create_directory(directory)
|
150
|
-
raise "unimplemented"
|
151
|
-
end
|
152
|
-
=end
|
153
|
-
|
154
94
|
def cleanup
|
155
95
|
@fog_server.destroy
|
156
96
|
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'vm_driver'
|
2
|
+
|
3
|
+
##
|
4
|
+
## $Id$
|
5
|
+
##
|
6
|
+
|
7
|
+
# This driver was built against:
|
8
|
+
# VMware Vsphere 4.1
|
9
|
+
|
10
|
+
module Lab
|
11
|
+
module Drivers
|
12
|
+
|
13
|
+
class VsphereDriver < VmDriver
|
14
|
+
|
15
|
+
def initialize(config)
|
16
|
+
unless config['user'] then raise ArgumentError, "Must provide a username" end
|
17
|
+
unless config['host'] then raise ArgumentError, "Must provide a hostname" end
|
18
|
+
unless config[''] then raise ArgumentError, "Must provide a password" end
|
19
|
+
super(config)
|
20
|
+
|
21
|
+
@user = filter_command(config['user'])
|
22
|
+
@host = filter_command(config['host'])
|
23
|
+
|
24
|
+
# Soft dependency
|
25
|
+
begin
|
26
|
+
require 'rbvmomi'
|
27
|
+
rescue LoadError
|
28
|
+
raise "WARNING: Library rbvmomi not found. Could not create driver!"
|
29
|
+
end
|
30
|
+
|
31
|
+
vim = RbVmomi::VIM.connect host: @host, user: @user, password: @pass
|
32
|
+
dc = vim.serviceInstance.find_datacenter("datacenter1") or fail "datacenter not found"
|
33
|
+
@vm = dc.find_vm("test") or fail "VM not found"
|
34
|
+
end
|
35
|
+
|
36
|
+
def start
|
37
|
+
@vm.PowerOnVM_Task.wait_for_completion
|
38
|
+
end
|
39
|
+
|
40
|
+
def stop
|
41
|
+
@vm.PowerOffVM_Task.wait_for_completion
|
42
|
+
end
|
43
|
+
|
44
|
+
def suspend
|
45
|
+
@vm.SuspendVM_Task.wait_for_completion
|
46
|
+
end
|
47
|
+
|
48
|
+
def pause
|
49
|
+
raise "Unimplemented"
|
50
|
+
end
|
51
|
+
|
52
|
+
def resume
|
53
|
+
raise "Unimplemented"
|
54
|
+
end
|
55
|
+
|
56
|
+
def reset
|
57
|
+
@vm.ResetVM_Task.wait_for_completion
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_snapshot(snapshot)
|
61
|
+
snapshot = filter_input(snapshot)
|
62
|
+
raise "Unimplemented"
|
63
|
+
end
|
64
|
+
|
65
|
+
def revert_snapshot(snapshot)
|
66
|
+
raise "Unimplemented"
|
67
|
+
# If we got here, the snapshot didn't exist
|
68
|
+
raise "Invalid Snapshot Name"
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete_snapshot(snapshot, remove_children=false)
|
72
|
+
raise "Unimplemented"
|
73
|
+
# If we got here, the snapshot didn't exist
|
74
|
+
raise "Invalid Snapshot Name"
|
75
|
+
end
|
76
|
+
|
77
|
+
def delete_all_snapshots
|
78
|
+
raise "Unimplemented"
|
79
|
+
end
|
80
|
+
|
81
|
+
def run_command(command)
|
82
|
+
raise "Unimplemented"
|
83
|
+
end
|
84
|
+
|
85
|
+
def copy_from(from, to)
|
86
|
+
if @os == "linux"
|
87
|
+
scp_from(from, to)
|
88
|
+
else
|
89
|
+
raise "Unimplemented"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def copy_to(from, to)
|
94
|
+
if @os == "linux"
|
95
|
+
scp_to(from, to)
|
96
|
+
else
|
97
|
+
raise "Unimplemented"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def check_file_exists(file)
|
102
|
+
raise "Unimplemented"
|
103
|
+
end
|
104
|
+
|
105
|
+
def create_directory(directory)
|
106
|
+
raise "Unimplemented"
|
107
|
+
end
|
108
|
+
|
109
|
+
def cleanup
|
110
|
+
raise "Unimplemented"
|
111
|
+
end
|
112
|
+
|
113
|
+
def running?
|
114
|
+
raise "Unimplemented"
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
data/lib/lab/drivers.rb
CHANGED
@@ -0,0 +1,167 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', '..'))
|
2
|
+
|
3
|
+
module Lab
|
4
|
+
module Modifier
|
5
|
+
module Meterpreter
|
6
|
+
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
# This allows us to override the default way of running commands
|
13
|
+
# Currently useful for the esx controller
|
14
|
+
|
15
|
+
module Lab
|
16
|
+
class Vm
|
17
|
+
|
18
|
+
attr_accessor :framework
|
19
|
+
attr_accessor :session
|
20
|
+
attr_accessor :session_input
|
21
|
+
attr_accessor :session_output
|
22
|
+
|
23
|
+
def create_framework
|
24
|
+
return if @framework
|
25
|
+
@framework = Msf::Simple::Framework.create
|
26
|
+
end
|
27
|
+
|
28
|
+
# perform the setup only once
|
29
|
+
def setup_session
|
30
|
+
return if @session
|
31
|
+
|
32
|
+
# require the framework (assumes this sits in lib/lab/modifiers)
|
33
|
+
require 'msf/base'
|
34
|
+
|
35
|
+
create_framework ## TODO - this should use a single framework
|
36
|
+
## for all hosts, not one-per-host
|
37
|
+
|
38
|
+
@session = nil
|
39
|
+
@session_input = Rex::Ui::Text::Input::Buffer.new
|
40
|
+
@session_output = Rex::Ui::Text::Output::Buffer.new
|
41
|
+
|
42
|
+
if @os == "windows"
|
43
|
+
exploit_name = 'windows/smb/psexec'
|
44
|
+
|
45
|
+
# TODO - check for x86, choose the appropriate payload
|
46
|
+
|
47
|
+
payload_name = 'windows/meterpreter/bind_tcp'
|
48
|
+
options = { "RHOST" => @hostname,
|
49
|
+
"SMBUser" => @vm_user,
|
50
|
+
"SMBPass" => @vm_pass}
|
51
|
+
|
52
|
+
puts "DEBUG: using options #{options}"
|
53
|
+
|
54
|
+
# Initialize the exploit instance
|
55
|
+
exploit = @framework.exploits.create(exploit_name)
|
56
|
+
|
57
|
+
begin
|
58
|
+
# Fire it off.
|
59
|
+
@session = exploit.exploit_simple(
|
60
|
+
'Payload' => payload_name,
|
61
|
+
'Options' => options,
|
62
|
+
'LocalInput' => @session_input,
|
63
|
+
'LocalOutput' => @session_output)
|
64
|
+
@session.load_stdapi
|
65
|
+
|
66
|
+
puts "DEBUG: Generated session: #{@session}"
|
67
|
+
|
68
|
+
rescue Exception => e
|
69
|
+
puts "DEBUG: Unable to exploit"
|
70
|
+
puts e.to_s
|
71
|
+
end
|
72
|
+
|
73
|
+
else
|
74
|
+
module_name = 'scanner/ssh/ssh_login'
|
75
|
+
|
76
|
+
# TODO - check for x86, choose the appropriate payload
|
77
|
+
|
78
|
+
payload_name = 'linux/x86/shell_bind_tcp'
|
79
|
+
options = { "RHOSTS" => @hostname,
|
80
|
+
"USERNAME" => @vm_user,
|
81
|
+
"PASSWORD" => @vm_pass,
|
82
|
+
"BLANK_PASSWORDS" => false,
|
83
|
+
"USER_AS_PASS" => false,
|
84
|
+
"VERBOSE" => false}
|
85
|
+
|
86
|
+
puts "DEBUG: using options #{options}"
|
87
|
+
|
88
|
+
# Initialize the module instance
|
89
|
+
aux = @framework.auxiliary.create(module_name)
|
90
|
+
|
91
|
+
puts "DEBUG: created module: #{aux}"
|
92
|
+
|
93
|
+
begin
|
94
|
+
# Fire it off.
|
95
|
+
aux.run_simple(
|
96
|
+
'Payload' => payload_name,
|
97
|
+
'Options' => options,
|
98
|
+
'LocalInput' => @session_input,
|
99
|
+
'LocalOutput' => @session_output)
|
100
|
+
|
101
|
+
@session = @framework.sessions.first.last
|
102
|
+
puts "DEBUG: Generated session: #{@session}"
|
103
|
+
rescue Exception => e
|
104
|
+
puts "DEBUG: Unable to exploit"
|
105
|
+
puts e.to_s
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
def run_command(command, timeout=60)
|
114
|
+
|
115
|
+
setup_session
|
116
|
+
puts "Using session #{@session}"
|
117
|
+
|
118
|
+
# TODO: pass the timeout down
|
119
|
+
|
120
|
+
if @session
|
121
|
+
if @session.type == "shell"
|
122
|
+
puts "Running command via shell: #{command}"
|
123
|
+
@session.shell_command_token(command, timeout)
|
124
|
+
elsif @session.type == "meterpreter"
|
125
|
+
puts "Running command via meterpreter: #{command}"
|
126
|
+
@session.shell_command(command) #, timeout)
|
127
|
+
end
|
128
|
+
else
|
129
|
+
raise "No session"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
# This isn't part of the normal API, but too good to pass up.
|
135
|
+
def run_script(script, options)
|
136
|
+
if @session.type == "meterpreter"
|
137
|
+
@session.execute_script(script, options)
|
138
|
+
else
|
139
|
+
raise "Unsupported on #{@session.type}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# For meterpreter API compatibility
|
144
|
+
#def execute_file(script,options)
|
145
|
+
# run_script(script,options)
|
146
|
+
#end
|
147
|
+
|
148
|
+
def copy_to(local,remote)
|
149
|
+
setup_session
|
150
|
+
if @session.type == "meterpreter"
|
151
|
+
@session.run_cmd("upload #{local} #{remote}")
|
152
|
+
else
|
153
|
+
@driver.copy_to(local,remote)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def copy_from(local, remote)
|
158
|
+
setup_session
|
159
|
+
if @session.type == "meterpreter"
|
160
|
+
@session.run_cmd("download #{local} #{remote}")
|
161
|
+
else
|
162
|
+
@driver.copy_from(local,remote)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
data/lib/lab/version.rb
CHANGED
data/lib/lab/vm.rb
CHANGED
@@ -7,28 +7,34 @@ class Vm
|
|
7
7
|
|
8
8
|
attr_accessor :vmid
|
9
9
|
attr_accessor :hostname
|
10
|
-
attr_accessor :name
|
11
10
|
attr_accessor :description
|
11
|
+
attr_accessor :user
|
12
|
+
attr_accessor :host
|
12
13
|
attr_accessor :location
|
13
14
|
attr_accessor :driver
|
14
15
|
attr_accessor :credentials
|
15
16
|
attr_accessor :tools
|
16
17
|
attr_accessor :type
|
17
|
-
attr_accessor :user
|
18
|
-
attr_accessor :host
|
19
18
|
attr_accessor :os
|
20
19
|
attr_accessor :arch
|
20
|
+
attr_accessor :tags
|
21
21
|
|
22
22
|
## Initialize takes a vm configuration hash of the form
|
23
|
-
## - vmid (unique
|
24
|
-
##
|
23
|
+
## - vmid (unique id)
|
24
|
+
## hostname (unique name)
|
25
|
+
## description (describes the vm's contents)
|
26
|
+
## driver (vm driver)
|
27
|
+
## location (if applicable - local system)
|
25
28
|
## user (if applicable - remote system)
|
26
29
|
## host (if applicable - remote system)
|
27
|
-
## pass (if applicable - remote system)
|
28
|
-
##
|
30
|
+
## pass (if applicable - remote system) # DISCOURAGED - USE KEYS!
|
31
|
+
## tools (true if tools are installed)
|
32
|
+
## type ( qa / vulnerable / etc - freeform option)
|
29
33
|
## credentials (of the form [ {'user'=>"user",'pass'=>"pass", 'admin' => false}, ... ])
|
30
|
-
## os (currently
|
31
|
-
## arch (currently
|
34
|
+
## os (currently linux / windows / solaris / aix) - may be used in modifiers
|
35
|
+
## arch (currently 32 / 64)
|
36
|
+
## modifiers - can be anything in the modifiers directory
|
37
|
+
## tags - list of strings associated with this vm
|
32
38
|
|
33
39
|
def initialize(config = {})
|
34
40
|
|
@@ -48,7 +54,6 @@ class Vm
|
|
48
54
|
@driver_type.downcase!
|
49
55
|
|
50
56
|
@location = filter_input(config['location'])
|
51
|
-
#@name = config['name'] || ""
|
52
57
|
@description = config['description']
|
53
58
|
@tools = config['tools']
|
54
59
|
@os = config['os']
|
@@ -67,19 +72,19 @@ class Vm
|
|
67
72
|
@user = filter_input(config['user']) || nil
|
68
73
|
@host = filter_input(config['host']) || nil
|
69
74
|
@port = filter_input(config['port']) || nil
|
70
|
-
@pass = filter_input(config['pass']) || nil
|
75
|
+
@pass = filter_input(config['pass']) || nil # DISCORAGED, use keys!
|
71
76
|
|
72
|
-
#Only dynagen systems need this
|
77
|
+
# Only dynagen systems need this
|
73
78
|
@platform = config['platform']
|
74
79
|
|
75
|
-
#Only fog systems need this
|
80
|
+
# Only fog systems need this
|
76
81
|
@fog_config = config['fog_config']
|
77
82
|
|
78
|
-
#puts "Passing driver config: #{config}"
|
79
|
-
|
80
83
|
# Process the correct driver
|
81
84
|
if @driver_type == "workstation"
|
82
85
|
@driver = Lab::Drivers::WorkstationDriver.new(config)
|
86
|
+
elsif @driver_type == "remote_workstation"
|
87
|
+
@driver = Lab::Drivers::RemoteWorkstationDriver.new(config)
|
83
88
|
elsif @driver_type == "virtualbox"
|
84
89
|
@driver = Lab::Drivers::VirtualBoxDriver.new(config)
|
85
90
|
elsif @driver_type == "fog"
|
@@ -88,8 +93,8 @@ class Vm
|
|
88
93
|
@driver = Lab::Drivers::DynagenDriver.new(config, config['dynagen_config'])
|
89
94
|
elsif @driver_type == "remote_esxi"
|
90
95
|
@driver = Lab::Drivers::RemoteEsxiDriver.new(config)
|
91
|
-
elsif @driver_type == "
|
92
|
-
@driver = Lab::Drivers::
|
96
|
+
elsif @driver_type == "vsphere"
|
97
|
+
@driver = Lab::Drivers::VsphereDriver.new(config)
|
93
98
|
#elsif @driver_type == "qemu"
|
94
99
|
# @driver = Lab::Drivers::QemuDriver.new
|
95
100
|
#elsif @driver_type == "qemudo"
|
@@ -110,8 +115,14 @@ class Vm
|
|
110
115
|
@modifiers.each { |modifier| self.class.send(:include, eval("Lab::Modifier::#{modifier}"))}
|
111
116
|
rescue Exception => e
|
112
117
|
# modifier likely didn't exist
|
113
|
-
end
|
118
|
+
end
|
114
119
|
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Grab a tags array
|
123
|
+
#
|
124
|
+
@tags = config['tags']
|
125
|
+
|
115
126
|
end
|
116
127
|
|
117
128
|
def running?
|
@@ -205,7 +216,9 @@ class Vm
|
|
205
216
|
|
206
217
|
# Standard configuration options
|
207
218
|
out = " - vmid: #{@vmid}\n"
|
219
|
+
out += " hostname: #{@hostname}\n"
|
208
220
|
out += " driver: #{@driver_type}\n"
|
221
|
+
out += " description: |\n #{@description}\n"
|
209
222
|
out += " location: #{@location}\n"
|
210
223
|
out += " type: #{@type}\n"
|
211
224
|
out += " tools: #{@tools}\n"
|
@@ -215,6 +228,8 @@ class Vm
|
|
215
228
|
if @user or @host # Remote vm/drivers only
|
216
229
|
out += " user: #{@user}\n"
|
217
230
|
out += " host: #{@host}\n"
|
231
|
+
out += " port: #{@port}\n"
|
232
|
+
out += " pass: #{@pass}\n"
|
218
233
|
end
|
219
234
|
|
220
235
|
if @platform
|
data/lib/lab/vm_controller.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
#
|
2
|
+
# $Id$
|
3
|
+
#
|
4
|
+
# This is the main lab controller. Require this controller to get all
|
5
|
+
# lab functionality.
|
6
|
+
#
|
7
|
+
#
|
8
8
|
|
9
9
|
$:.unshift(File.expand_path(File.dirname(__FILE__)))
|
10
10
|
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), 'driver')))
|
@@ -29,16 +29,17 @@ module Controllers
|
|
29
29
|
class VmController
|
30
30
|
|
31
31
|
include Enumerable
|
32
|
-
include Lab::Controllers::WorkstationController
|
32
|
+
include Lab::Controllers::WorkstationController
|
33
|
+
include Lab::Controllers::RemoteWorkstationController
|
33
34
|
include Lab::Controllers::VirtualBoxController
|
34
35
|
include Lab::Controllers::FogController
|
35
36
|
include Lab::Controllers::DynagenController
|
36
37
|
include Lab::Controllers::RemoteEsxiController
|
37
|
-
include Lab::Controllers::
|
38
|
+
include Lab::Controllers::VsphereDriver
|
38
39
|
#include Lab::Controllers::QemuController
|
39
40
|
#include Lab::Controllers::QemudoController
|
40
41
|
def initialize (labdef=nil)
|
41
|
-
|
42
|
+
|
42
43
|
# Start with an empty array of vm objects
|
43
44
|
@vms = []
|
44
45
|
|
@@ -59,23 +60,31 @@ module Controllers
|
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
62
|
-
def find_by_vmid(
|
63
|
+
def find_by_vmid(search)
|
64
|
+
@vms.each do |vm|
|
65
|
+
return vm if vm.hostname.to_s.downcase == search.to_s.downcase
|
66
|
+
end
|
67
|
+
return nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def find_by_tag(search)
|
63
71
|
@vms.each do |vm|
|
64
|
-
|
65
|
-
return vm
|
72
|
+
vm.tags.each do |tag|
|
73
|
+
return vm if tag.downcase == search.to_s.downcase
|
66
74
|
end
|
67
75
|
end
|
68
76
|
return nil
|
69
77
|
end
|
70
78
|
|
71
|
-
def add_vm(vmid, location=nil, os=nil, tools=nil, credentials=nil, user=nil, host=nil)
|
72
|
-
@vms << Vm.new( {
|
79
|
+
def add_vm(vmid, location=nil, os=nil, tools=nil, credentials=nil, user=nil, host=nil)
|
80
|
+
@vms << Vm.new( {
|
73
81
|
'vmid' => vmid,
|
74
82
|
'driver' => type,
|
75
83
|
'location' => location,
|
76
84
|
'credentials' => credentials,
|
77
85
|
'user' => user,
|
78
|
-
'host' => host
|
86
|
+
'host' => host
|
87
|
+
})
|
79
88
|
end
|
80
89
|
|
81
90
|
def remove_by_vmid(vmid)
|
@@ -112,24 +121,30 @@ module Controllers
|
|
112
121
|
false
|
113
122
|
end
|
114
123
|
|
124
|
+
#
|
125
|
+
# Build a vm lab from a directory of files. Really only useful for file-based
|
126
|
+
# vm hosts. (vmware workstation)
|
127
|
+
#
|
115
128
|
def build_from_dir(driver_type, dir, clear=false)
|
116
|
-
|
129
|
+
|
117
130
|
if clear
|
118
131
|
@vms = []
|
119
132
|
end
|
120
133
|
|
121
134
|
if driver_type.downcase == "workstation"
|
122
135
|
vm_list = ::Lab::Controllers::WorkstationController::dir_list(dir)
|
136
|
+
elsif driver_type.downcase == "remote_workstation"
|
137
|
+
vm_list = ::Lab::Controllers::RemoteWorkstationController::dir_list(dir)
|
123
138
|
elsif driver_type.downcase == "virtualbox"
|
124
139
|
vm_list = ::Lab::Controllers::VirtualBoxController::dir_list(dir)
|
125
140
|
elsif driver_type.downcase == "fog"
|
126
141
|
vm_list = ::Lab::Controllers::FogController::dir_list(dir)
|
127
142
|
elsif driver_type.downcase == "Dynagen"
|
128
143
|
vm_list = ::Lab::Controllers::DynagenController::dir_list(dir)
|
129
|
-
elsif driver_type.downcase == "remote_workstation"
|
130
|
-
vm_list = ::Lab::Controllers::RemoteWorkstationController::dir_list(dir)
|
131
144
|
elsif driver_type.downcase == "remote_esxi"
|
132
145
|
vm_list =::Lab::Controllers::RemoteEsxiController::dir_list(dir)
|
146
|
+
elsif driver_type.downcase == "vsphere"
|
147
|
+
vm_list =::Lab::Controllers::VsphereController::dir_list(dir)
|
133
148
|
else
|
134
149
|
raise TypeError, "Unsupported VM Type"
|
135
150
|
end
|
@@ -139,7 +154,12 @@ module Controllers
|
|
139
154
|
end
|
140
155
|
end
|
141
156
|
|
142
|
-
|
157
|
+
|
158
|
+
#
|
159
|
+
# Builds a vm lab from all running vms. Handy for connecting and saving out
|
160
|
+
# a config or just managing the currently running vms
|
161
|
+
#
|
162
|
+
def build_from_running(driver_type=nil, user=nil, host=nil, clear=false, pass=nil)
|
143
163
|
|
144
164
|
if clear
|
145
165
|
@vms = []
|
@@ -148,69 +168,77 @@ module Controllers
|
|
148
168
|
case driver_type.intern
|
149
169
|
when :workstation
|
150
170
|
vm_list = ::Lab::Controllers::WorkstationController::running_list
|
151
|
-
|
152
171
|
vm_list.each do |item|
|
153
|
-
|
154
|
-
## Name the VM
|
172
|
+
# Name the VM
|
155
173
|
index = @vms.count + 1
|
156
|
-
|
157
|
-
|
158
|
-
|
174
|
+
# Add it to the vm list
|
175
|
+
@vms << Vm.new({
|
176
|
+
'vmid' => "vm_#{index}",
|
177
|
+
'driver' => driver_type,
|
178
|
+
'location' => item
|
179
|
+
})
|
180
|
+
end
|
181
|
+
when :remote_workstation
|
182
|
+
vm_list = ::Lab::Controllers::RemoteWorkstationController::running_list(user, host)
|
183
|
+
vm_list.each do |item|
|
184
|
+
# Name the VM
|
185
|
+
index = @vms.count + 1
|
186
|
+
# Add it to the VM list
|
187
|
+
@vms << Vm.new({
|
159
188
|
'vmid' => "vm_#{index}",
|
160
189
|
'driver' => driver_type,
|
161
190
|
'location' => item,
|
162
191
|
'user' => user,
|
163
|
-
'host' => host
|
192
|
+
'host' => host
|
193
|
+
})
|
164
194
|
end
|
165
|
-
|
166
|
-
|
167
195
|
when :virtualbox
|
168
196
|
vm_list = ::Lab::Controllers::VirtualBoxController::running_list
|
169
197
|
vm_list.each do |item|
|
170
|
-
|
198
|
+
# Add it to the vm list
|
171
199
|
@vms << Vm.new( {
|
172
200
|
'vmid' => "#{item}",
|
173
201
|
'driver' => driver_type,
|
174
|
-
'location' => nil
|
202
|
+
'location' => nil
|
203
|
+
})
|
175
204
|
end
|
176
205
|
when :fog
|
177
|
-
raise "Unsupported"
|
206
|
+
raise "Unsupported"
|
178
207
|
when :dynagen
|
179
208
|
raise "Unsupported"
|
180
|
-
when :
|
181
|
-
vm_list = ::Lab::Controllers::
|
182
|
-
|
209
|
+
when :remote_esxi
|
210
|
+
vm_list = ::Lab::Controllers::RemoteEsxiController::running_list(user,host)
|
183
211
|
vm_list.each do |item|
|
184
|
-
|
185
|
-
## Name the VM
|
186
|
-
index = @vms.count + 1
|
187
|
-
|
188
|
-
## Add it to the vm list
|
189
212
|
@vms << Vm.new( {
|
190
|
-
'vmid' => "
|
213
|
+
'vmid' => "#{item[:id]}",
|
214
|
+
'name' => "#{item[:name]}",
|
191
215
|
'driver' => driver_type,
|
192
|
-
'location' => item,
|
193
216
|
'user' => user,
|
194
|
-
'host' => host
|
217
|
+
'host' => host
|
218
|
+
})
|
195
219
|
end
|
196
|
-
when :
|
197
|
-
vm_list = ::Lab::Controllers::
|
198
|
-
|
220
|
+
when :vsphere
|
221
|
+
vm_list = ::Lab::Controllers::VsphereController::running_list(user,host,pass)
|
199
222
|
vm_list.each do |item|
|
200
223
|
@vms << Vm.new( {
|
201
224
|
'vmid' => "#{item[:id]}",
|
202
225
|
'name' => "#{item[:name]}",
|
203
226
|
'driver' => driver_type,
|
204
227
|
'user' => user,
|
205
|
-
'host' => host
|
228
|
+
'host' => host,
|
229
|
+
'pass' => pass
|
230
|
+
})
|
206
231
|
end
|
207
|
-
|
208
232
|
else
|
209
233
|
raise TypeError, "Unsupported VM Type"
|
210
234
|
end
|
211
235
|
|
212
236
|
end
|
213
237
|
|
238
|
+
#
|
239
|
+
# Applicable only to virtualbox. Reads the config file & parses / creates
|
240
|
+
# VM objects for each vm.
|
241
|
+
#
|
214
242
|
def build_from_config(driver_type=nil, user=nil, host=nil, clear=false)
|
215
243
|
if clear
|
216
244
|
@vms = []
|
@@ -221,7 +249,7 @@ module Controllers
|
|
221
249
|
vm_list = ::Lab::Controllers::VirtualBoxController::config_list
|
222
250
|
|
223
251
|
vm_list.each do |item|
|
224
|
-
|
252
|
+
# Add it to the vm list
|
225
253
|
@vms << Vm.new( {
|
226
254
|
'vmid' => "#{item}",
|
227
255
|
'driver' => driver_type,
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lab
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
16
|
-
requirement: &
|
16
|
+
requirement: &21345740 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *21345740
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: net-ssh
|
27
|
-
requirement: &
|
27
|
+
requirement: &21329820 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *21329820
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: net-scp
|
38
|
-
requirement: &
|
38
|
+
requirement: &21328760 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *21328760
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rbvmomi
|
49
|
-
requirement: &
|
49
|
+
requirement: &21328000 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :runtime
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *21328000
|
58
58
|
description: ! 'Start/Stop/Revert and do other cool stuff w/ Vmware, Virtualbox, and
|
59
59
|
ESXi vms. This gem wraps common CLI utilities and other gems to create a common
|
60
60
|
inteface for vms. '
|
@@ -76,10 +76,10 @@ files:
|
|
76
76
|
- lib/lab.rb
|
77
77
|
- lib/lab/controller/dynagen_controller.rb
|
78
78
|
- lib/lab/controller/fog_controller.rb
|
79
|
-
- lib/lab/controller/remote_esx_controller.rb
|
80
79
|
- lib/lab/controller/remote_esxi_controller.rb
|
81
80
|
- lib/lab/controller/remote_workstation_controller.rb
|
82
81
|
- lib/lab/controller/virtualbox_controller.rb
|
82
|
+
- lib/lab/controller/vsphere_controller.rb
|
83
83
|
- lib/lab/controller/workstation_controller.rb
|
84
84
|
- lib/lab/controller/workstation_vixr_controller.rb
|
85
85
|
- lib/lab/controllers.rb
|
@@ -89,18 +89,19 @@ files:
|
|
89
89
|
- lib/lab/driver/remote_workstation_driver.rb
|
90
90
|
- lib/lab/driver/virtualbox_driver.rb
|
91
91
|
- lib/lab/driver/vm_driver.rb
|
92
|
+
- lib/lab/driver/vsphere_driver.rb
|
92
93
|
- lib/lab/driver/workstation_driver.rb
|
93
94
|
- lib/lab/driver/workstation_vixr_driver.rb
|
94
95
|
- lib/lab/drivers.rb
|
95
96
|
- lib/lab/modifier/backtrack5_modifier.rb
|
96
97
|
- lib/lab/modifier/dos_modifier.rb
|
98
|
+
- lib/lab/modifier/meterpreter_modifier.rb
|
97
99
|
- lib/lab/modifier/test_modifier.rb
|
98
100
|
- lib/lab/modifiers.rb
|
99
101
|
- lib/lab/version.rb
|
100
102
|
- lib/lab/vm.rb
|
101
103
|
- lib/lab/vm_controller.rb
|
102
104
|
- src/Gemfile
|
103
|
-
- src/README.md
|
104
105
|
- src/Rakefile
|
105
106
|
- src/TODO
|
106
107
|
- src/config/test_lab.yml
|
data/src/README.md
DELETED
@@ -1,80 +0,0 @@
|
|
1
|
-
This folder contains the libraries necessary to run the lab plugin, and can also be used in a standalone way to automate virtual machines.
|
2
|
-
|
3
|
-
CONCEPTS:
|
4
|
-
=========
|
5
|
-
|
6
|
-
The lab provides a clean interface to common vm functions such as start / stop / snapshot / revert and even running system commands or higher-level functions like opening a browser to a specified URL. It's designed so the different VM technologies have a similiar interface, and you can ignore the specifics of the VM tech. The majority of the functionality is implemented in the form of drivers and controllers. Drivers implement the underlying command for each vm software (such as start/stop/revert), and controllers implement the commands which apply to all vms (such as listing all running vms, or cloning a vm).
|
7
|
-
|
8
|
-
If you're interested in porting a vm software (see below), please take a look at the workstation_driver.rb and the workstation_controller.rb -- This is a simple driver / controller in the lab, and you can simply copy / modify this to implement a new driver & controller for the software.
|
9
|
-
|
10
|
-
SUPPORTED VM TECHNOLOGIES:
|
11
|
-
==========================
|
12
|
-
NOTE: The lab libraries have only been tested with linux as a host, porting to windows is not planned at this time.
|
13
|
-
|
14
|
-
Implemented:
|
15
|
-
- workstation (Tested against 7.x)
|
16
|
-
- remote_workstation (Tested against 7.x)
|
17
|
-
- virtualbox (Tested against 4.x)
|
18
|
-
- remote_esx (VMware ESX Host Agent 4.1.0 build-348481)
|
19
|
-
|
20
|
-
Partially Implemented:
|
21
|
-
- amazon_ec2 (via fog gem)
|
22
|
-
- dynagen
|
23
|
-
|
24
|
-
Need Implementation:
|
25
|
-
- qemu
|
26
|
-
- qemudo
|
27
|
-
- others?
|
28
|
-
|
29
|
-
PLATFORM SUPPORT:
|
30
|
-
=================
|
31
|
-
You will need to have this code running on a linux box, Currently this has only been run / tested on Ubuntu 9.04 -> 10.04, though it should run on any linux with an ssh client and the dependencies below. Remote VM Hosts will need to be linux as well, though other platforms may work (untested). If you're interested in porting it to windows, please contact me (jcran).
|
32
|
-
|
33
|
-
Platform Dependencies:
|
34
|
-
- whatever vm software is necessary for the driver you're using (see SUPPORTED VM TECHNOLOGIES above)
|
35
|
-
- net/scp - the gem (net-scp). Required to copy files to/from the devices in the case that tools are not installed. Not necessary if tools are installed.
|
36
|
-
- fog - require to use the amazon_ec2 driver
|
37
|
-
|
38
|
-
STANDALONE API:
|
39
|
-
===============
|
40
|
-
BACKGROUND:
|
41
|
-
|
42
|
-
The lab libraries add tons of useful functionality that isn't exposed through the lab plugin, such as the ability to run commands on hosts. This library can serve as an excellent base for more complex operations on a remote host as well.
|
43
|
-
|
44
|
-
USAGE:
|
45
|
-
|
46
|
-
You must first create a yaml file which describes your vm. See data/lab/test_targets.yml for an example.
|
47
|
-
<pre>
|
48
|
-
require 'vm_controller'
|
49
|
-
vm_controller = ::Lab::Controllers::VmController.new(YAML.load_file(lab_def))
|
50
|
-
vm_controller['vm1'].start
|
51
|
-
vm_controller['vm1'].snapshot("clean")
|
52
|
-
vm_controller['vm1'].run_command("rm /etc/resolv.conf")
|
53
|
-
vm_controller['vm1'].open_uri("http://autopwn:8080")
|
54
|
-
vm_controller['vm1'].revert("clean")
|
55
|
-
vm_controller['vm1'].revert("clean")
|
56
|
-
</pre>
|
57
|
-
METASPLOIT MSFCONSOLE LAB PLUGIN:
|
58
|
-
=================================
|
59
|
-
|
60
|
-
BACKGROUND:
|
61
|
-
|
62
|
-
The lab plugin for msfconsole adds a number of commands which may be useful if you're interested in automating remote hosts with rc scripts, or if you need to control targets / support systems while utilizing the metasploit console. A potential use case is testing an IPS / IDS, and resetting the target after running each exploit.
|
63
|
-
|
64
|
-
USAGE:
|
65
|
-
|
66
|
-
Here's some example usage for the lab plugin.
|
67
|
-
<pre>
|
68
|
-
msf> load lab // Loads the lab plugin
|
69
|
-
msf> lab_load <path_to_lab_file> // Loads from a lab configuration file. See data/lab/test_targets.yml for an example
|
70
|
-
msf> lab_load_dir workstation /path/to/vmx/files // Loads from a local directory.
|
71
|
-
msf> lab_load_running remote_esx root esx_server // Loads all running vms.
|
72
|
-
msf> lab_start vm1 // Start a vm which was loaded above
|
73
|
-
msf> lab_snapshot vm1 snapshot_1 // Snapshot a vm as 'snapshot_1'
|
74
|
-
msf> lab_run_command ("rm -rf /") // oops!
|
75
|
-
msf> lab_show // Show all vms that we're aware of
|
76
|
-
msf> lab_show_running // Show only running vms
|
77
|
-
msf> lab_start vm2 // Start another vm
|
78
|
-
msf> lab_suspend vm1 // Suspend a vm
|
79
|
-
msf> lab_revert all snapshot_1 // Revert all vms back to 'snapshot_1'
|
80
|
-
</pre>
|