msfrpc-simple 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ .DS_Store
@@ -14,8 +14,6 @@ module Msf
14
14
  include Msf::RPC::Simple::Features::Framework
15
15
  include Msf::RPC::Simple::Features::Pro
16
16
 
17
- #attr_accessor :options
18
-
19
17
  # Public: Create a simple client object.
20
18
  #
21
19
  # user_options - hash of options to include in our initial connection.
@@ -26,16 +24,16 @@ module Msf
26
24
 
27
25
  # configure default options
28
26
  @options = {
29
- :project => user_options[:project] || "default",
27
+ :project => user_options[:project] || "default",
30
28
  :port => user_options[:project] || 55553,
31
- :user => user_options[:rpc_user],
32
- :pass => user_options[:rpc_pass],
29
+ :user => user_options[:rpc_user],
30
+ :pass => user_options[:rpc_pass],
33
31
  :db_host => user_options[:db_host] || "localhost",
34
32
  :db_user => user_options[:db_user],
35
33
  :db_pass => user_options[:db_pass],
36
- :db => user_options[:db_name] || "msf"
34
+ :db_name => user_options[:db_name] || "msf"
37
35
  }
38
-
36
+
39
37
  @options.merge!(user_options)
40
38
 
41
39
  #
@@ -43,29 +41,46 @@ module Msf
43
41
  #
44
42
  @client = Msf::RPC::Client.new(@options)
45
43
 
46
- #
47
- # Connect to the database based on the included options
48
- #
44
+ # connect to the database
49
45
  _connect_database
46
+ end
50
47
 
51
- #
52
- # Add a new workspace
53
- #
54
- @workspace_name = Time.now.utc.to_s.gsub(" ","_").gsub(":","_")
55
- _create_workspace
48
+ # Public: clean up after ourselves
49
+ #
50
+ # Returns nothing
51
+ def cleanup
52
+ _send_command("hosts -d")
53
+ _send_command("services -d")
54
+ _send_command("creds -d")
55
+ end
56
56
 
57
- #
58
- # Create a logger
59
- #
60
- #@logger = Msf::RPC::Simple::Logger.new
57
+ # Public: list all running threads
58
+ #
59
+ # Returns a hash of running threads
60
+ def list_threads
61
+ @client.call("core.thread_list")
62
+ end
63
+
64
+ # Public: determine if we're connected to the RPC server
65
+ #
66
+ # returns true/false
67
+ def rpc_connected?
68
+ return false unless @client.call("core.version")
69
+ true
70
+ end
71
+
72
+ # Public: determine if we're connected to the RPC server
73
+ #
74
+ # returns true/false
75
+ def db_connected?
76
+ return false unless _send_command("db_status") =~ /connected/
77
+ true
61
78
  end
62
79
 
63
80
  # Public: Creates and retuns an xml report
64
81
  #
65
82
  # This method is ugly for a number of reasons, but there doesn't
66
83
  # appear to be a way to be notified when the command is completed
67
- # nor when the
68
- #
69
84
  #
70
85
  # returns a valid xml string
71
86
  def create_report
@@ -79,73 +94,73 @@ module Msf
79
94
  begin
80
95
  xml_string = ""
81
96
  status = Timeout::timeout(240) {
82
- # We don't know when the file is going to show up, so
97
+ # We don't know when the file is going to show up, so
83
98
  # wait for it...
84
99
  until File.exists? report_path do
85
- sleep 1
100
+ sleep 1
86
101
  end
87
102
 
88
- # Read and clean up the file when it exists...
89
- until xml_string.include? "</MetasploitV4>" do
90
- sleep 5
91
- xml_string = File.read(report_path)
92
- end
93
-
94
- File.delete(report_path)
103
+ # Read and clean up the file when it exists...
104
+ until xml_string.include? "</MetasploitV4>" do
105
+ sleep 5
106
+ xml_string = File.read(report_path)
107
+ end
108
+
109
+ File.delete(report_path)
95
110
  }
96
111
  rescue Timeout::Error
97
112
  xml_string = "<MetasploitV4></MetasploitV4>"
98
113
  end
99
114
 
100
- xml_string
101
- end
102
-
103
- def cleanup
104
- #_send_command("workspace -d #{@workspace_name}")
105
- _send_command("db_disconnect")
115
+ xml_string
106
116
  end
107
117
 
108
- def connected?
109
- return true if @client.call("core.version")
110
- end
111
118
 
112
119
  private
113
120
 
121
+ # Private: connect to the database given the supplied / default options
122
+ #
123
+ # Returns nothing
114
124
  def _connect_database
