Skip to content

Commit 4add407

Browse files
committed
First round of RPC API documentation
Resolve rapid7#5209
1 parent 8f5d222 commit 4add407

File tree

7 files changed

+335
-4
lines changed

7 files changed

+335
-4
lines changed

lib/msf/core/rpc/v10/client.rb

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,18 @@ module RPC
1212

1313
class Client
1414

15-
attr_accessor :token, :info
15+
# @return [String] A login token.
16+
attr_accessor :token
1617

18+
# @return [Hash] Login information.
19+
attr_accessor :info
1720

21+
22+
# Initializes the RPC client to connect to: https://127.0.0.1:3790 (TLS1)
23+
#
24+
# @param [Hash] info Information needed for the initialization.
25+
# @option info [String] :token A token used by the client.
26+
# @return [void]
1827
def initialize(info={})
1928
self.info = {
2029
:host => '127.0.0.1',
@@ -29,6 +38,12 @@ def initialize(info={})
2938
end
3039

3140

41+
# Logs in by calling the 'auth.login' API.
42+
#
43+
# @param [String] user Username.
44+
# @param [String] pass Password.
45+
# @raise RuntimeError Indicating a failed authentication.
46+
# @return [TrueClass] Indicating a successful login.
3247
def login(user,pass)
3348
res = self.call("auth.login", user, pass)
3449
unless (res && res['result'] == "success")
@@ -38,8 +53,20 @@ def login(user,pass)
3853
true
3954
end
4055

41-
# Prepend the authentication token as the first parameter
42-
# of every call except auth.login. Requires the
56+
57+
# Calls an API.
58+
#
59+
# @param [String] meth The RPC API to call.
60+
# @param [Array] args The arguments to pass.
61+
# @raise [RuntimeError] Something is wrong while calling the remote API, including:
62+
# * A missing token (your client needs to authenticate).
63+
# * A unexpected response from the server, such as a timeout or unexpected HTTP code.
64+
# @raise [Msf::RPC::ServerException] The RPC service returns an error.
65+
# @return [Hash] The API response.
66+
# @example
67+
# # This will return something like this:
68+
# # {"version"=>"4.11.0-dev", "ruby"=>"2.1.5 x86_64-darwin14.0 2014-11-13", "api"=>"1.0"}
69+
# rpc.call('core.version')
4370
def call(meth, *args)
4471
unless meth == "auth.login"
4572
unless self.token
@@ -84,6 +111,10 @@ def call(meth, *args)
84111
end
85112
end
86113

114+
115+
# Closes the client.
116+
#
117+
# @return [void]
87118
def close
88119
if @cli && @cli.conn?
89120
@cli.close

lib/msf/core/rpc/v10/constants.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ module RPC
88
class Exception < RuntimeError
99
attr_accessor :code, :message
1010

11+
# Initializes Exception.
12+
#
13+
# @param [Fixnum] code An error code.
14+
# @param [String] message An error message.
15+
# @return [void]
1116
def initialize(code, message)
1217
self.code = code
1318
self.message = message
@@ -18,6 +23,13 @@ def initialize(code, message)
1823
class ServerException < RuntimeError
1924
attr_accessor :code, :error_message, :error_class, :error_backtrace
2025

26+
# Initializes ServerException.
27+
#
28+
# @param [Fixnum] code An error code.
29+
# @param [String] error_message An error message.
30+
# @param [Exception] error_class An error class.
31+
# @param [Array] error_backtrace A backtrace of the error.
32+
# @return [void]
2133
def initialize(code, error_message, error_class, error_backtrace)
2234
self.code = code
2335
self.error_message = error_message

lib/msf/core/rpc/v10/rpc_auth.rb

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,20 @@ class RPC_Auth < RPC_Base
1111
rescue ::LoadError
1212
end
1313

14+
# Handles client authentication.
15+
#
16+
# @param [String] user The username.
17+
# @param [String] pass The password.
18+
# @raise [Msf::RPC::Exception] Something is wrong while authenticating, you can possibly get:
19+
# * 401 Failed authentication.
20+
# @return [Hash] A hash indicating a successful login.
21+
# * 'result' [String] A successful message: 'success'.
22+
# * 'token' [String] A token for the authentication.
23+
# @example Here's how you would use this from the client:
24+
# # This returns something like the following:
25+
# # {"result"=>"success", "token"=>"TEMPyp1N40NK8GM0Tx7A87E6Neak2tVJ"}
26+
# rpc.call('auth.login_noauth', 'username', 'password')
1427
def rpc_login_noauth(user,pass)
15-
1628
if not (user.kind_of?(::String) and pass.kind_of?(::String))
1729
error(401, "Login Failed")
1830
end
@@ -42,6 +54,19 @@ def rpc_login_noauth(user,pass)
4254
{ "result" => "success", "token" => token }
4355
end
4456

57+
58+
# Handles client deauthentication.
59+
#
60+
# @param [String] token The user's token to log off.
61+
# @raise [Msf::RPC::Exception] An error indicating a failed deauthentication, including:
62+
# * 500 Invalid authentication token.
63+
# * 500 Permanent authentication token.
64+
# @return [Hash] A hash indiciating the action was successful.
65+
# * 'result' [String] The successful message: 'success'
66+
# @example Here's how you would use this from the client:
67+
# # This returns something like:
68+
# # {"result"=>"success"}
69+
# rpc.call('auth.logout', 'TEMPyp1N40NK8GM0Tx7A87E6Neak2tVJ')
4570
def rpc_logout(token)
4671
found = self.service.tokens[token]
4772
error("500", "Invalid Authentication Token") if not found
@@ -53,6 +78,15 @@ def rpc_logout(token)
5378
{ "result" => "success" }
5479
end
5580

81+
82+
# Returns a list of authentication tokens.
83+
#
84+
# @return [Hash] A hash that contains a list of authentication tokens.
85+
# * 'tokens' [Array] An array of tokens.
86+
# @example Here's how you would use this from the client:
87+
# # This returns something like:
88+
# # {"tokens"=>["TEMPf5I4Ec8cBEKVD8D7xtIbTXWoKapP", "TEMPtcVmMld8w74zo0CYeosM3iXW0nJz"]}
89+
# rpc.call('auth.token_list')
5690
def rpc_token_list
5791
res = self.service.tokens.keys
5892
begin
@@ -66,6 +100,14 @@ def rpc_token_list
66100
{ "tokens" => res }
67101
end
68102

103+
104+
# Adds a new token to the database.
105+
#
106+
# @param [String] token A unique token.
107+
# @return [Hash] A hash indicating the action was successful.
108+
# * 'result' [String] The successful message: 'success'
109+
# @example Here's how you would use this from the client:
110+
# rpc.call('auth.token_add', 'UNIQUE_TOKEN')
69111
def rpc_token_add(token)
70112
db = false
71113
begin
@@ -85,6 +127,14 @@ def rpc_token_add(token)
85127
{ "result" => "success" }
86128
end
87129

130+
131+
# Generates a unique token, and automatically saved to the database.
132+
#
133+
# @return [Hash] A hash indicating the action was successful, also the new token.
134+
# * 'result' [String] The successful message: 'success'
135+
# * 'token' [String] A new token.
136+
# @example Here's how you would use this from the client:
137+
# rpc.call('auth.token_generate')
88138
def rpc_token_generate
89139
token = Rex::Text.rand_text_alphanumeric(32)
90140
db = false
@@ -106,6 +156,14 @@ def rpc_token_generate
106156
{ "result" => "success", "token" => token }
107157
end
108158

159+
160+
# Removes a token from the database.
161+
#
162+
# @param [String] token The token to delete.
163+
# @return [Hash] A hash indicating the action was successful.
164+
# * 'result' [String] The successful message: 'success'
165+
# @example Here's how you would use this from the client:
166+
# rpc.call('auth.token_remove', 'TEMPtcVmMld8w74zo0CYeosM3iXW0nJz')
109167
def rpc_token_remove(token)
110168
db = false
111169
begin

lib/msf/core/rpc/v10/rpc_base.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,22 @@ module RPC
55
class RPC_Base
66
attr_accessor :framework, :service, :tokens, :users
77

8+
# Initializes framework, service, tokens, and users
9+
#
10+
# return [void]
811
def initialize(service)
912
self.service = service
1013
self.framework = service.framework
1114
self.tokens = service.tokens
1215
self.users = service.users
1316
end
1417

18+
# Raises an Msf::RPC Exception.
19+
#
20+
# @param [Fixnum] code The error code to raise.
21+
# @param [String] message The error message.
22+
# @raise [Msf::RPC::Exception]
23+
# @return [void]
1524
def error(code, message)
1625
raise Msf::RPC::Exception.new(code, message)
1726
end

lib/msf/core/rpc/v10/rpc_console.rb

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,24 @@ module Msf
77
module RPC
88
class RPC_Console < RPC_Base
99

10+
# Initializes the RPC console
11+
#
12+
# @return [Msf::Ui::Web::Driver]
1013
def initialize(*args)
1114
super
1215
@console_driver = Msf::Ui::Web::Driver.new(:framework => framework)
1316
end
1417

18+
# Creates a new framework console.
19+
#
20+
# @param [Hash] opts (Optional) See Msf::Ui::Web::Driver#create_console
21+
# @return [Hash] Information about the new console, such as:
22+
# * 'id' [Fixnum] The console's ID.
23+
# * 'prompt' [String] The framework prompt (example: 'msf > ')
24+
# * 'busy' [TrueClass] The console's busy state, or
25+
# * 'busy' [FalseClass] The console's busy state.
26+
# @example Here's how you would use this from the client:
27+
# rpc.call('console.create')
1528
def rpc_create(opts={})
1629
cid = @console_driver.create_console(opts)
1730
{
@@ -21,6 +34,17 @@ def rpc_create(opts={})
2134
}
2235
end
2336

37+
38+
# Returns a list of framework consoles.
39+
#
40+
# @return [Hash] Console information.
41+
# * 'consoles' [Array] consoles, each element is another hash that includes:
42+
# * 'id' [Fixnum] The console's ID
43+
# * 'prompt' [String] The framework prompt (example: 'msf > ')
44+
# * 'busy' [TrueClass] The console's busy state, or
45+
# * 'busy' [FalseClass] The console's busy state.
46+
# @example Here's how you would use this from the client:
47+
# rpc.call('console.list')
2448
def rpc_list
2549
ret = []
2650
@console_driver.consoles.each_key do |cid|
@@ -33,13 +57,36 @@ def rpc_list
3357
{'consoles' => ret}
3458
end
3559

60+
61+
# Deletes a framework console.
62+
#
63+
# @param [Fixnum] cid Framework console ID.
64+
# @return [Hash] A result indicating whether the action was successful or not.
65+
# * 'result' [String] Either 'success' or 'failure'.
66+
# @example Here's how you would use this from the client:
67+
# rpc.call('console.destroy', 1)
3668
def rpc_destroy(cid)
3769
cid = cid.to_s
3870
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
3971
res = @console_driver.destroy_console(cid)
4072
{ 'result' => res ? 'success' : 'failure' }
4173
end
4274

75+
76+
# Returns the framework console output.
77+
#
78+
# @param [Fixnum] cid Framework console ID.
79+
# @return [Hash] There are two different hashes you might get:
80+
#
81+
# If the console ID is invalid, you will get a hash like the following:
82+
# * 'result' [String] A value that says 'failure'.
83+
# If the console ID is valid, you will get a hash like the following:
84+
# * 'data' [String] The output the framework console produces (example: the banner)
85+
# * 'prompt' [String] The framework prompt (example: 'msf > ')
86+
# * 'busy' [TrueClass] The console's busy state, or
87+
# * 'busy' [FalseClass] The console's busy state.
88+
# @example Here's how you would use this from the client:
89+
# rpc.call('console.read', 1)
4390
def rpc_read(cid)
4491
cid = cid.to_s
4592
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
@@ -50,25 +97,77 @@ def rpc_read(cid)
5097
}
5198
end
5299

