msfrpc-simple 0.0.4 → 0.0.5

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.
@@ -3,6 +3,7 @@ require 'msfrpc-client'
3
3
  require 'features/framework'
4
4
  require 'features/pro'
5
5
  require 'module_mapper'
6
+ require 'timeout'
6
7
  require 'logger'
7
8
 
8
9
  module Msf
@@ -13,30 +14,26 @@ module Msf
13
14
  include Msf::RPC::Simple::Features::Framework
14
15
  include Msf::RPC::Simple::Features::Pro
15
16
 
16
- attr_accessor :options
17
+ #attr_accessor :options
17
18
 
18
19
  # Public: Create a simple client object.
19
20
  #
20
- # opts - hash of options to include in our initial connection.
21
+ # user_options - hash of options to include in our initial connection.
21
22
  # project - project name we want to use for this connection.
22
23
  #
23
24
  # Returns nothing.
24
25
  def initialize(user_options)
25
26
 
26
- # db username
27
- # db password
28
-
29
- #
30
- # Merge our project in, and set this as the project we'll
31
- # use going forward.
32
- #
27
+ # configure default options
33
28
  @options = {
34
29
  :project => user_options[:project] || "default",
35
30
  :port => user_options[:project] || 55553,
36
31
  :user => user_options[:rpc_user],
37
32
  :pass => user_options[:rpc_pass],
33
+ :db_host => user_options[:db_host] || "localhost",
38
34
  :db_user => user_options[:db_user],
39
- :db_pass => user_options[:db_pass]
35
+ :db_pass => user_options[:db_pass],
36
+ :db => user_options[:db_name] || "msf"
40
37
  }
41
38
 
42
39
  @options.merge!(user_options)
@@ -46,18 +43,108 @@ module Msf
46
43
  #
47
44
  @client = Msf::RPC::Client.new(@options)
48
45
 
46
+ #
47
+ # Connect to the database based on the included options
48
+ #
49
+ _connect_database
50
+
51
+ #
52
+ # Add a new workspace
53
+ #
54
+ @workspace_name = Time.now.utc.to_s.gsub(" ","_").gsub(":","_")
55
+ _create_workspace
56
+
49
57
  #
50
58
  # Create a logger
51
59
  #
52
60
  #@logger = Msf::RPC::Simple::Logger.new
53
61
  end
54
62
 
63
+ # Public: Creates and retuns an xml report
55
64
  #
56
- #
65
+ # This method is ugly for a number of reasons, but there doesn't
66
+ # appear to be a way to be notified when the command is completed
67
+ # nor when the
68
+ #
57
69
  #
70
+ # returns a valid xml string
71
+ def create_report
72
+ report_path = "/tmp/metasploit_#{@workspace_name}.xml"
73
+
74
+ # Create the report using the db_export command
75
+ _send_command("db_export #{report_path}\n")
76
+
77
+ # We've sent the command, so let's sit back and wait for th
78
+ # output to hit the disk.
79
+ begin
80
+ xml_string = ""
81
+ status = Timeout::timeout(240) {
82
+ # We don't know when the file is going to show up, so
83
+ # wait for it...
84
+ until File.exists? report_path do
85
+ sleep 1
86
+ end
87
+
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)
95
+ }
96
+ rescue Timeout::Error
97
+ xml_string = "<MetasploitV4></MetasploitV4>"
98
+ end
99
+
100
+ xml_string
101
+ end
102
+
103
+ def cleanup
104
+ #_send_command("workspace -d #{@workspace_name}")
105
+ _send_command("db_disconnect")
106
+ end
107
+
58
108
  def connected?
59
109
  return true if @client.call("core.version")
60
110
  end
111
+
112
+ private
113
+
114
+ 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}")
120
+ end
121
+
122
+ def _send_command(command)
123
+ # Create the console and get its id
124
+ console = @client.call("console.create")
125
+
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")
130
+
131
+ # Initial read
132
+ output_string = ""
133
+ output = @client.call("console.read", console["id"])
134
+
135
+ # Read until finished
136
+ while (output["busy"] == true) do
137
+ output_string += "#{output['data']}"
138
+ output = @client.call("console.read", console["id"])
139
+ output_string = "Error" if output["result"] == "failure"
140
+ end
141
+
142
+ # Clean up console
143
+ #@client.call("console.destroy", console["id"])
144
+
145
+ output_string
146
+ end
147
+
61
148
  end
62
149
  end
63
150
  end
@@ -3,116 +3,12 @@ module Msf
3
3
  module Simple
4
4
  module Features
5
5
  module Framework