115
- _send_command("db_connect #{@options[:db_user]}:#{@options[:db_pass]}@#{@options[:db_host]}/#{@options[:db]}")
116
- end
117
-
118
- def _create_workspace
119
- _send_command("workspace -a #{@workspace_name}")
125
+ _send_command("db_connect #{@options[:db_user]}:#{@options[:db_pass]}@#{@options[:db_host]}/#{@options[:db_name]}")
120
126
  end
121
127
 
128
+ # Private: provision a console and send the supplied command, capturing output
129
+ #
130
+ # Returns a valid string
122
131
  def _send_command(command)
123
- # Create the console and get its id
124
- console = @client.call("console.create")
132
+ # Create the console and get its id
133
+ console = @client.call("console.create")
134
+
135
+ # Do an initial read / discard to pull out any info on the console
136
+ # then write the command to the console
137
+ @client.call("console.read", console["id"])
138
+ @client.call("console.write", console["id"], "#{command}\n")
125
139
 
126
- # Do an initial read / discard to pull out any info on the console
127
- # then write the command to the console
128
- @client.call("console.read", console["id"])
129
- @client.call("console.write", console["id"], "#{command}\n")
140
+ # Initial read
141
+ output_string = ""
142
+ output = @client.call("console.read", console["id"])
143
+ output_string += "#{output['data']}"
130
144
 
131
- # Initial read
132
- output_string = ""
133
- output = @client.call("console.read", console["id"])
145
+ # Very very hacky. -- There should be a way to check
146
+ # status of a call to make sure that it isn't in an error
147
+ # state. For now, check the output for known error heuristics
148
+ return output_string if output_string =~ /(\[-\]|Error)/
134
149
 
135
150
  # Read until finished
136
- while (output["busy"] == true) do
151
+ while (!output.has_key?("result")) do
152
+ return unless output["busy"]
137
153
  output_string += "#{output['data']}"
138
154
  output = @client.call("console.read", console["id"])
139
- output_string = "Error" if output["result"] == "failure"
155
+ return "Error" if output["result"] == "failure"
140
156
  end
141
-
142
- # Clean up console
143
- #@client.call("console.destroy", console["id"])
144
157
 
145
- output_string
146
- end
158
+ # Clean up console
159
+ @client.call("console.destroy", console["id"])
147
160
 
161
+ output_string
162
+ end
148
163
  end
149
164
  end
150
165
  end
151
- end
166
+ end
@@ -1,114 +1,211 @@
1
1
  module Msf
2
2
  module RPC
3
3
  module Simple
4
- module Features
4
+ module Features
5
5
  module Framework
6
6
 
7
+ # Public: This module runs a db_nmap command
7
8
  #
8
- # This module runs a number of discovery modules
9
+ # range - an ipv4 ip address range in cidr format
9
10
  #
10
- def discover_range(range)
11
+ # This method should only be run after running setup
12
+ #
13
+ # Returns nothing
14
+ def nmap_range(range)
15
+
16
+ unique_string = Time.now.utc.to_s.gsub(" ","_").gsub(":","_")
17
+ nmap_report_path = "/tmp/metasploit_temp_#{unique_string}.xml"
18
+
19
+ # Call out to nmap to scan the given range
20
+ `nmap --top-ports 100 -oX #{nmap_report_path} #{range}`
21
+
22
+ # Import the XML into metasploit
23
+ _send_command("db_import #{nmap_report_path}")
24
+
25
+ # Wait for a few seconds while the xml is imported
26
+ sleep 10
27
+
28
+ # Remove the file
29
+ File.delete(nmap_report_path)
30
+
31
+ end
32
+
33
+ # Public: This module runs a number of discovery modules. This method should only
34
+ # be run after running setup, then the nmap_range method.
35
+ #
36
+ # host - an ipv4 ip address or hostname
37
+ #
38
+ # This method should only be run after running setup, and then the
39
+ # nmap_range method.
40
+ #
41
+ # Returns nothing
42
+
43
+ def discover_range(range, threads=25)
11
44
 
12
45
  # Other Potential options
13
46
  # - auxiliary/scanner/smb/pipe_auditor
14
47
  # - auxiliary/scanner/smb/pipe_dcerpc_auditor
15
48
  # - auxiliary/scanner/smb/smb_enumshares
16
49
  # - auxiliary/scanner/smb/smb_enumusers
