Land #11142, use POST for API token generation

GSoC/Meterpreter_Web_Console
Matthew Kienow 2019-01-08 11:59:30 -05:00
commit d117e6a1d1
No known key found for this signature in database
GPG Key ID: 40787F8B1EAC6E41
7 changed files with 81 additions and 32 deletions

View File

@ -15,26 +15,21 @@ module AuthApiDoc
end end
swagger_path '/api/v1/auth/generate-token' do swagger_path '/api/v1/auth/generate-token' do
# Swagger documentation for /api/v1/auth/generate-token GET # Swagger documentation for /api/v1/auth/generate-token POST
operation :get do operation :post do
key :description, 'Return a valid Authorization Bearer token.' key :description, 'Return a valid Authorization Bearer token.'
key :tags, [ 'auth' ] key :tags, [ 'auth' ]
parameter do parameter do
key :name, :username key :in, :body
key :in, :query key :name, :body
key :description, 'The username for the user you want to authenticate.' key :description, 'Login credentials for the user who will be generating a token.'
key :required, true key :required, true
key :type, :string schema do
property :username, type: :string, required: true
property :password, type: :string, required: true
end end
parameter do
key :name, :password
key :in, :query
key :description, 'The password for the user you want to authenticate.'
key :required, true
key :type, :string
end end
response 200 do response 200 do

View File

@ -1,3 +1,5 @@
require 'json'
module Authentication module Authentication
module Strategies module Strategies
class UserPassword < Warden::Strategies::Base class UserPassword < Warden::Strategies::Base
@ -16,21 +18,31 @@ module Authentication
# Check if request contains valid data and should be authenticated. # Check if request contains valid data and should be authenticated.
# @return [Boolean] true if strategy should be run for the request; otherwise, false. # @return [Boolean] true if strategy should be run for the request; otherwise, false.
def valid? def valid?
params['username'] && params['password'] begin
body = JSON.parse(request.body.read, symbolize_names: true)
body[:username] && body[:password]
ensure
request.body.rewind # Reset the StringIO buffer so any further consumers can read the body
end
end end
# Authenticate the request. # Authenticate the request.
def authenticate! def authenticate!
db_manager = env['msf.db_manager'] begin
user = db_manager.users(username: params['username']).first body = JSON.parse(request.body.read, symbolize_names: true)
if user.nil? || !db_manager.authenticate_user(id: user.id, password: params['password']) db_manager = env['msf.db_manager']
user = db_manager.users(username: body[:username]).first
if user.nil? || !db_manager.authenticate_user(id: user.id, password: body[:password])
fail("Invalid username or password.") fail("Invalid username or password.")
else else
success!(user) success!(user)
end end
ensure
request.body.rewind # Reset the StringIO buffer so any further consumers can read the body
end
end end
end end
end end
end end

View File