100+
101+
# Sends an input (such as a command) to the framework console.
102+
#
103+
# @param [Fixnum] cid Framework console ID.
104+
# @param [String] data User input.
105+
# @return [Hash] There are two different hashes you might get:
106+
#
107+
# If the console ID is invalid, you will get a hash like the following:
108+
# * 'result' [String] A value that says 'failure'.
109+
# If the console ID is invalid, you will get a hash like the following:
110+
# * 'wrote' [Fixnum] Number of bytes sent.
111+
# @note Remember to add a newline (\\r\\n) at the end of input, otherwise
112+
# the console will not do anything. And you will need to use the
113+
# #rpc_read method to retrieve the output again.
114+
# @example Here's how you would use this from the client:
115+
# # This will show the current module's options.
116+
# rpc.call('console.write', 4, "show options\r\n")
53117
def rpc_write(cid, data)
54118
cid = cid.to_s
55119
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
56120
{ "wrote" => @console_driver.write_console(cid, data || '') }
57121
end
58122

123+
124+
# Returns the tab-completed version of your input (such as a module path).
125+
#
126+
# @param [Fixnum] cid Framework console ID.
127+
# @param [String] line Command.
128+
# @return [Hash] There are two different hashes you might get:
129+
#
130+
# If the console ID is invalid, you will get a hash like the following:
131+
# * 'result' [String] A value that says 'failure'.
132+
# If the console ID is valid, you will get a hash like the following:
133+
# * 'tabs' [String] The tab-completed version of the command.
134+
# @example Here's how you would use this from the client:
135+
# # This will return:
136+
# # {"tabs"=>["use exploit/windows/smb/ms08_067_netapi"]}
137+
# rpc.call('console.tabs', 4, "use exploit/windows/smb/ms08_067_")
59138
def rpc_tabs(cid, line)
60139
cid = cid.to_s
61140
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
62141
{ "tabs" => @console_driver.consoles[cid].tab_complete(line) }
63142
end
64143

