371 lines
12 KiB
Ruby
371 lines
12 KiB
Ruby
module Msf::DBManager::Web
|
|
#
|
|
# Report a Web Form to the database. WebForm must be tied to an existing Web Site
|
|
#
|
|
# opts MUST contain
|
|
# +:web_site+:: the web site object that this page should be associated with
|
|
# +:path+:: the virtual host name for this particular web site
|
|
# +:query+:: the query string that is appended to the path (not valid for GET)
|
|
# +:method+:: the form method, one of GET, POST, or PATH
|
|
# +:params+:: an ARRAY of all parameters and values specified in the form
|
|
#
|
|
# If web_site is NOT specified, the following values are mandatory
|
|
# +:host+:: the ip address of the server hosting the web site
|
|
# +:port+:: the port number of the associated web site
|
|
# +:vhost+:: the virtual host for this particular web site
|
|
# +:ssl+:: whether or not SSL is in use on this port
|
|
#
|
|
# Duplicate records for a given web_site, path, method, and params combination will be overwritten
|
|
#
|
|
def report_web_form(opts)
|
|
return if not active
|
|
::ActiveRecord::Base.connection_pool.with_connection {
|
|
wspace = opts.delete(:workspace) || workspace
|
|
|
|
path = opts[:path]
|
|
meth = opts[:method].to_s.upcase
|
|
para = opts[:params]
|
|
quer = opts[:query].to_s
|
|
site = nil
|
|
|
|
if not (path and meth)
|
|
raise ArgumentError, "report_web_form requires the path and method parameters"
|
|
end
|
|
|
|
if not %W{GET POST PATH}.include?(meth)
|
|
raise ArgumentError, "report_web_form requires the method to be one of GET, POST, PATH"
|
|
end
|
|
|
|
if opts[:web_site] and opts[:web_site].kind_of?(::Mdm::WebSite)
|
|
site = opts.delete(:web_site)
|
|
else
|
|
site = report_web_site(
|
|
:workspace => wspace,
|
|
:host => opts[:host], :port => opts[:port],
|
|
:vhost => opts[:host], :ssl => opts[:ssl]
|
|
)
|
|
if not site
|
|
raise ArgumentError, "report_web_form was unable to create the associated web site"
|
|
end
|
|
end
|
|
|
|
ret = {}
|
|
|
|
# Since one of our serialized fields is used as a unique parameter, we must do the final
|
|
# comparisons through ruby and not SQL.
|
|
|
|
form = nil
|
|
::Mdm::WebForm.where(web_site_id: site[:id], path: path, method: meth, query: quer).each do |xform|
|
|
if xform.params == para
|
|
form = xform
|
|
break
|
|
end
|
|
end
|
|
if not form
|
|
form = ::Mdm::WebForm.new
|
|
form.web_site_id = site[:id]
|
|
form.path = path
|
|
form.method = meth
|
|
form.params = para
|
|
form.query = quer
|
|
end
|
|
|
|
msf_import_timestamps(opts, form)
|
|
form.save!
|
|
ret[:web_form] = form
|
|
}
|
|
end
|
|
|
|
#
|
|
# Report a Web Page to the database. WebPage must be tied to an existing Web Site
|
|
#
|
|
# opts MUST contain
|
|
# +:web_site+:: the web site object that this page should be associated with
|
|
# +:path+:: the virtual host name for this particular web site
|
|
# +:code+:: the http status code from requesting this page
|
|
# +:headers+:: this is a HASH of headers (lowercase name as key) of ARRAYs of values
|
|
# +:body+:: the document body of the server response
|
|
# +:query+:: the query string after the path
|
|
#
|
|
# If web_site is NOT specified, the following values are mandatory
|
|
# +:host+:: the ip address of the server hosting the web site
|
|
# +:port+:: the port number of the associated web site
|
|
# +:vhost+:: the virtual host for this particular web site
|
|
# +:ssl+:: whether or not SSL is in use on this port
|
|
#
|
|
# These values will be used to create new host, service, and web_site records
|
|
#
|
|
# opts can contain
|
|
# +:cookie+:: the Set-Cookie headers, merged into a string
|
|
# +:auth+:: the Authorization headers, merged into a string
|
|
# +:ctype+:: the Content-Type headers, merged into a string
|
|
# +:mtime+:: the timestamp returned from the server of the last modification time
|
|
# +:location+:: the URL that a redirect points to
|
|
#
|
|
# Duplicate records for a given web_site, path, and query combination will be overwritten
|
|
#
|
|
def report_web_page(opts)
|
|
return if not active
|
|
::ActiveRecord::Base.connection_pool.with_connection {
|
|
wspace = opts.delete(:workspace) || workspace
|
|
|
|
path = opts[:path]
|
|
code = opts[:code].to_i
|
|
body = opts[:body].to_s
|
|
query = opts[:query].to_s
|
|
headers = opts[:headers]
|
|
site = nil
|
|
|
|
if not (path and code and body and headers)
|
|
raise ArgumentError, "report_web_page requires the path, query, code, body, and headers parameters"
|
|
end
|
|
|
|
if opts[:web_site] and opts[:web_site].kind_of?(::Mdm::WebSite)
|
|
site = opts.delete(:web_site)
|
|
else
|
|
site = report_web_site(
|
|
:workspace => wspace,
|
|
:host => opts[:host], :port => opts[:port],
|
|
:vhost => opts[:host], :ssl => opts[:ssl]
|
|
)
|
|
if not site
|
|
raise ArgumentError, "report_web_page was unable to create the associated web site"
|
|
end
|
|
end
|
|
|
|
ret = {}
|
|
|
|
page = ::Mdm::WebPage.where(web_site_id: site[:id], path: path, query: query).first_or_initialize
|
|
page.code = code
|
|
page.body = body
|
|
page.headers = headers
|
|
page.cookie = opts[:cookie] if opts[:cookie]
|
|
page.auth = opts[:auth] if opts[:auth]
|
|
page.mtime = opts[:mtime] if opts[:mtime]
|
|
|
|
|
|
if opts[:ctype].blank? || opts[:ctype] == [""]
|
|
page.ctype = ""
|
|
else
|
|
page.ctype = opts[:ctype]
|
|
end
|
|
|
|
page.location = opts[:location] if opts[:location]
|
|
|
|
msf_import_timestamps(opts, page)
|
|
page.save!
|
|
|
|
ret[:web_page] = page
|
|
}
|
|
|
|
end
|
|
|
|
#
|
|
# WMAP
|
|
# Support methods
|
|
#
|
|
|
|
#
|
|
# Report a Web Site to the database. WebSites must be tied to an existing Service
|
|
#
|
|
# opts MUST contain
|
|
# +:service+:: the service object this site should be associated with
|
|
# +:vhost+:: the virtual host name for this particular web site`
|
|
#
|
|
# If +:service+ is NOT specified, the following values are mandatory
|
|
# +:host+:: the ip address of the server hosting the web site
|
|
# +:port+:: the port number of the associated web site
|
|
# +:ssl+:: whether or not SSL is in use on this port
|
|
#
|
|
# These values will be used to create new host and service records
|
|
#
|
|
# opts can contain
|
|
# +:options+:: a hash of options for accessing this particular web site
|
|
# +:info+:: if present, report the service with this info
|
|
#
|
|
# Duplicate records for a given host, port, vhost combination will be overwritten
|
|
#
|
|
def report_web_site(opts)
|
|
return if not active
|
|
::ActiveRecord::Base.connection_pool.with_connection { |conn|
|
|
wspace = opts.delete(:workspace) || workspace
|
|
vhost = opts.delete(:vhost)
|
|
|
|
addr = nil
|
|
port = nil
|
|
name = nil
|
|
serv = nil
|
|
info = nil
|
|
|
|
if opts[:service] and opts[:service].kind_of?(::Mdm::Service)
|
|
serv = opts[:service]
|
|
else
|
|
addr = opts[:host]
|
|
port = opts[:port]
|
|
name = opts[:ssl] ? 'https' : 'http'
|
|
info = opts[:info]
|
|
if not (addr and port)
|
|
raise ArgumentError, "report_web_site requires service OR host/port/ssl"
|
|
end
|
|
|
|
# Force addr to be the address and not hostname
|
|
addr = Rex::Socket.getaddress(addr, true)
|
|
end
|
|
|
|
ret = {}
|
|
|
|
host = serv ? serv.host : find_or_create_host(
|
|
:workspace => wspace,
|
|
:host => addr,
|
|
:state => Msf::HostState::Alive
|
|
)
|
|
|
|
if host.name.to_s.empty?
|
|
host.name = vhost
|
|
host.save!
|
|
end
|
|
|
|
serv = serv ? serv : find_or_create_service(
|
|
:workspace => wspace,
|
|
:host => host,
|
|
:port => port,
|
|
:proto => 'tcp',
|
|
:state => 'open'
|
|
)
|
|
|
|
# Change the service name if it is blank or it has
|
|
# been explicitly specified.
|
|
if opts.keys.include?(:ssl) or serv.name.to_s.empty?
|
|
name = opts[:ssl] ? 'https' : 'http'
|
|
serv.name = name
|
|
end
|
|
# Add the info if it's there.
|
|
unless info.to_s.empty?
|
|
serv.info = info
|
|
end
|
|
serv.save! if serv.changed?
|
|
=begin
|
|
host.updated_at = host.created_at
|
|
host.state = HostState::Alive
|
|
host.save!
|
|
=end
|
|
|
|
vhost ||= host.address
|
|
site = ::Mdm::WebSite.where(vhost: vhost, service_id: serv[:id]).first_or_initialize
|
|
site.options = opts[:options] if opts[:options]
|
|
|
|
# XXX:
|
|
msf_import_timestamps(opts, site)
|
|
site.save!
|
|
|
|
ret[:web_site] = site
|
|
}
|
|
end
|
|
|
|
#
|
|
# Report a Web Vuln to the database. WebVuln must be tied to an existing Web Site
|
|
#
|
|
# opts MUST contain
|
|
# +:web_site+:: the web site object that this page should be associated with
|
|
# +:path+:: the virtual host name for this particular web site
|
|
# +:query+:: the query string appended to the path (not valid for GET method flaws)
|
|
# +:method+:: the form method, one of GET, POST, or PATH
|
|
# +:params+:: an ARRAY of all parameters and values specified in the form
|
|
# +:pname+:: the specific field where the vulnerability occurs
|
|
# +:proof+:: the string showing proof of the vulnerability
|
|
# +:risk+:: an INTEGER value from 0 to 5 indicating the risk (5 is highest)
|
|
# +:name+:: the string indicating the type of vulnerability
|
|
#
|
|
# If web_site is NOT specified, the following values are mandatory
|
|
# +:host+:: the ip address of the server hosting the web site
|
|
# +:port+:: the port number of the associated web site
|
|
# +:vhost+:: the virtual host for this particular web site
|
|
# +:ssl+:: whether or not SSL is in use on this port
|
|
#
|
|
#
|
|
# Duplicate records for a given web_site, path, method, pname, and name
|
|
# combination will be overwritten
|
|
#
|
|
def report_web_vuln(opts)
|
|
return if not active
|
|
::ActiveRecord::Base.connection_pool.with_connection {
|
|
wspace = opts.delete(:workspace) || workspace
|
|
|
|
path = opts[:path]
|
|
meth = opts[:method]
|
|
para = opts[:params] || []
|
|
quer = opts[:query].to_s
|
|
pname = opts[:pname]
|
|
proof = opts[:proof]
|
|
risk = opts[:risk].to_i
|
|
name = opts[:name].to_s.strip
|
|
blame = opts[:blame].to_s.strip
|
|
desc = opts[:description].to_s.strip
|
|
conf = opts[:confidence].to_i
|
|
cat = opts[:category].to_s.strip
|
|
payload = opts[:payload].to_s
|
|
owner = opts[:owner] ? opts[:owner].shortname : nil
|
|
|
|
|
|
site = nil
|
|
|
|
if not (path and meth and proof and pname)
|
|
raise ArgumentError, "report_web_vuln requires the path, method, proof, risk, name, params, and pname parameters. Received #{opts.inspect}"
|
|
end
|
|
|
|
if not %W{GET POST PATH}.include?(meth)
|
|
raise ArgumentError, "report_web_vuln requires the method to be one of GET, POST, PATH. Received '#{meth}'"
|
|
end
|
|
|
|
if risk < 0 or risk > 5
|
|
raise ArgumentError, "report_web_vuln requires the risk to be between 0 and 5 (inclusive). Received '#{risk}'"
|
|
end
|
|
|
|
if conf < 0 or conf > 100
|
|
raise ArgumentError, "report_web_vuln requires the confidence to be between 1 and 100 (inclusive). Received '#{conf}'"
|
|
end
|
|
|
|
if cat.empty?
|
|
raise ArgumentError, "report_web_vuln requires the category to be a valid string"
|
|
end
|
|
|
|
if name.empty?
|
|
raise ArgumentError, "report_web_vuln requires the name to be a valid string"
|
|
end
|
|
|
|
if opts[:web_site] and opts[:web_site].kind_of?(::Mdm::WebSite)
|
|
site = opts.delete(:web_site)
|
|
else
|
|
site = report_web_site(
|
|
:workspace => wspace,
|
|
:host => opts[:host], :port => opts[:port],
|
|
:vhost => opts[:host], :ssl => opts[:ssl]
|
|
)
|
|
if not site
|
|
raise ArgumentError, "report_web_form was unable to create the associated web site"
|
|
end
|
|
end
|
|
|
|
ret = {}
|
|
|
|
meth = meth.to_s.upcase
|
|
|
|
vuln = ::Mdm::WebVuln.where(web_site_id: site[:id], path: path, method: meth, pname: pname, name: name, category: cat, query: quer).first_or_initialize
|
|
vuln.name = name
|
|
vuln.risk = risk
|
|
vuln.params = para
|
|
vuln.proof = proof.to_s
|
|
vuln.category = cat
|
|
vuln.blame = blame
|
|
vuln.description = desc
|
|
vuln.confidence = conf
|
|
vuln.payload = payload
|
|
vuln.owner = owner
|
|
|
|
msf_import_timestamps(opts, vuln)
|
|
vuln.save!
|
|
|
|
ret[:web_vuln] = vuln
|
|
}
|
|
end
|
|
end |