lab 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in lab.gemspec
4
+ gemspec
@@ -0,0 +1,16 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ lab (0.0.1)
5
+ nokogiri
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ nokogiri (1.5.0)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ lab!
data/README ADDED
@@ -0,0 +1,77 @@
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
+ 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).
6
+
7
+ 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.
8
+
9
+ ==SUPPORTED VM TECHNOLOGIES:
10
+
11
+ NOTE: The lab libraries have only been tested on linux, porting to windows is not planned at this time.
12
+
13
+ Implemented:
14
+ - workstation (Tested against 7.x)
15
+ - remote_workstation (Tested against 7.x)
16
+ - virtualbox (Tested against 4.x)
17
+ - remote_esx (VMware ESX Host Agent 4.1.0 build-348481)
18
+
19
+ Partially Implemented:
20
+ - amazon_ec2 (via fog gem)
21
+ - dynagen
22
+
23
+ Need Implementation:
24
+ - qemu
25
+ - qemudo
26
+ - others?
27
+
28
+ ==PLATFORM SUPPORT:
29
+
30
+ 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).
31
+
32
+ Platform Dependencies:
33
+ - whatever vm software is necessary for the driver you're using (see SUPPORTED VM TECHNOLOGIES above)
34
+ - 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.
35
+ - fog - require to use the amazon_ec2 driver
36
+
37
+ ==STANDALONE API:
38
+
39
+ BACKGROUND:
40
+
41
+ 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.
42
+
43
+ USAGE:
44
+
45
+ You must first create a yaml file which describes your vm. See data/lab/test_targets.yml for an example.
46
+
47
+ require 'vm_controller'
48
+ vm_controller = ::Lab::Controllers::VmController.new(YAML.load_file(lab_def))
49
+ vm_controller['vm1'].start
50
+ vm_controller['vm1'].snapshot("clean")
51
+ vm_controller['vm1'].run_command("rm /etc/resolv.conf")
52
+ vm_controller['vm1'].open_uri("http://autopwn:8080")
53
+ vm_controller['vm1'].revert("clean")
54
+ vm_controller['vm1'].revert("clean")
55
+
56
+ ==METASPLOIT MSFCONSOLE LAB PLUGIN:
57
+
58
+ BACKGROUND:
59
+
60
+ 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.
61
+
62
+ USAGE:
63
+
64
+ Here's some example usage for the lab plugin.
65
+
66
+ msf> load lab // Loads the lab plugin
67
+ msf> lab_load <path_to_lab_file> // Loads from a lab configuration file. See data/lab/test_targets.yml for an example
68
+ msf> lab_load_dir workstation /path/to/vmx/files // Loads from a local directory.
69
+ msf> lab_load_running remote_esx root esx_server // Loads all running vms.
70
+ msf> lab_start vm1 // Start a vm which was loaded above
71
+ msf> lab_snapshot vm1 snapshot_1 // Snapshot a vm as 'snapshot_1'
72
+ msf> lab_run_command ("rm -rf /") // oops!
73
+ msf> lab_show // Show all vms that we're aware of
74
+ msf> lab_show_running // Show only running vms
75
+ msf> lab_start vm2 // Start another vm
76
+ msf> lab_suspend vm1 // Suspend a vm
77
+ msf> lab_revert all snapshot_1 // Revert all vms back to 'snapshot_1'
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/TODO ADDED
@@ -0,0 +1,15 @@
1
+ This is a list of basic priorities for the lab code...
2
+
3
+ * Implement more technologies
4
+
5
+ a) finish amazon ec2 (via fog)
6
+ b) qemu
7
+ c) qemudo
8
+ d) kvm
9
+ e) other cloud technologies (newservers, slicehost/rackspace,etc)
10
+
11
+ * Implement a cloning function on each controller
12
+
13
+ * Support Windows as a host platform. Currently all the code assumes a linux host is running it. The same applies for the remote_* drivers -- they've not been tested on windows.
14
+
15
+ * Consolidate the remote_system_command code & provide a filter. Create an unsafe_system_command and unsafe_remote_system_command function call for when we control the entire string.
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "lab/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "lab"
7
+ s.version = Lab::VERSION
8
+ s.authors = ["Jonathan Cran"]
9
+ s.email = ["jcran@pentestify.com"]
10
+ s.homepage = "http://www.pentestify.com/projects/lab"
11
+ s.summary = %q{Manage vms like a boss.}
12
+ s.description = %q{Start/Stop/Revert and do other cool stuff w/ Vmware, Virtualbox, and ESXi vms}
13
+
14
+ s.rubyforge_project = "lab"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ # s.add_development_dependency "rspec"
23
+ s.add_runtime_dependency "nokogiri"
24
+ end
@@ -0,0 +1,4 @@
1
+ # TODO - use autoload
2
+
3
+ require 'lab/vm_controller'
4
+ require 'lab/version'
@@ -0,0 +1,14 @@
1
+ module Lab
2
+ module Controllers
3
+ module DynagenController
4
+
5
+ def self.running_list
6
+ raise "Unsupported"
7
+ end
8
+
9
+ def self.dir_list(basepath=nil)
10
+ raise "Unsupported"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,6 @@
1
+ module Lab
2
+ module Controllers
3
+ module FogController
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,62 @@
1
+ # This controller was built against:
2
+ # VMware ESX Host Agent 4.1.0 build-348481
3
+
4
+ module Lab
5
+ module Controllers
6
+ module RemoteEsxController
7
+
8
+ # Note that 3.5 was different (vmware-vim-cmd)
9
+ VIM_CMD = 'vim-cmd'.freeze
10
+
11
+ def self.dir_list(basepath=nil)
12
+ # Does this method really even make sense for esx?
13
+ return "Unsupported :("
14
+ end
15
+
16
+ def self.running_list(user, host)
17
+ user.gsub!(/(\W)*/, '')
18
+ host.gsub!(/(\W)*/, '')
19
+
20
+ # first get all registered vms
21
+ registered_vms = self.get_vms(user, host) || []
22
+ running_vms = []
23
+
24
+ # now let's see which ones are running
25
+ # TODO: this is ghetto, would be better not to connect repeatedly
26
+ registered_vms.each do |vm|
27
+ remote_cmd = "ssh #{user}@#{host} \"#{VIM_CMD} vmsvc/power.getstate #{vm[:id]}\""
28
+ raw = `#{remote_cmd}`
29
+ running_vms << vm if raw =~ /Powered on/
30
+ end
31
+
32
+ return running_vms
33
+ end
34
+
35
+ private
36
+
37
+ def self.get_vms(user, host)
38
+ user.gsub!(/(\W)*/, '')
39
+ host.gsub!(/(\W)*/, '')
40
+
41
+ vms = [] # array of VM hashes
42
+ remote_cmd = "ssh #{user}@#{host} \"#{VIM_CMD} vmsvc/getallvms | grep ^[0-9] | sed 's/[[:blank:]]\\{3,\\}/ /g'\""
43
+ raw = `#{remote_cmd}`.split("\n")
44
+
45
+ raw.each do |line|
46
+ # So effing ghetto
47
+ id_and_name = line.split('[datastore').first
48
+ id = id_and_name.split(' ').first
49
+
50
+ ## TODO - there's surely a better way to do this.
51
+ name_array = id_and_name.split(' ')
52
+ name_array.shift
53
+ name = name_array.join(' ')
54
+ vms << {:id => id, :name => name}
55
+ end
56
+
57
+ return vms
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,22 @@
1
+ module Lab
2
+ module Controllers
3
+ module RemoteWorkstationController
4
+
5
+ def self.running_list(user, host)
6
+ user.gsub!(/(\W)*/, '')
7
+ host.gsub!(/(\W)*/, '')
8
+
9
+ remote_cmd = "ssh #{user}@#{host} \"vmrun list nogui\""
10
+ vm_list = `#{remote_cmd}`.split("\n")
11
+ vm_list.shift
12
+
13
+ return vm_list
14
+ end
15
+
16
+ def self.dir_list(basepath=nil)
17
+ vm_list = Find.find(basepath).select { |f| f =~ /\.vmx$/ }
18
+ return vm_list
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ module Lab
2
+ module Controllers
3
+ module VirtualBoxController
4
+
5
+ def self.running_list
6
+ vm_names_and_uuids = `VBoxManage list runningvms`
7
+ return vm_names_and_uuids.scan(/\"(.*)\" {.*}/).flatten
8
+ end
9
+
10
+ def self.config_list
11
+ vm_names_and_uuids = `VBoxManage list vms`
12
+ return vm_names_and_uuids.scan(/\"(.*)\" {.*}/).flatten
13
+ end
14
+
15
+ def self.config_list_uuid
16
+ vm_names_and_uuids = `VBoxManage list vms`
17
+ return vm_names_and_uuids.scan(/\".*\" {(.*)}/).flatten
18
+ end
19
+
20
+ def self.dir_list(basepath=nil)
21
+ vm_list = Find.find(basepath).select { |f| f =~ /\.xml$/ }
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ module Lab
2
+ module Controllers
3
+ module WorkstationController
4
+
5
+ def self.running_list
6
+ vm_list = `vmrun list`.split("\n")
7
+ vm_list.shift
8
+ return vm_list
9
+ end
10
+
11
+ def self.dir_list(basepath=nil)
12
+ vm_list = Find.find(basepath).select { |f| f =~ /\.vmx$/ }
13
+ return vm_list
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ module Lab
2
+ module Controllers
3
+ module WorkstationVixrController
4
+
5
+ def self.running_list
6
+ vm_list = `vmrun list`.split("\n")
7
+ vm_list.shift
8
+
9
+ return vm_list
10
+ end
11
+
12
+ def self.dir_list(basepath=nil)
13
+ vm_list = Find.find(basepath).select { |f| f =~ /\.vmx$/ }
14
+
15
+ return vm_list
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ require 'controller/workstation_controller'
2
+ require 'controller/virtualbox_controller'
3
+ require 'controller/fog_controller'
4
+ require 'controller/dynagen_controller'
5
+ require 'controller/remote_workstation_controller'
6
+ require 'controller/remote_esx_controller'
7
+ #require 'controller/qemu_controller'
8
+ #require 'controller/qemudo_controller'
9
+
10
+
@@ -0,0 +1,47 @@
1
+ require 'vm_driver'
2
+
3
+ #
4
+ # $Id$
5
+ #
6
+
7
+ #
8
+ # To use this driver, you have to have a lab which is preconfigured. The best / easiest
9
+ # way i've found to to set up a lab is GNS3
10
+ #
11
+
12
+ module Lab
13
+ module Drivers
14
+ class DynagenDriver < VmDriver
15
+ def initialize(config,dynagen_config)
16
+ super(config)
17
+ @running = false
18
+ @dynagen_platform = filter_command(dynagen_config['dynagen_platform'])
19
+ end
20
+
21
+ def start
22
+ # TODO - write the location-file to a temp-file
23
+ # and set the autostart property
24
+
25
+ ## start background dynamips process
26
+ system_command("dynamips -H #{@dynagen_platform} &")
27
+ system_command("dynagen #{@location}")
28
+ @running = true
29
+ end
30
+
31
+ def stop
32
+ system_command("killall dynagen")
33
+ @running = false
34
+ end
35
+
36
+ def cleanup
37
+ `killall dynagen`
38
+ `killall dynamips`
39
+ @running = false
40
+ end
41
+
42
+ def running?
43
+ return @running
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,164 @@
1
+ require 'vm_driver'
2
+
3
+ ##
4
+ ## $Id$
5
+ ##
6
+
7
+ module Lab
8
+ module Drivers
9
+ class FogDriver < VmDriver
10
+
11
+ def initialize(config,fog_config)
12
+
13
+ super(config)
14
+ @fog_config = fog_config
15
+
16
+ # Soft dependency
17
+ begin
18
+ require 'fog'
19
+ rescue LoadError
20
+ raise "WARNING: Library fog not found. Could Not Create Driver"
21
+ end
22
+
23
+ if @fog_config['fog_type'] == "ec2"
24
+
25
+ # AWS / EC2 Base Credential Configuration
26
+ @aws_cert_file = IO.read(fog_config['fog_aws_cert_file']).chomp if fog_config['fog_aws_cert_file']
27
+ @aws_private_key_file = IO.read(fog_config['fog_aws_private_key_file']).chomp if fog_config['fog_aws_private_key_file']
28
+ @ec2_access_key_file = IO.read(fog_config['fog_ec2_access_key_file']).chomp if fog_config['fog_ec2_access_key_file']
29
+ @ec2_secret_access_key_file = IO.read(fog_config['fog_ec2_secret_access_key_file']).chomp if fog_config['fog_ec2_secret_access_key_file']
30
+
31
+ # Instance Keys
32
+ @ec2_instance_public_key_file = IO.read(fog_config['fog_ec2_instance_public_key_file']).chomp if fog_config['fog_ec2_instance_public_key_file']
33
+ @ec2_instance_private_key_file = IO.read(fog_config['fog_ec2_instance_private_key_file']).chomp if fog_config['fog_ec2_instance_private_key_file']
34
+
35
+ # Instance Details
36
+ @ec2_base_ami = fog_config['fog_ec2_base_ami']
37
+ @ec2_flavor = fog_config['fog_ec2_flavor']
38
+ @ec2_user = fog_config['fog_ec2_user']
39
+ @ec2_region = fog_config['fog_ec2_region']
40
+
41
+ # Set up a connection
42
+ @compute = Fog::Compute.new(
43
+ :provider => "Aws",
44
+ :aws_access_key_id => @aws_access_key_file,
45
+ :aws_secret_access_key => @aws_secret_access_key_file )
46
+ else
47
+ raise "Unsupported Fog Type"
48
+ end
49
+ end
50
+
51
+ def start
52
+ ec2_settings = {
53
+ :image_id => @ec2_base_ami,
54
+ :flavor_id => @ec2_flavor,
55
+ :public_key_path => @ec2_instance_public_key_file,
56
+ :private_key_path => @ec2_instance_private_key_file,
57
+ :username => @ec2_user}
58
+
59
+ begin
60
+ @fog_server = @compute.servers.bootstrap(ec2_settings)
61
+ rescue Fog::Compute::AWS::Error => e
62
+ raise "Couldn't authenticate to AWS - did you place keys in the creds/ directory?"
63
+ exit
64
+ end
65
+ end
66
+
67
+ def stop
68
+ @fog_server.destroy
69
+ end
70
+
71
+ def suspend
72
+ raise "unimplemented"
73
+ end
74
+
75
+ def pause
76
+ raise "unimplemented"
77
+ end
78
+
79
+ def reset
80
+ raise "unimplemented"
81
+ end
82
+
83
+ def create_snapshot(snapshot)
84
+ raise "unimplemented"
85
+ end
86
+
87
+ def revert_snapshot(snapshot)
88
+ raise "unimplemented"
89
+ end
90
+
91
+ def delete_snapshot(snapshot)
92
+ raise "unimplemented"
93
+ end
94
+
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
+ def cleanup
155
+ @fog_server.destroy
156
+ end
157
+
158
+ def running?
159
+ return true #TODO
160
+ end
161
+
162
+ end
163
+ end
164
+ end