msfrpc-simple 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -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