6
-
7
- def create_report
8
-
9
- # Create the console and get its id
10
- console = @client.call("console.create")
11
- console_id = console["id"]
12
-
13
- # Do an initial read / discard to pull out the banner
14
- @client.call("console.read", console_id)
15
-
16
- # Move to the context of our module
17
- @client.call("console.write", console_id, "db_connect #{self.options[:db_user]}:#{self.options[:db_pass]}@localhost/msf3\n")
18
- @client.call("console.write", console_id, "db_export /tmp/metasploit.xml\n")
19
-
20
- # do an initial read of the module's output
21
- output = @client.call("console.read", console_id)
22
- output_string = "#{output['data']}"
23
-
24
- return "Module Error" if output["result"] == "failure"
25
-
26
- until (output["busy"] == false) do
27
- output = @client.call("console.read", console_id)
28
- output_string += "#{output['data']}"
29
- return "Module Error" if output["result"] == "failure"
30
- end
31
-
32
- # Clean up
33
- @client.call("console.destroy", console_id)
34
-
35
- File.open("/tmp/metasploit.xml").read
36
- end
37
-
38
- #
39
- # This module simply runs a module
40
- #
41
- def execute_module_and_return_output(options)
42
-
43
- module_name = options[:module_name]
44
- #module_options = options[:module_options]
45
- module_option_string = options[:module_option_string]
46
-
47
- # split up the module name into type / name
48
- module_type = module_name.split("/").first
49
- raise "Error, bad module name" unless ["exploit", "auxiliary", "post", "encoder", "nop"].include? module_type
50
-
51
- #module_options["TARGET"] = 0 unless module_options["TARGET"]
52
-
53
- #info = @client.call("module.execute", module_type, module_name, module_options)
54
- #@client.call("job.info", info["job_id"])
55
-
56
- # The module output will be not available when run this way; to
57
- # capture the result of the print_* commands, you have to set the
58
- # output driver of the module to something you can read from (Buffer,
59
- # File, etc). For your use case, the best bet is to run the module
60
- # via the Console API instead of module.execute, and use that to read
61
- # the output from the console itself, which provides buffer output for you.
62
-
63
- # Create the console and get its id
64
- console = @client.call("console.create")
65
- console_id = console["id"]
66
-
67
- # Do an initial read / discard to pull out the banner
68
- @client.call("console.read", console_id)
69
-
70
- # Move to the context of our module
71
- @client.call("console.write", console_id, "use #{module_name}\n")
72
-
73
- # Set up the module's datastore
74
- module_option_string.split(",").each do |module_option|
75
- @client.call "console.write", console_id, "set #{module_option}\n"
76
- end
77
-
78
- # Do an another read / discard to pull out the option confirmation
79
- @client.call("console.read", console_id)
80
-
81
- # Depending on the module_type, kick off the module
82
- if module_type == "auxiliary"
83
- @client.call "console.write", console_id, "run\n"
84
- elsif module_type == "exploit"
85
- @client.call "console.write", console_id, "exploit\n"
86
- else
87
- return "Unsupported"
88
- end
89
-
90
- # do an initial read of the module's output
91
- module_output = @client.call("console.read", console_id)
92
- module_output_data_string = "#{module_output['data']}"
93
-
94
- return "Module Error" if module_output["result"] == "failure"
95
-
96
- until (module_output["busy"] == false) do
97
- module_output = @client.call("console.read", console_id)
98
- module_output_data_string += "#{module_output['data']}"
99
- return "Module Error" if module_output["result"] == "failure"
100
- end
101
-
102
- # Clean up
103
- @client.call("console.destroy", console_id)
104
-
105
- module_output_data_string
106
- end
107
-
108
6
 
109
7
  #
110
8
  # This module runs a number of discovery modules
111
9
  #
112
- def discover_host(host)
10
+ def discover_range(range)
113
11
 
114
- #
115
- #
116
12
  # Other Potential options
117
13
  # - auxiliary/scanner/smb/pipe_auditor
118
14
  # - auxiliary/scanner/smb/pipe_dcerpc_auditor
@@ -120,45 +16,45 @@ module Msf
120
16
  # - auxiliary/scanner/smb/smb_enumusers
121
17
  modules_and_options = [
122
18
  {:module_name => "auxiliary/scanner/http/http_version",
123
- :module_option_string => "RHOSTS #{host}" },
19
+ :module_option_string => "RHOSTS #{range}" },
124
20
  #{:module_name => "auxiliary/scanner/http/cert",
125
- # :module_option_string => "RHOSTS #{host}" },
21
+ # :module_option_string => "RHOSTS #{range}" },
126
22
  {:module_name => "auxiliary/scanner/ftp/ftp_version",
127
- :module_option_string => "RHOSTS #{host}" },
23
+ :module_option_string => "RHOSTS #{range}" },
128
24
  {:module_name => "auxiliary/scanner/h323/h323_version",
129
- :module_option_string => "RHOSTS #{host}" },
25
+ :module_option_string => "RHOSTS #{range}" },
130
26
  {:module_name => "auxiliary/scanner/imap/imap_version",
131
- :module_option_string => "RHOSTS #{host}" },
27
+ :module_option_string => "RHOSTS #{range}" },
132
28
  #{:module_name => "auxiliary/scanner/portscan/syn",