@ -86,7 +86,7 @@ input[type=text], input[type=password] {
height: 34px; height: 34px;
} }
button { input[type=button] {
border-color: rgba(0, 0, 0, 0.6); border-color: rgba(0, 0, 0, 0.6);
border-width: 1px; border-width: 1px;
cursor: pointer; cursor: pointer;

View File

@ -31,7 +31,7 @@ module AuthServlet
app.post AuthServlet.api_login_path, &post_login app.post AuthServlet.api_login_path, &post_login
app.get AuthServlet.api_logout_path, &get_logout app.get AuthServlet.api_logout_path, &get_logout
app.get AuthServlet.api_generate_token_path, &get_generate_token app.post AuthServlet.api_generate_token_path, &post_generate_token
app.post "#{AuthServlet.api_unauthenticated_path}/?:scope?", &post_unauthenticated app.post "#{AuthServlet.api_unauthenticated_path}/?:scope?", &post_unauthenticated
end end
@ -75,7 +75,7 @@ module AuthServlet
end end
# Generate a new API token for the current user # Generate a new API token for the current user
def self.get_generate_token def self.post_generate_token
lambda { lambda {
# change action to drop the scope param since this is used # change action to drop the scope param since this is used
# by XMLHttpRequest (XHR) and we don't want a redirect # by XMLHttpRequest (XHR) and we don't want a redirect

View File

@ -9,7 +9,7 @@
<script type="text/javascript"> <script type="text/javascript">
function getNewApiToken() { function getNewApiToken() {
loadDoc("GET", "<%= AuthServlet.api_generate_token_path %>", function(xhr) { loadDoc("POST", "<%= AuthServlet.api_generate_token_path %>", function(xhr) {
var response = JSON.parse(xhr.responseText); var response = JSON.parse(xhr.responseText);
document.getElementById("api-token").innerHTML = response.data.token; document.getElementById("api-token").innerHTML = response.data.token;
}, errorHandler); }, errorHandler);
@ -61,6 +61,8 @@
<div id="api-token" class="api-token"> <div id="api-token" class="api-token">
<%= !warden.user(:user).nil? && !warden.user(:user).persistence_token.nil? ? warden.user(:user).persistence_token : 'none' %> <%= !warden.user(:user).nil? && !warden.user(:user).persistence_token.nil? ? warden.user(:user).persistence_token : 'none' %>
</div> </div>
<% else %>
<div id="not-logged-in-label">You are not currently logged in. Please click <a href="<%= AuthServlet.api_login_path %>">here</a> to be taken to the login page.</div>
<% end %> <% end %>
</div> </div>

View File

@ -7,8 +7,49 @@
</head> </head>
<body> <body>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script>
function ConvertFormToJSON(form){
var array = jQuery(form).serializeArray();
var json = {};
jQuery.each(array, function() {
json[this.name] = this.value || '';
});
return JSON.stringify(json);
}
$(document).ready(function(){
$("#submit").on('click', function(){
$.ajax({
url: '<%= AuthServlet.api_login_path %>',
type : "POST",
data : ConvertFormToJSON($("#login_form")),
contentType: "application/json; charset=utf-8",
processData: false,
success : function(data) {
// TODO: This currently always redirects the user to the /auth/account page.
// This is because the POST /login endpoint always returns a redirect, which
// is not easily handled by Javascript AJAX requests. When the /login endpoint
// is more fully fleshed out we should add proper handling for valid and
// invalid login attempts.
window.location.replace('<%= AuthServlet.api_account_path %>');
},
error: function(xhr, resp, text) {
console.log(xhr, resp, text);
}
})
});
});
</script>
<div style="padding:20px;"> <div style="padding:20px;">
<form action="<%= AuthServlet.api_login_path %>" method="post"> <form id="login_form" action="" method="post">
<div class="credential-container"> <div class="credential-container">
<h2>Log In - Metasploit API</h2> <h2>Log In - Metasploit API</h2>
<label for="username"><b>Username</b></label> <label for="username"><b>Username</b></label>
@ -16,8 +57,7 @@
<label for="password"><b>Password</b></label> <label for="password"><b>Password</b></label>
<input type="password" placeholder="Enter Password" name="password" required> <input type="password" placeholder="Enter Password" name="password" required>
<input id="submit" type="button" name="submit" value="submit">
<button type="submit">Log In</button>
</div> </div>
</form> </form>
</div> </div>

2
msfdb
View File

@ -636,7 +636,7 @@ def add_web_service_user
# Send request to create new API token for the user # Send request to create new API token for the user
generate_token_uri = get_web_service_uri(path: '/api/v1/auth/generate-token') generate_token_uri = get_web_service_uri(path: '/api/v1/auth/generate-token')
response_data = http_request(uri: generate_token_uri, query: cred_data, method: :get, response_data = http_request(uri: generate_token_uri, data: cred_data, method: :post,
skip_verify: skip_ssl_verify?, cert: get_ssl_cert) skip_verify: skip_ssl_verify?, cert: get_ssl_cert)
response = response_data[:response] response = response_data[:response]
puts "add_web_service_user: generate token response=#{response}" if @options[:debug] puts "add_web_service_user: generate token response=#{response}" if @options[:debug]