msfrpc-simple 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/msfrpc-simple/client.rb +98 -11
- data/lib/msfrpc-simple/features/framework.rb +111 -137
- data/lib/msfrpc-simple/version.rb +1 -1
- metadata +1 -1
data/lib/msfrpc-simple/client.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
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
|
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 #{
|
19
|
+
:module_option_string => "RHOSTS #{range}" },
|
124
20
|
#{:module_name => "auxiliary/scanner/http/cert",
|
125
|
-
# :module_option_string => "RHOSTS #{
|
21
|
+
# :module_option_string => "RHOSTS #{range}" },
|
126
22
|
{:module_name => "auxiliary/scanner/ftp/ftp_version",
|
127
|
-
:module_option_string => "RHOSTS #{
|
23
|
+
:module_option_string => "RHOSTS #{range}" },
|
128
24
|
{:module_name => "auxiliary/scanner/h323/h323_version",
|
129
|
-
:module_option_string => "RHOSTS #{
|
25
|
+
:module_option_string => "RHOSTS #{range}" },
|
130
26
|
{:module_name => "auxiliary/scanner/imap/imap_version",
|
131
|
-
:module_option_string => "RHOSTS #{
|
27
|
+
:module_option_string => "RHOSTS #{range}" },
|
132
28
|
#{:module_name => "auxiliary/scanner/portscan/syn",
|
133
|
-
#:module_option_string => "RHOSTS #{
|
29
|
+
#:module_option_string => "RHOSTS #{range}" },
|
134
30
|
#{:module_name => "auxiliary/scanner/portscan/tcp",
|
135
|
-
#:module_option_string => "RHOSTS #{
|
31
|
+
#:module_option_string => "RHOSTS #{range}" },
|
136
32
|
#{:module_name => "auxiliary/scanner/lotus/lotus_domino_version",
|
137
|
-
#:module_option_string => "RHOSTS #{
|
33
|
+
#:module_option_string => "RHOSTS #{range}" },
|
138
34
|
{:module_name => "auxiliary/scanner/mysql/mysql_version",
|
139
|
-
:module_option_string => "RHOSTS #{
|
35
|
+
:module_option_string => "RHOSTS #{range}" },
|
140
36
|
#{:module_name => "auxiliary/scanner/netbios/nbname",
|
141
|
-
#:module_option_string => "RHOSTS #{
|
37
|
+
#:module_option_string => "RHOSTS #{range}" },
|
142
38
|
#{:module_name => "auxiliary/scanner/netbios/nbname_probe",
|
143
|
-
#:module_option_string => "RHOSTS #{
|
39
|
+
#:module_option_string => "RHOSTS #{range}" },
|
144
40
|
#{:module_name => "auxiliary/scanner/pcanywhere/pcanywhere_tcp",
|
145
|
-
#:module_option_string => "RHOSTS #{
|
41
|
+
#:module_option_string => "RHOSTS #{range}" },
|
146
42
|
#{:module_name => "auxiliary/scanner/pcanywhere/pcanywhere_udp",
|
147
|
-
#:module_option_string => "RHOSTS #{
|
43
|
+
#:module_option_string => "RHOSTS #{range}" },
|
148
44
|
{:module_name => "auxiliary/scanner/pop3/pop3_version",
|
149
|
-
:module_option_string => "RHOSTS #{
|
45
|
+
:module_option_string => "RHOSTS #{range}" },
|
150
46
|
{:module_name => "auxiliary/scanner/postgres/postgres_version",
|
151
|
-
:module_option_string => "RHOSTS #{
|
47
|
+
:module_option_string => "RHOSTS #{range}" },
|
152
48
|
{:module_name => "auxiliary/scanner/smb/smb_version",
|
153
|
-
:module_option_string => "RHOSTS #{
|
49
|
+
:module_option_string => "RHOSTS #{range}" },
|
154
50
|
{:module_name => "auxiliary/scanner/snmp/snmp_enum",
|
155
|
-
:module_option_string => "RHOSTS #{
|
51
|
+
:module_option_string => "RHOSTS #{range}" },
|
156
52
|
{:module_name => "auxiliary/scanner/ssh/ssh_version",
|
157
|
-
:module_option_string => "RHOSTS #{
|
53
|
+
:module_option_string => "RHOSTS #{range}" },
|
158
54
|
{:module_name => "auxiliary/scanner/telnet/telnet_version",
|
159
|
-
:module_option_string => "RHOSTS #{
|
55
|
+
:module_option_string => "RHOSTS #{range}" },
|
160
56
|
#{:module_name => "auxiliary/scanner/vmware/vmauthd_version",
|
161
|
-
#:module_option_string => "RHOSTS #{
|
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
|
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 #{
|
85
|
+
:module_option_string => "RHOSTS #{range}" },
|
190
86
|
{:module_name => "auxiliary/scanner/http/http_login",
|
191
|
-
:module_option_string => "RHOSTS #{
|
87
|
+
:module_option_string => "RHOSTS #{range}" },
|
192
88
|
{:module_name => "auxiliary/scanner/smb/smb_login",
|
193
|
-
:module_option_string => "RHOSTS #{
|
89
|
+
:module_option_string => "RHOSTS #{range}" },
|
194
90
|
{:module_name => "auxiliary/scanner/mssql/mssql_login",
|
195
|
-
:module_option_string => "RHOSTS #{
|
91
|
+
:module_option_string => "RHOSTS #{range}" },
|
196
92
|
{:module_name => "auxiliary/scanner/mysql/mysql_login",
|
197
|
-
:module_option_string => "RHOSTS #{
|
93
|
+
:module_option_string => "RHOSTS #{range}" },
|
198
94
|
{:module_name => "auxiliary/scanner/pop3/pop3_login",
|
199
|
-
:module_option_string => "RHOSTS #{
|
95
|
+
:module_option_string => "RHOSTS #{range}" },
|
200
96
|
{:module_name => "auxiliary/scanner/smb/smb_login",
|
201
|
-
:module_option_string => "RHOSTS #{
|
97
|
+
:module_option_string => "RHOSTS #{range}" },
|
202
98
|
{:module_name => "auxiliary/scanner/snmp/snmp_login",
|
203
|
-
:module_option_string => "RHOSTS #{
|
99
|
+
:module_option_string => "RHOSTS #{range}" },
|
204
100
|
{:module_name => "auxiliary/scanner/ssh/ssh_login",
|
205
|
-
:module_option_string => "RHOSTS #{
|
101
|
+
:module_option_string => "RHOSTS #{range}" },
|
206
102
|
{:module_name => "auxiliary/scanner/telnet/telnet_login",
|
207
|
-
:module_option_string => "RHOSTS #{
|
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
|
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
|