133
- #:module_option_string => "RHOSTS #{host}" },
29
+ #:module_option_string => "RHOSTS #{range}" },
134
30
  #{:module_name => "auxiliary/scanner/portscan/tcp",
135
- #:module_option_string => "RHOSTS #{host}" },
31
+ #:module_option_string => "RHOSTS #{range}" },
136
32
  #{:module_name => "auxiliary/scanner/lotus/lotus_domino_version",
137
- #:module_option_string => "RHOSTS #{host}" },
33
+ #:module_option_string => "RHOSTS #{range}" },
138
34
  {:module_name => "auxiliary/scanner/mysql/mysql_version",
139
- :module_option_string => "RHOSTS #{host}" },
35
+ :module_option_string => "RHOSTS #{range}" },
140
36
  #{:module_name => "auxiliary/scanner/netbios/nbname",
141
- #:module_option_string => "RHOSTS #{host}" },
37
+ #:module_option_string => "RHOSTS #{range}" },
142
38
  #{:module_name => "auxiliary/scanner/netbios/nbname_probe",
143
- #:module_option_string => "RHOSTS #{host}" },
39
+ #:module_option_string => "RHOSTS #{range}" },
144
40
  #{:module_name => "auxiliary/scanner/pcanywhere/pcanywhere_tcp",
145
- #:module_option_string => "RHOSTS #{host}" },
41
+ #:module_option_string => "RHOSTS #{range}" },
146
42
  #{:module_name => "auxiliary/scanner/pcanywhere/pcanywhere_udp",
147
- #:module_option_string => "RHOSTS #{host}" },
43
+ #:module_option_string => "RHOSTS #{range}" },
148
44
  {:module_name => "auxiliary/scanner/pop3/pop3_version",
149
- :module_option_string => "RHOSTS #{host}" },
45
+ :module_option_string => "RHOSTS #{range}" },
150
46
  {:module_name => "auxiliary/scanner/postgres/postgres_version",
151
- :module_option_string => "RHOSTS #{host}" },
47
+ :module_option_string => "RHOSTS #{range}" },
152
48
  {:module_name => "auxiliary/scanner/smb/smb_version",
153
- :module_option_string => "RHOSTS #{host}" },
49
+ :module_option_string => "RHOSTS #{range}" },
154
50
  {:module_name => "auxiliary/scanner/snmp/snmp_enum",
155
- :module_option_string => "RHOSTS #{host}" },
51
+ :module_option_string => "RHOSTS #{range}" },
156
52
  {:module_name => "auxiliary/scanner/ssh/ssh_version",
157
- :module_option_string => "RHOSTS #{host}" },
53
+ :module_option_string => "RHOSTS #{range}" },
158
54
  {:module_name => "auxiliary/scanner/telnet/telnet_version",
159
- :module_option_string => "RHOSTS #{host}" },
55
+ :module_option_string => "RHOSTS #{range}" },
160
56
  #{:module_name => "auxiliary/scanner/vmware/vmauthd_version",
161
- #:module_option_string => "RHOSTS #{host}" },
57
+ #:module_option_string => "RHOSTS #{range}" },
162
58
  ]
163
59
 
164
60
  # This is a naive and horrible way of doing it, but let's just knock
@@ -182,29 +78,29 @@ module Msf
182
78
  #
183
79
  # This module runs a number of _login modules
184
80
  #
185
- def bruteforce_host(host)
81
+ def bruteforce_range(range)
186
82
 