144+
145+
# Kills a framework session.
146+
#
147+
# @param [Fixnum] cid Framework console ID.
148+
# @return [Hash] There are two different hashes you might get:
149+
#
150+
# If the console ID is invalid, you will get a hash like the following:
151+
# * 'result' [String] A value that says 'failure'.
152+
# If the console ID is valid, you will get the following that indicates the action was successful.
153+
# * 'result' [String] A value that says 'success'.
65154
def rpc_session_kill(cid)
66155
cid = cid.to_s
67156
return { 'result' => 'failure' } if not @console_driver.consoles[cid]
68157
@console_driver.consoles[cid].session_kill
69158
{ 'result' => 'success' }
70159
end
71160

161+
162+
# Detaches a framework session.
163+
#
164+
# @param [Fixnum] cid Framework console ID.
165+
# @return [Hash] There are two different hashes you might get:
166+
#
167+
# If the console ID is invalid, you will get a hash like the following:
168+
# * 'result' [String] A value that says 'failure'.
169+
# If the console ID is valid, you will get the following that indicates the action was successful.
170+
# * 'result' [String] A value that says 'success'.
72171
def rpc_session_detach(cid)
73172
cid = cid.to_s
74173
return { 'result' => 'failure' } if not @console_driver.consoles[cid]

0 commit comments

Comments
 (0)