17
- modules_and_options = [
18
- {:module_name => "auxiliary/scanner/http/http_version"},
19
- #{:module_name => "auxiliary/scanner/http/cert"},
20
- {:module_name => "auxiliary/scanner/ftp/ftp_version"},
21
- {:module_name => "auxiliary/scanner/h323/h323_version"},
22
- {:module_name => "auxiliary/scanner/imap/imap_version"},
23
- #{:module_name => "auxiliary/scanner/portscan/syn"},
24
- #{:module_name => "auxiliary/scanner/portscan/tcp"},
25
- #{:module_name => "auxiliary/scanner/lotus/lotus_domino_version"},
26
- {:module_name => "auxiliary/scanner/mysql/mysql_version"},
27
- #{:module_name => "auxiliary/scanner/netbios/nbname"},
28
- #{:module_name => "auxiliary/scanner/netbios/nbname_probe"},
29
- #{:module_name => "auxiliary/scanner/pcanywhere/pcanywhere_tcp"},
30
- #{:module_name => "auxiliary/scanner/pcanywhere/pcanywhere_udp"},
31
- {:module_name => "auxiliary/scanner/pop3/pop3_version"},
32
- {:module_name => "auxiliary/scanner/postgres/postgres_version"},
33
- {:module_name => "auxiliary/scanner/smb/smb_version"},
34
- {:module_name => "auxiliary/scanner/snmp/snmp_enum"},
35
- {:module_name => "auxiliary/scanner/ssh/ssh_version"},
36
- {:module_name => "auxiliary/scanner/telnet/telnet_version"},
37
- #{:module_name => "auxiliary/scanner/vmware/vmauthd_version"},
50
+ modules_and_options = [
51
+ {:module_name => "auxiliary/scanner/http/http_version", :module_options => {}},
52
+ {:module_name => "auxiliary/scanner/http/cert", :module_options => {}},
53
+ {:module_name => "auxiliary/scanner/ftp/ftp_version", :module_options => {}},
54
+ {:module_name => "auxiliary/scanner/h323/h323_version", :module_options => {}},
55
+ {:module_name => "auxiliary/scanner/imap/imap_version", :module_options => {}},
56
+ {:module_name => "auxiliary/scanner/portscan/syn", :module_options => {}},
57
+ {:module_name => "auxiliary/scanner/portscan/tcp", :module_options => {}},
58
+ #{:module_name => "auxiliary/scanner/lotus/lotus_domino_version", :module_options => {}},
59
+ {:module_name => "auxiliary/scanner/mysql/mysql_version", :module_options => {}},
60
+ {:module_name => "auxiliary/scanner/netbios/nbname", :module_options => {}},
61
+ {:module_name => "auxiliary/scanner/netbios/nbname_probe"},
62
+ #{:module_name => "auxiliary/scanner/pcanywhere/pcanywhere_tcp", :module_options => {}},
63
+ #{:module_name => "auxiliary/scanner/pcanywhere/pcanywhere_udp", :module_options => {}},
64
+ {:module_name => "auxiliary/scanner/pop3/pop3_version", :module_options => {}},
65
+ {:module_name => "auxiliary/scanner/postgres/postgres_version", :module_options => {}},
66
+ {:module_name => "auxiliary/scanner/smb/smb_version", :module_options => {}},
67
+ {:module_name => "auxiliary/scanner/snmp/snmp_enum", :module_options => {}},
68
+ {:module_name => "auxiliary/scanner/ssh/ssh_version", :module_options => {}},
69
+ {:module_name => "auxiliary/scanner/telnet/telnet_version", :module_options => {}},
70
+ #{:module_name => "auxiliary/scanner/vmware/vmauthd_version", :module_options => {}}
38
71
  ]
39
72
 
40
- output = ""
41
- module_list.each do |m|
73
+ module_list.each do |mod|
42
74
  # Merge in default options
43
- m[:module_option_string] = "RHOSTS #{range}"
75
+ mod[:options] = {
76
+ "RHOSTS" => "#{range}",
77
+ "THREADS" => "#{threads}"
78
+ }
44
79
 
45
- # store this module's name in the output
46
- output += "=== #{m[:module_name]} ===\n"
47
- output += execute_module_and_return_output(m)
48
- end
80
+ # Module specific options
81
+ mod[:options].merge!(mod[:module_options])
49
82
 
50
- output
83
+ # execute the module
84
+ execute_module(mod)
85
+ end
51
86
  end
52
87
 
53
88
 
89
+ # Public: This module runs a number of bruteforce modules. This method should only
90
+ # be run after running setup, then the nmap_range method.
91
+ #
92
+ # host - an ipv4 ip address or hostname
54
93
  #
55
- # This module runs a number of _login modules
94
+ # This method should only be run after running setup, and then the
95
+ # nmap_range method.
56
96
  #
57
- def bruteforce_range(range)
97
+ # Returns nothing
98
+ def bruteforce_range(range, user_file, pass_file, threads=25)
58
99
 
59
100
  module_list = [
60
- {:module_name => "auxiliary/scanner/ftp/ftp_login"},
61
- {:module_name => "auxiliary/scanner/http/http_login"},
62
- {:module_name => "auxiliary/scanner/smb/smb_login"},
63
- {:module_name => "auxiliary/scanner/mssql/mssql_login"},
64
- {:module_name => "auxiliary/scanner/mysql/mysql_login"},
65
- {:module_name => "auxiliary/scanner/pop3/pop3_login"},
66
- {:module_name => "auxiliary/scanner/smb/smb_login"},
67
- {:module_name => "auxiliary/scanner/snmp/snmp_login"},
68
- {:module_name => "auxiliary/scanner/ssh/ssh_login"},
69
- {:module_name => "auxiliary/scanner/telnet/telnet_login"},
101
+ {:module_name => "auxiliary/scanner/http/http_login", :module_options => {}},
102
+ {:module_name => "auxiliary/scanner/smb/smb_login", :module_options => {}},
103
+ {:module_name => "auxiliary/scanner/snmp/snmp_login", :module_options => {}},
104
+ {:module_name => "auxiliary/scanner/ssh/ssh_login", :module_options => {"SSH_TIMEOUT" => 3}}
70
105
  ]
71
106
 
72
- output = ""
73
- module_list.each do |m|
74
- #m[:module_option_string] = "RHOSTS #{range}, USER_FILE /opt/metasploit/msf3/data/wordlists/unix_users.txt, PASS_FILE /opt/metasploit/msf3/data/wordlists/unix_passwords.txt"
75
- m[:module_option_string] = "RHOSTS #{range}, USERNAME root, PASSWORD root"
107
+ # Iterate through modules in the list, adding in generic and module-specific options
108
+ # if they exist
109
+ module_list.each do |mod|
110
+
111
+ # Generic module options
112
+ mod[:options] = {
113
+ "RHOSTS" => "#{range}",
114
+ "USER_FILE" => "#{user_file}",
115
+ "PASS_FILE" => "#{pass_file}",
116
+ "THREADS" => "#{threads}"
117
+ }
118
+ # Module specific options
119
+ mod[:options].merge!(mod[:module_options])
76
120
 
77
- # store this module's name in the output
78
- output += "=== #{m[:module_name]} ===\n"
79
- output += execute_module_and_return_output(m)
121
+ execute_module(mod)
80
122
  end
81
123
 
82
- output
83
124
  end
84
125
 
126
+ # Public: This module runs a number of exploit modules. This method should only
127
+ # be run after running setup, then the nmap_range method.
128
+ #
129
+ # host - an ipv4 ip address or hostname
85
130
  #
86
- # This module runs a number of exploit modules
131
+ # This method should only be run after running setup, and then the
132
+ # nmap_range method.
87
133
  #
88
- def exploit_range(range)
89
- return "Not Implemented"
134
+ # Returns nothing
135
+ def exploit_single(host)
136
+
137
+ # TODO - will need to set up / manage a handler - should this go
138
+ # back to the console?
139
+
140
+ module_list = [
141
+ { :module_name => "exploit/windows/smb/ms08_067_netapi", :module_options => {} }
142
+ ]
143
+
144
+ module_list.each do |mod|
145
+ mod[:options] = { "RHOST" => "#{host}" }
146
+ mod[:options].merge!(mod[:module_options])
147
+ execute_module(mod)
148
+ end
90
149
  end
91
150
 
151
+ # Public: This method executes a specified metasploit module
92
152
  #
93
- # This module simply runs a module
153
+ # params - A parameters hash containing:
154
+ # - :module_name - a full metasploit module name
155
+ # - :options - a hash of options to be "set" for the module
94
156
  #
95
- def execute_module_and_return_output(options)
96
- module_name = options[:module_name]
97
- module_option_string = options[:module_option_string]
157
+ # Note that typical behavior for metasploit when calling "module.execute" is to
158
+ # background the task. This method waits for the task to complete, thereby
159
+ # allowing you to fire this method, then interact with the database to find
160
+ # the requisite result(s).
161
+ #
162
+ # returns nothing
163
+ def execute_module(params)
164
+ module_name = params[:module_name]
165
+ module_type = params[:module_name].split("/").first
166
+ module_options = params[:options]
167
+ raise "Error, bad module name" unless ["exploit", "auxiliary", "post", "encoder", "nop"].include? module_type
168
+
169
+ # Execute the module and obtain the job details
170
+ job_details = @client.call("module.execute", module_type, module_name, module_options)
171
+
172
+ while @client.call("job.list").has_key?(job_details["job_id"].to_s)
173
+ # Wait while the module is executed in the background
174
+ sleep 1
175
+ end
176
+
177
+ end
178
+ =begin
179
+ # Public: execute_module_and_return_output
180
+ #
181
+ # This method runs a module in a metasploit console and captures the output
182
+ # to a string which is returned at the end of the module. An attempt is made
183
+ # to remove the metasploit banner, which is simply noise
184
+ #
185
+ # Note that this is currently deprecated, but preserved here in case a different
186
+ # approach (text output needed) to running modules is req'd. Please use the
187
+ # execute_module method to run a module going forward. While output is not
188
+ # preserved when running that method, all appropriate results should be captured
189
+ # in the database
190
+ #
191
+ # Returns a string with module output
192
+ def execute_module_and_return_output(params)
193
+ module_name = params[:module_name]
194
+ module_option_string = params[:module_option_string]
98
195
 
99
196
  # split up the module name into type / name
100
197
  module_type = module_name.split("/").first
101
- raise "Error, bad module name" unless ["exploit", "auxiliary", "post", "encoder", "nop"].include? module_type
102
-
198
+ raise "Error, bad module name" unless ["exploit", "auxiliary", "post", "encoder", "nop"].include? module_type
199
+
103
200
  # TODO - we may have to deal w/ targets somehow
104
201
 
105
202
  #info = @client.call("module.execute", module_type, module_name, module_options)
106
203
  #@client.call("job.info", info["job_id"])
107
204
 
108
- # The module output will be not available when run this way; to
205
+ # The module output will be not available when run this way; to
109
206
  # capture the result of the print_* commands, you have to set the
110
207
  # output driver of the module to something you can read from (Buffer,
111
- # File, etc). For your use case, the best bet is to run the module
208
+ # File, etc). For your use case, the best bet is to run the module
112
209
  # via the Console API instead of module.execute, and use that to read
113
210
  # the output from the console itself, which provides buffer output for you.
114
211
  output = ""
@@ -148,7 +245,7 @@ module Msf
148
245
  # do an initial read of the module's output
149
246
  module_output = @client.call("console.read", console_id)
150
247
  output += "#{module_output['data']}"
151
-
248
+
152
249
  until !module_output["busy"] do
153
250
  module_output = @client.call("console.read", console_id)
154
251
  output += "#{module_output['data']}"
@@ -161,14 +258,14 @@ module Msf
161
258
  output += "#{module_output['data']}"
162
259
  end
163
260
 
164
- # Clean up
261
+ # Clean up
165
262
  @client.call("console.destroy", console_id)
166
263
 
167
264
  output
168
265
  end
169
-
266
+ =end
170
267
  end
171
268
  end
172
269
  end
173
270
  end
174
- end
271
+ end
@@ -1,7 +1,7 @@
1
1
  module Msf
2
2
  module RPC
3
3
  module Simple
4
- VERSION = "0.0.7"
4
+ VERSION = "0.0.8"
5
5
  end
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: msfrpc-simple
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-21 00:00:00.000000000 Z
12
+ date: 2013-07-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: msfrpc-client
@@ -34,6 +34,7 @@ executables: []
34
34
  extensions: []
35
35
  extra_rdoc_files: []
36
36
  files:
37
+ - .gitignore
37
38
  - Gemfile
38
39
  - Gemfile.lock
39
40
  - LICENSE
@@ -41,11 +42,8 @@ files:
41
42
  - Rakefile
42
43
  - build.sh
43
44
  - doc/NOTES
44
- - lib/.DS_Store
45
45
  - lib/msfrpc-simple.rb
46
- - lib/msfrpc-simple/.DS_Store
47
46
  - lib/msfrpc-simple/client.rb
48
- - lib/msfrpc-simple/features/.DS_Store
49
47
  - lib/msfrpc-simple/features/framework.rb
50
48
  - lib/msfrpc-simple/features/pro.rb
51
49
  - lib/msfrpc-simple/logger.rb
@@ -76,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
74
  version: '0'
77
75
  requirements: []
78
76
  rubyforge_project:
79
- rubygems_version: 1.8.24
77
+ rubygems_version: 1.8.23
80
78
  signing_key:
81
79
  specification_version: 3
82
80
  summary: This library provides a simple-to-use wrapper for the Rapid7 Metasploit RPC
Binary file
Binary file