187
83
  modules_and_options = [
188
84
  {:module_name => "auxiliary/scanner/ftp/ftp_login",
189
- :module_option_string => "RHOSTS #{host}" },
85
+ :module_option_string => "RHOSTS #{range}" },
190
86
  {:module_name => "auxiliary/scanner/http/http_login",
191
- :module_option_string => "RHOSTS #{host}" },
87
+ :module_option_string => "RHOSTS #{range}" },
192
88
  {:module_name => "auxiliary/scanner/smb/smb_login",
193
- :module_option_string => "RHOSTS #{host}" },
89
+ :module_option_string => "RHOSTS #{range}" },
194
90
  {:module_name => "auxiliary/scanner/mssql/mssql_login",
195
- :module_option_string => "RHOSTS #{host}" },
91
+ :module_option_string => "RHOSTS #{range}" },
196
92
  {:module_name => "auxiliary/scanner/mysql/mysql_login",
197
- :module_option_string => "RHOSTS #{host}" },
93
+ :module_option_string => "RHOSTS #{range}" },
198
94
  {:module_name => "auxiliary/scanner/pop3/pop3_login",
199
- :module_option_string => "RHOSTS #{host}" },
95
+ :module_option_string => "RHOSTS #{range}" },
200
96
  {:module_name => "auxiliary/scanner/smb/smb_login",
201
- :module_option_string => "RHOSTS #{host}" },
97
+ :module_option_string => "RHOSTS #{range}" },
202
98
  {:module_name => "auxiliary/scanner/snmp/snmp_login",
203
- :module_option_string => "RHOSTS #{host}" },
99
+ :module_option_string => "RHOSTS #{range}" },
204
100
  {:module_name => "auxiliary/scanner/ssh/ssh_login",
205
- :module_option_string => "RHOSTS #{host}" },
101
+ :module_option_string => "RHOSTS #{range}" },
206
102
  {:module_name => "auxiliary/scanner/telnet/telnet_login",
207
- :module_option_string => "RHOSTS #{host}" },
103
+ :module_option_string => "RHOSTS #{range}" },
208
104
  ]
209
105
 
210
106
  # This is a naive and horrible way of doing it, but let's just knock
@@ -228,10 +124,88 @@ module Msf
228
124
  #
229
125
  # This module runs a number of exploit modules
230
126
  #
231
- def exploit_host(host)
127
+ def exploit_range(range)
232
128
  return "Not Implemented"
233
129
  end
234
130
 
131
+ #
132
+ # This module simply runs a module
133
+ #
134
+ def execute_module_and_return_output(options)
135
+ module_name = options[:module_name]
136
+ #module_options = options[:module_options]
137
+ module_option_string = options[:module_option_string]
138
+
139
+ # split up the module name into type / name
140
+ module_type = module_name.split("/").first
141
+ raise "Error, bad module name" unless ["exploit", "auxiliary", "post", "encoder", "nop"].include? module_type
142
+
143
+ #module_options["TARGET"] = 0 unless module_options["TARGET"]
144
+
145
+ #info = @client.call("module.execute", module_type, module_name, module_options)
146
+ #@client.call("job.info", info["job_id"])
147
+
148
+ # The module output will be not available when run this way; to
149
+ # capture the result of the print_* commands, you have to set the
150
+ # output driver of the module to something you can read from (Buffer,
151
+ # File, etc). For your use case, the best bet is to run the module
152
+ # via the Console API instead of module.execute, and use that to read
153
+ # the output from the console itself, which provides buffer output for you.
154
+
155
+ # Create the console and get its id
156
+ console = @client.call("console.create")
157
+ console_id = console["id"]
158
+
159
+ # Do an initial read / discard to pull out the banner
160
+ @client.call("console.read", console_id)
161
+
162
+ # Move to the context of our module
163
+ @client.call("console.write", console_id, "use #{module_name}\n")
164
+
165
+ # Set up the module's datastore
166
+ module_option_string.split(",").each do |module_option|
167
+ @client.call "console.write", console_id, "set #{module_option}\n"
168
+ @client.call("console.read", console_id)
169
+ end
170
+
171
+ # Ugh, this is horrible, but the read call is currently racey
172
+ 5.times do
173
+ @client.call("console.read", console_id)
174
+ end
175
+
176
+ # Depending on the module_type, kick off the module
177
+ if module_type == "auxiliary"
178
+ @client.call "console.write", console_id, "run\n"
179
+ elsif module_type == "exploit"
180
+ @client.call "console.write", console_id, "exploit\n"
181
+ else
182
+ return "Unsupported"
183
+ end
184
+
185
+ # do an initial read of the module's output
186
+ module_output = @client.call("console.read", console_id)
187
+ module_output_data_string = "#{module_output['data']}"
188
+
189
+ return "Module Error" if module_output["result"] == "failure"
190
+
191
+ while module_output["busy"] do
192
+ module_output = @client.call("console.read", console_id)
193
+ module_output_data_string += "#{module_output['data']}"
194
+ return "Module Error" if module_output["result"] == "failure"
195
+ end
196
+
197
+ # Ugh, this is horrible, but the read call is currently racey
198
+ 5.times do
199
+ module_output = @client.call("console.read", console_id)
200
+ module_output_data_string += "#{module_output['data']}"
201
+ end
202
+
203
+ # Clean up
204
+ @client.call("console.destroy", console_id)
205
+
206
+ module_output_data_string
207
+ end
208
+
235
209
  end
236
210
  end
237
211
  end
@@ -1,7 +1,7 @@
1
1
  module Msf
2
2
  module RPC
3
3
  module Simple
4
- VERSION = "0.0.4"
4
+ VERSION = "0.0.5"
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.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: