/lib/msf/core/db.rb
Ruby | 4759 lines | 3391 code | 586 blank | 782 comment | 634 complexity | a0f00401a3aa688c88dfb996dbc9b9e1 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, BSD-3-Clause
Large files files are truncated, but you can click here to view the full file
- require 'rex/parser/nmap_xml'
- require 'rex/parser/nexpose_xml'
- require 'rex/parser/retina_xml'
- require 'rex/parser/netsparker_xml'
- require 'rex/parser/nessus_xml'
- require 'rex/parser/ip360_xml'
- require 'rex/parser/ip360_aspl_xml'
- require 'rex/socket'
- require 'zip'
- require 'packetfu'
- require 'uri'
- require 'tmpdir'
- require 'fileutils'
- module Msf
- ###
- #
- # The states that a host can be in.
- #
- ###
- module HostState
- #
- # The host is alive.
- #
- Alive = "alive"
- #
- # The host is dead.
- #
- Dead = "down"
- #
- # The host state is unknown.
- #
- Unknown = "unknown"
- end
- ###
- #
- # The states that a service can be in.
- #
- ###
- module ServiceState
- Open = "open"
- Closed = "closed"
- Filtered = "filtered"
- Unknown = "unknown"
- end
- ###
- #
- # Events that can occur in the host/service database.
- #
- ###
- module DatabaseEvent
- #
- # Called when an existing host's state changes
- #
- def on_db_host_state(host, ostate)
- end
- #
- # Called when an existing service's state changes
- #
- def on_db_service_state(host, port, ostate)
- end
- #
- # Called when a new host is added to the database. The host parameter is
- # of type Host.
- #
- def on_db_host(host)
- end
- #
- # Called when a new client is added to the database. The client
- # parameter is of type Client.
- #
- def on_db_client(client)
- end
- #
- # Called when a new service is added to the database. The service
- # parameter is of type Service.
- #
- def on_db_service(service)
- end
- #
- # Called when an applicable vulnerability is found for a service. The vuln
- # parameter is of type Vuln.
- #
- def on_db_vuln(vuln)
- end
- #
- # Called when a new reference is created.
- #
- def on_db_ref(ref)
- end
- end
- class DBImportError < RuntimeError
- end
- ###
- #
- # The DB module ActiveRecord definitions for the DBManager
- #
- ###
- class DBManager
- def rfc3330_reserved(ip)
- case ip.class.to_s
- when "PacketFu::Octets"
- ip_x = ip.to_x
- ip_i = ip.to_i
- when "String"
- if ipv4_validator(ip)
- ip_x = ip
- ip_i = Rex::Socket.addr_atoi(ip)
- else
- raise ArgumentError, "Invalid IP address: #{ip.inspect}"
- end
- when "Fixnum"
- if (0..2**32-1).include? ip
- ip_x = Rex::Socket.addr_itoa(ip)
- ip_i = ip
- else
- raise ArgumentError, "Invalid IP address: #{ip.inspect}"
- end
- else
- raise ArgumentError, "Invalid IP address: #{ip.inspect}"
- end
- return true if Rex::Socket::RangeWalker.new("0.0.0.0-0.255.255.255").include? ip_x
- return true if Rex::Socket::RangeWalker.new("127.0.0.0-127.255.255.255").include? ip_x
- return true if Rex::Socket::RangeWalker.new("169.254.0.0-169.254.255.255").include? ip_x
- return true if Rex::Socket::RangeWalker.new("224.0.0.0-239.255.255.255").include? ip_x
- return true if Rex::Socket::RangeWalker.new("255.255.255.255-255.255.255.255").include? ip_x
- return false
- end
- def ipv4_validator(addr)
- return false unless addr.kind_of? String
- addr =~ /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
- end
- # Takes a space-delimited set of ips and ranges, and subjects
- # them to RangeWalker for validation. Returns true or false.
- def validate_ips(ips)
- ret = true
- begin
- ips.split(' ').each {|ip|
- unless Rex::Socket::RangeWalker.new(ip).ranges
- ret = false
- break
- end
- }
- rescue
- ret = false
- end
- return ret
- end
- #
- # Determines if the database is functional
- #
- def check
- res = Host.find(:first)
- end
- def default_workspace
- Workspace.default
- end
- def find_workspace(name)
- Workspace.find_by_name(name)
- end
- #
- # Creates a new workspace in the database
- #
- def add_workspace(name)
- Workspace.find_or_create_by_name(name)
- end
- def workspaces
- Workspace.find(:all)
- end
- #
- # Wait for all pending write to finish
- #
- def sync
- task = queue( Proc.new { } )
- task.wait
- end
- #
- # Find a host. Performs no database writes.
- #
- def get_host(opts)
- if opts.kind_of? Host
- return opts
- elsif opts.kind_of? String
- raise RuntimeError, "This invokation of get_host is no longer supported: #{caller}"
- else
- address = opts[:addr] || opts[:address] || opts[:host] || return
- return address if address.kind_of? Host
- end
- wspace = opts.delete(:workspace) || workspace
- host = wspace.hosts.find_by_address(address)
- return host
- end
- #
- # Exactly like report_host but waits for the database to create a host and returns it.
- #
- def find_or_create_host(opts)
- report_host(opts.merge({:wait => true}))
- end
- #
- # Report a host's attributes such as operating system and service pack
- #
- # The opts parameter MUST contain
- # :host -- the host's ip address
- #
- # The opts parameter can contain:
- # :state -- one of the Msf::HostState constants
- # :os_name -- one of the Msf::OperatingSystems constants
- # :os_flavor -- something like "XP" or "Gentoo"
- # :os_sp -- something like "SP2"
- # :os_lang -- something like "English", "French", or "en-US"
- # :arch -- one of the ARCH_* constants
- # :mac -- the host's MAC address
- #
- def report_host(opts)
- return if not active
- addr = opts.delete(:host) || return
- # Sometimes a host setup through a pivot will see the address as "Remote Pipe"
- if addr.eql? "Remote Pipe"
- return
- end
- # Ensure the host field updated_at is changed on each report_host()
- if addr.kind_of? Host
- queue( Proc.new { addr.updated_at = addr.created_at; addr.save! } )
- return addr
- end
- addr = normalize_host(addr)
- wait = opts.delete(:wait)
- wspace = opts.delete(:workspace) || workspace
- if opts[:host_mac]
- opts[:mac] = opts.delete(:host_mac)
- end
- unless ipv4_validator(addr)
- raise ::ArgumentError, "Invalid IP address in report_host(): #{addr}"
- end
- ret = {}
- task = queue( Proc.new {
- if opts[:comm] and opts[:comm].length > 0
- host = wspace.hosts.find_or_initialize_by_address_and_comm(addr, opts[:comm])
- else
- host = wspace.hosts.find_or_initialize_by_address(addr)
- end
- opts.each { |k,v|
- if (host.attribute_names.include?(k.to_s))
- host[k] = v unless host.attribute_locked?(k.to_s)
- else
- dlog("Unknown attribute for Host: #{k}")
- end
- }
- host.info = host.info[0,Host.columns_hash["info"].limit] if host.info
- # Set default fields if needed
- host.state = HostState::Alive if not host.state
- host.comm = '' if not host.comm
- host.workspace = wspace if not host.workspace
- # Always save the host, helps track updates
- msf_import_timestamps(opts,host)
- host.save!
- ret[:host] = host
- } )
- if wait
- return nil if task.wait != :done
- return ret[:host]
- end
- return task
- end
- #
- # Iterates over the hosts table calling the supplied block with the host
- # instance of each entry.
- #
- def each_host(wspace=workspace, &block)
- wspace.hosts.each do |host|
- block.call(host)
- end
- end
- #
- # Returns a list of all hosts in the database
- #
- def hosts(wspace = workspace, only_up = false, addresses = nil)
- conditions = {}
- conditions[:state] = [Msf::HostState::Alive, Msf::HostState::Unknown] if only_up
- conditions[:address] = addresses if addresses
- wspace.hosts.all(:conditions => conditions, :order => :address)
- end
- def find_or_create_service(opts)
- report_service(opts.merge({:wait => true}))
- end
- #
- # Record a service in the database.
- #
- # opts must contain
- # :host -- the host where this service is running
- # :port -- the port where this service listens
- # :proto -- the transport layer protocol (e.g. tcp, udp)
- #
- # opts may contain
- # :name -- the application layer protocol (e.g. ssh, mssql, smb)
- #
- def report_service(opts)
- return if not active
- addr = opts.delete(:host) || return
- hname = opts.delete(:host_name)
- hmac = opts.delete(:host_mac)
- wait = opts.delete(:wait)
- wspace = opts.delete(:workspace) || workspace
- hopts = {:workspace => wspace, :host => addr}
- hopts[:name] = hname if hname
- hopts[:mac] = hmac if hmac
- report_host(hopts)
- ret = {}
- task = queue(Proc.new {
- host = get_host(:workspace => wspace, :address => addr)
- if host
- host.updated_at = host.created_at
- host.state = HostState::Alive
- host.save!
- end
- proto = opts[:proto] || 'tcp'
- opts[:name].downcase! if (opts[:name])
- service = host.services.find_or_initialize_by_port_and_proto(opts[:port].to_i, proto)
- opts.each { |k,v|
- if (service.attribute_names.include?(k.to_s))
- service[k] = v
- else
- dlog("Unknown attribute for Service: #{k}")
- end
- }
- if (service.state == nil)
- service.state = ServiceState::Open
- end
- if (service and service.changed?)
- msf_import_timestamps(opts,service)
- service.save!
- end
- ret[:service] = service
- })
- if wait
- return nil if task.wait() != :done
- return ret[:service]
- end
- return task
- end
- def get_service(wspace, host, proto, port)
- host = get_host(:workspace => wspace, :address => host)
- return if not host
- return host.services.find_by_proto_and_port(proto, port)
- end
- #
- # Iterates over the services table calling the supplied block with the
- # service instance of each entry.
- #
- def each_service(wspace=workspace, &block)
- services(wspace).each do |service|
- block.call(service)
- end
- end
- #
- # Returns a list of all services in the database
- #
- def services(wspace = workspace, only_up = false, proto = nil, addresses = nil, ports = nil, names = nil)
- conditions = {}
- conditions[:state] = [ServiceState::Open] if only_up
- conditions[:proto] = proto if proto
- conditions["hosts.address"] = addresses if addresses
- conditions[:port] = ports if ports
- conditions[:name] = names if names
- wspace.services.all(:include => :host, :conditions => conditions, :order => "hosts.address, port")
- end
- def get_client(opts)
- wspace = opts.delete(:workspace) || workspace
- host = get_host(:workspace => wspace, :host => opts[:host]) || return
- client = host.clients.find(:first, :conditions => {:ua_string => opts[:ua_string]})
- return client
- end
- def find_or_create_client(opts)
- report_client(opts.merge({:wait => true}))
- end
- #
- # Report a client running on a host.
- #
- # opts must contain
- # :ua_string -- the value of the User-Agent header
- # :host -- the host where this client connected from, can be an ip address or a Host object
- #
- # opts can contain
- # :ua_name -- one of the Msf::HttpClients constants
- # :ua_ver -- detected version of the given client
- # :campaign -- an id or Campaign object
- #
- # Returns a Client.
- #
- def report_client(opts)
- return if not active
- addr = opts.delete(:host) || return
- wspace = opts.delete(:workspace) || workspace
- report_host(:workspace => wspace, :host => addr)
- wait = opts.delete(:wait)
- ret = {}
- task = queue(Proc.new {
- host = get_host(:workspace => wspace, :host => addr)
- client = host.clients.find_or_initialize_by_ua_string(opts[:ua_string])
- campaign = opts.delete(:campaign)
- if campaign
- case campaign
- when Campaign
- opts[:campaign_id] = campaign.id
- else
- opts[:campaign_id] = campaign
- end
- end
- opts.each { |k,v|
- if (client.attribute_names.include?(k.to_s))
- client[k] = v
- else
- dlog("Unknown attribute for Client: #{k}")
- end
- }
- if (client and client.changed?)
- client.save!
- end
- ret[:client] = client
- })
- if wait
- return nil if task.wait() != :done
- return ret[:client]
- end
- return task
- end
- #
- # This method iterates the vulns table calling the supplied block with the
- # vuln instance of each entry.
- #
- def each_vuln(wspace=workspace,&block)
- wspace.vulns.each do |vulns|
- block.call(vulns)
- end
- end
- #
- # This methods returns a list of all vulnerabilities in the database
- #
- def vulns(wspace=workspace)
- wspace.vulns
- end
- #
- # This methods returns a list of all credentials in the database
- #
- def creds(wspace=workspace)
- Cred.find(
- :all,
- :include => {:service => :host}, # That's some magic right there.
- :conditions => ["hosts.workspace_id = ?", wspace.id]
- )
- end
- #
- # This method returns a list of all exploited hosts in the database.
- #
- def exploited_hosts(wspace=workspace)
- wspace.exploited_hosts
- end
- #
- # This method iterates the notes table calling the supplied block with the
- # note instance of each entry.
- #
- def each_note(wspace=workspace, &block)
- wspace.notes.each do |note|
- block.call(note)
- end
- end
- #
- # Find or create a note matching this type/data
- #
- def find_or_create_note(opts)
- report_note(opts.merge({:wait => true}))
- end
- #
- # Report a Note to the database. Notes can be tied to a Workspace, Host, or Service.
- #
- # opts MUST contain
- # :data -- whatever it is you're making a note of
- # :type -- The type of note, e.g. smb_peer_os
- #
- # opts can contain
- # :workspace -- the workspace to associate with this Note
- # :host -- an IP address or a Host object to associate with this Note
- # :service -- a Service object to associate with this Note
- # :port -- along with :host and proto, a service to associate with this Note
- # :proto -- along with :host and port, a service to associate with this Note
- # :update -- what to do in case a similar Note exists, see below
- #
- # The :update option can have the following values:
- # :unique -- allow only a single Note per +host+/+type+ pair
- # :unique_data -- like :uniqe, but also compare +data+
- # :insert -- always insert a new Note even if one with identical values exists
- #
- # If the provided :host is an IP address and does not exist in the
- # database, it will be created. If :workspace, :host and :service are all
- # omitted, the new Note will be associated with the current workspace.
- #
- def report_note(opts)
- return if not active
- wait = opts.delete(:wait)
- wspace = opts.delete(:workspace) || workspace
- seen = opts.delete(:seen) || false
- crit = opts.delete(:critical) || false
- host = nil
- addr = nil
- # Report the host so it's there for the Proc to use below
- if opts[:host]
- if opts[:host].kind_of? Host
- host = opts[:host]
- else
- report_host({:workspace => wspace, :host => opts[:host]})
- addr = normalize_host(opts[:host])
- end
- # Do the same for a service if that's also included.
- if (opts[:port])
- proto = nil
- sname = nil
- case opts[:proto].to_s.downcase # Catch incorrect usages
- when 'tcp','udp'
- proto = opts[:proto]
- sname = opts[:sname] if opts[:sname]
- when 'dns','snmp','dhcp'
- proto = 'udp'
- sname = opts[:proto]
- else
- proto = 'tcp'
- sname = opts[:proto]
- end
- sopts = {
- :workspace => wspace,
- :host => opts[:host],
- :port => opts[:port],
- :proto => proto
- }
- sopts[:name] = sname if sname
- report_service(sopts)
- end
- end
- # Update Modes can be :unique, :unique_data, :insert
- mode = opts[:update] || :unique
- ret = {}
- task = queue(Proc.new {
- if addr and not host
- host = get_host(:workspace => wspace, :host => addr)
- end
- if host and (opts[:port] and opts[:proto])
- service = get_service(wspace, host, opts[:proto], opts[:port])
- elsif opts[:service] and opts[:service].kind_of? Service
- service = opts[:service]
- end
- if host
- host.updated_at = host.created_at
- host.state = HostState::Alive
- host.save!
- end
- ntype = opts.delete(:type) || opts.delete(:ntype) || (raise RuntimeError, "A note :type or :ntype is required")
- data = opts[:data] || (raise RuntimeError, "Note :data is required")
- method = nil
- args = []
- note = nil
- conditions = { :ntype => ntype }
- conditions[:host_id] = host[:id] if host
- conditions[:service_id] = service[:id] if service
- notes = wspace.notes.find(:all, :conditions => conditions)
- case mode
- when :unique
- # Only one note of this type should exist, make a new one if it
- # isn't there. If it is, grab it and overwrite its data.
- if notes.empty?
- note = wspace.notes.new(conditions)
- else
- note = notes[0]
- end
- note.data = data
- when :unique_data
- # Don't make a new Note with the same data as one that already
- # exists for the given: type and (host or service)
- notes.each do |n|
- # Compare the deserialized data from the table to the raw
- # data we're looking for. Because of the serialization we
- # can't do this easily or reliably in SQL.
- if n.data == data
- note = n
- break
- end
- end
- if not note
- # We didn't find one with the data we're looking for, make
- # a new one.
- note = wspace.notes.new(conditions.merge(:data => data))
- end
- else
- # Otherwise, assume :insert, which means always make a new one
- note = wspace.notes.new
- if host
- note.host_id = host[:id]
- end
- if opts[:service] and opts[:service].kind_of? Service
- note.service_id = opts[:service][:id]
- end
- note.seen = seen
- note.critical = crit
- note.ntype = ntype
- note.data = data
- end
- msf_import_timestamps(opts,note)
- note.save!
- ret[:note] = note
- })
- if wait
- return nil if task.wait() != :done
- return ret[:note]
- end
- return task
- end
- #
- # This methods returns a list of all notes in the database
- #
- def notes(wspace=workspace)
- wspace.notes
- end
- # This is only exercised by MSF3 XML importing for now. Needs the wait
- # conditions and return hash as well.
- def report_host_tag(opts)
- name = opts.delete(:name)
- raise DBImportError.new("Missing required option :name") unless name
- addr = opts.delete(:addr)
- raise DBImportError.new("Missing required option :addr") unless addr
- wspace = opts.delete(:wspace)
- raise DBImportError.new("Missing required option :wspace") unless wspace
- host = nil
- report_host(:workspace => wspace, :address => addr)
- task = queue( Proc.new {
- host = get_host(:workspace => wspace, :address => addr)
- desc = opts.delete(:desc)
- summary = opts.delete(:summary)
- detail = opts.delete(:detail)
- crit = opts.delete(:crit)
- possible_tag = Tag.find(:all,
- :include => :hosts,
- :conditions => ["hosts.workspace_id = ? and tags.name = ?",
- wspace.id,
- name
- ]
- ).first
- tag = possible_tag || Tag.new
- tag.name = name
- tag.desc = desc
- tag.report_summary = !!summary
- tag.report_detail = !!detail
- tag.critical = !!crit
- tag.hosts = tag.hosts | [host]
- tag.save! if tag.changed?
- })
- return task
- end
- # report_auth_info used to create a note, now it creates
- # an entry in the creds table. It's much more akin to
- # report_vuln() now.
- #
- # opts must contain
- # :host -- an IP address
- # :port -- a port number
- #
- # opts can contain
- # :user -- the username
- # :pass -- the password, or path to ssh_key
- # :ptype -- the type of password (password, hash, or ssh_key)
- # :proto -- a transport name for the port
- # :sname -- service name
- # :active -- by default, a cred is active, unless explicitly false
- # :proof -- data used to prove the account is actually active.
- #
- # Sources: Credentials can be sourced from another credential, or from
- # a vulnerability. For example, if an exploit was used to dump the
- # smb_hashes, and this credential comes from there, the source_id would
- # be the Vuln id (as reported by report_vuln) and the type would be "Vuln".
- #
- # :source_id -- The Vuln or Cred id of the source of this cred.
- # :source_type -- Either Vuln or Cred
- #
- # TODO: This is written somewhat host-centric, when really the
- # Service is the thing. Need to revisit someday.
- def report_auth_info(opts={})
- return if not active
- raise ArgumentError.new("Missing required option :host") if opts[:host].nil?
- raise ArgumentError.new("Invalid address for :host") unless validate_ips(opts[:host])
- raise ArgumentError.new("Missing required option :port") if opts[:port].nil?
- host = opts.delete(:host)
- ptype = opts.delete(:type) || "password"
- token = [opts.delete(:user), opts.delete(:pass)]
- sname = opts.delete(:sname)
- port = opts.delete(:port)
- proto = opts.delete(:proto) || "tcp"
- proof = opts.delete(:proof)
- source_id = opts.delete(:source_id)
- source_type = opts.delete(:source_type)
- duplicate_ok = opts.delete(:duplicate_ok)
- # Nil is true for active.
- active = (opts[:active] || opts[:active].nil?) ? true : false
- wait = opts.delete(:wait)
- wspace = opts.delete(:workspace) || workspace
- # Service management; assume the user knows what
- # he's talking about.
- unless service = get_service(wspace, host, proto, port)
- report_service(:host => host, :port => port, :proto => proto, :name => sname, :workspace => wspace)
- end
- ret = {}
- task = queue( Proc.new {
- # Get the service
- service ||= get_service(wspace, host, proto, port)
- # If duplicate usernames are okay, find by both user and password (allows
- # for actual duplicates to get modified updated_at, sources, etc)
- if duplicate_ok
- cred = service.creds.find_or_initialize_by_user_and_ptype_and_pass(token[0] || "", ptype, token[1] || "")
- else
- # Create the cred by username only (so we can change passwords)
- cred = service.creds.find_or_initialize_by_user_and_ptype(token[0] || "", ptype)
- end
- # Update with the password
- cred.pass = (token[1] || "")
- # Annotate the credential
- cred.ptype = ptype
- cred.active = active
- # Update the source ID only if there wasn't already one.
- if source_id and !cred.source_id
- cred.source_id = source_id
- cred.source_type = source_type if source_type
- end
- # Safe proof (lazy way) -- doesn't chop expanded
- # characters correctly, but shouldn't ever be a problem.
- unless proof.nil?
- proof = Rex::Text.to_hex_ascii(proof)
- proof = proof[0,4096]
- end
- cred.proof = proof
- # Update the timestamp
- if cred.changed?
- msf_import_timestamps(opts,cred)
- cred.save!
- end
- # Ensure the updated_at is touched any time report_auth_info is called
- # except when it's set explicitly (as it is for imports)
- unless opts[:updated_at] || opts["updated_at"]
- cred.updated_at = Time.now.utc
- cred.save!
- end
- ret[:cred] = cred
- })
- if wait
- return nil if task.wait() != :done
- return ret[:cred]
- end
- return task
- end
- alias :report_cred :report_auth_info
- alias :report_auth :report_auth_info
- #
- # Find or create a credential matching this type/data
- #
- def find_or_create_cred(opts)
- report_auth_info(opts.merge({:wait => true}))
- end
- #
- # This method iterates the creds table calling the supplied block with the
- # cred instance of each entry.
- #
- def each_cred(wspace=workspace,&block)
- wspace.creds.each do |cred|
- block.call(cred)
- end
- end
- def each_exploited_host(wspace=workspace,&block)
- wspace.exploited_hosts.each do |eh|
- block.call(eh)
- end
- end
- #
- # Find or create a vuln matching this service/name
- #
- def find_or_create_vuln(opts)
- report_vuln(opts.merge({:wait => true}))
- end
- #
- # opts must contain
- # :host -- the host where this vulnerability resides
- # :name -- the scanner-specific id of the vuln (e.g. NEXPOSE-cifs-acct-password-never-expires)
- #
- # opts can contain
- # :info -- a human readable description of the vuln, free-form text
- # :refs -- an array of Ref objects or string names of references
- #
- def report_vuln(opts)
- return if not active
- raise ArgumentError.new("Missing required option :host") if opts[:host].nil?
- raise ArgumentError.new("Deprecated data column for vuln, use .info instead") if opts[:data]
- name = opts[:name] || return
- info = opts[:info]
- wait = opts.delete(:wait)
- wspace = opts.delete(:workspace) || workspace
- rids = nil
- if opts[:refs]
- rids = []
- opts[:refs].each do |r|
- if r.respond_to? :ctx_id
- r = r.ctx_id + '-' + r.ctx_val
- end
- rids << find_or_create_ref(:name => r)
- end
- end
- host = nil
- addr = nil
- if opts[:host].kind_of? Host
- host = opts[:host]
- else
- report_host({:workspace => wspace, :host => opts[:host]})
- addr = normalize_host(opts[:host])
- end
- ret = {}
- task = queue( Proc.new {
- if host
- host.updated_at = host.created_at
- host.state = HostState::Alive
- host.save!
- else
- host = get_host(:workspace => wspace, :address => addr)
- end
- if info
- vuln = host.vulns.find_or_initialize_by_name_and_info(name, info, :include => :refs)
- else
- vuln = host.vulns.find_or_initialize_by_name(name, :include => :refs)
- end
- if opts[:port]
- proto = nil
- case opts[:proto].to_s.downcase # Catch incorrect usages, as in report_note
- when 'tcp','udp'
- proto = opts[:proto]
- when 'dns','snmp','dhcp'
- proto = 'udp'
- sname = opts[:proto]
- else
- proto = 'tcp'
- sname = opts[:proto]
- end
- vuln.service = host.services.find_or_create_by_port_and_proto(opts[:port], proto)
- end
- if rids
- vuln.refs << (rids - vuln.refs)
- end
- if vuln.changed?
- msf_import_timestamps(opts,vuln)
- vuln.save!
- end
- ret[:vuln] = vuln
- })
- if wait
- return nil if task.wait() != :done
- return ret[:vuln]
- end
- return task
- end
- def get_vuln(wspace, host, service, name, data='')
- raise RuntimeError, "Not workspace safe: #{caller.inspect}"
- vuln = nil
- if (service)
- vuln = Vuln.find(:first, :conditions => [ "name = ? and service_id = ? and host_id = ?", name, service.id, host.id])
- else
- vuln = Vuln.find(:first, :conditions => [ "name = ? and host_id = ?", name, host.id])
- end
- return vuln
- end
- #
- # Find or create a reference matching this name
- #
- def find_or_create_ref(opts)
- ret = {}
- ret[:ref] = get_ref(opts[:name])
- return ret[:ref] if ret[:ref]
- task = queue(Proc.new {
- ref = Ref.find_or_initialize_by_name(opts[:name])
- if ref and ref.changed?
- ref.save!
- end
- ret[:ref] = ref
- })
- return nil if task.wait() != :done
- return ret[:ref]
- end
- def get_ref(name)
- Ref.find_by_name(name)
- end
- def report_exploit(opts={})
- return if not active
- raise ArgumentError.new("Missing required option :host") if opts[:host].nil?
- wait = opts[:wait]
- wspace = opts.delete(:workspace) || workspace
- host = nil
- addr = nil
- sname = opts.delete(:sname)
- port = opts.delete(:port)
- proto = opts.delete(:proto) || "tcp"
- name = opts.delete(:name)
- payload = opts.delete(:payload)
- session_uuid = opts.delete(:session_uuid)
- if opts[:host].kind_of? Host
- host = opts[:host]
- else
- report_host({:workspace => wspace, :host => opts[:host]})
- addr = normalize_host(opts[:host])
- end
- if opts[:service].kind_of? Service
- service = opts[:service]
- elsif port
- report_service(:host => host, :port => port, :proto => proto, :name => sname)
- service = get_service(wspace, host, proto, port)
- else
- service = nil
- end
- ret = {}
- task = queue(
- Proc.new {
- if host
- host.updated_at = host.created_at
- host.state = HostState::Alive
- host.save!
- else
- host = get_host(:workspace => wspace, :address => addr)
- end
- exploit_info = {
- :workspace => wspace,
- :host_id => host.id,
- :name => name,
- :payload => payload,
- }
- exploit_info[:service_id] = service.id if service
- exploit_info[:session_uuid] = session_uuid if session_uuid
- exploit_record = ExploitedHost.create(exploit_info)
- exploit_record.save!
- ret[:exploit] = exploit_record
- }
- )
- if wait
- return nil if task.wait() != :done
- return ret[:exploit]
- end
- return task
-
- end
- #
- # Deletes a host and associated data matching this address/comm
- #
- def del_host(wspace, address, comm='')
- host = wspace.hosts.find_by_address_and_comm(address, comm)
- host.destroy if host
- end
- #
- # Deletes a port and associated vulns matching this port
- #
- def del_service(wspace, address, proto, port, comm='')
- host = get_host(:workspace => wspace, :address => address)
- return unless host
- host.services.all(:conditions => {:proto => proto, :port => port}).each { |s| s.destroy }
- end
- #
- # Find a reference matching this name
- #
- def has_ref?(name)
- Ref.find_by_name(name)
- end
- #
- # Find a vulnerability matching this name
- #
- def has_vuln?(name)
- Vuln.find_by_name(name)
- end
- #
- # Look for an address across all comms
- #
- def has_host?(wspace,addr)
- wspace.hosts.find_by_address(addr)
- end
- def events(wspace=workspace)
- wspace.events.find :all, :order => 'created_at ASC'
- end
- def report_event(opts = {})
- return if not active
- wspace = opts.delete(:workspace) || workspace
- uname = opts.delete(:username)
- if opts[:host]
- report_host(:workspace => wspace, :host => opts[:host])
- end
- framework.db.queue(Proc.new {
- opts[:host] = get_host(:workspace => wspace, :host => opts[:host]) if opts[:host]
- Event.create(opts.merge(:workspace_id => wspace[:id], :username => uname))
- })
- end
- #
- # Loot collection
- #
- #
- # This method iterates the loot table calling the supplied block with the
- # instance of each entry.
- #
- def each_loot(wspace=workspace, &block)
- wspace.loots.each do |note|
- block.call(note)
- end
- end
- #
- # Find or create a loot matching this type/data
- #
- def find_or_create_loot(opts)
- report_loot(opts.merge({:wait => true}))
- end
- def report_loot(opts)
- return if not active
- wait = opts.delete(:wait)
- wspace = opts.delete(:workspace) || workspace
- path = opts.delete(:path) || (raise RuntimeError, "A loot :path is required")
- host = nil
- addr = nil
- # Report the host so it's there for the Proc to use below
- if opts[:host]
- if opts[:host].kind_of? Host
- host = opts[:host]
- else
- report_host({:workspace => wspace, :host => opts[:host]})
- addr = normalize_host(opts[:host])
- end
- end
- ret = {}
- task = queue(Proc.new {
- if addr and not host
- host = get_host(:workspace => wspace, :host => addr)
- end
- ltype = opts.delete(:type) || opts.delete(:ltype) || (raise RuntimeError, "A loot :type or :ltype is required")
- ctype = opts.delete(:ctype) || opts.delete(:content_type) || 'text/plain'
- name = opts.delete(:name)
- info = opts.delete(:info)
- data = opts[:data]
- loot = wspace.loots.new
- if host
- loot.host_id = host[:id]
- end
- if opts[:service] and opts[:service].kind_of? Service
- loot.service_id = opts[:service][:id]
- end
- loot.path = path
- loot.ltype = ltype
- loot.content_type = ctype
- loot.data = data
- loot.name = name if name
- loot.info = info if info
- msf_import_timestamps(opts,loot)
- loot.save!
- if !opts[:created_at]
- if host
- host.updated_at = host.created_at
- host.state = HostState::Alive
- host.save!
- end
- end
- ret[:loot] = loot
- })
- if wait
- return nil if task.wait() != :done
- return ret[:loot]
- end
- return task
- end
- #
- # This methods returns a list of all loot in the database
- #
- def loots(wspace=workspace)
- wspace.loots
- end
- #
- # Find or create a task matching this type/data
- #
- def find_or_create_task(opts)
- report_task(opts.merge({:wait => true}))
- end
- def report_task(opts)
- return if not active
- wait = opts.delete(:wait)
- wspace = opts.delete(:workspace) || workspace
- path = opts.delete(:path) || (raise RuntimeError, "A task :path is required")
- ret = {}
- this_task = queue(Proc.new {
- user = opts.delete(:user)
- desc = opts.delete(:desc)
- error = opts.delete(:error)
- info = opts.delete(:info)
- mod = opts.delete(:mod)
- options = opts.delete(:options)
- prog = opts.delete(:prog)
- result = opts.delete(:result)
- completed_at = opts.delete(:completed_at)
- task = wspace.tasks.new
- task.created_by = user
- task.description = desc
- task.error = error if error
- task.info = info
- task.module = mod
- task.options = options
- task.path = path
- task.progress = prog
- task.result = result if result
- msf_import_timestamps(opts,task)
- # Having blank completed_ats, while accurate, will cause unstoppable tasks.
- if completed_at.nil? || completed_at.empty?
- task.completed_at = opts[:updated_at]
- else
- task.completed_at = completed_at
- end
- task.save!
- ret[:task] = task
- })
- if wait
- return nil if this_task.wait() != :done
- return ret[:task]
- end
- return this_task
- end
- #
- # This methods returns a list of all tasks in the database
- #
- def tasks(wspace=workspace)
- wspace.tasks
- end
- #
- # Find or create a task matching this type/data
- #
- def find_or_create_report(opts)
- report_report(opts.merge({:wait => true}))
- end
- def report_report(opts)
- return if not active
- wait = opts.delete(:wait)
- wspace = opts.delete(:workspace) || workspace
- path = opts.delete(:path) || (raise RuntimeError, "A report :path is required")
- ret = {}
- this_task = queue(Proc.new {
- user = opts.delete(:user)
- options = opts.delete(:options)
- rtype = opts.delete(:rtype)
- report = wspace.reports.new
- report.created_by = user
- report.options = options
- report.rtype = rtype
- report.path = path
- msf_import_timestamps(opts,report)
- report.save!
- ret[:task] = report
- })
- if wait
- return nil if this_task.wait() != :done
- return ret[:task]
- end
- return this_task
- end
- #
- # This methods returns a list of all reports in the database
- #
- def reports(wspace=workspace)
- wspace.reports
- 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
- #
- # Duplicate records for a given host, port, vhost combination will be overwritten
- #
-
- def report_web_site(opts)
- return if not active
- wait = opts.delete(:wait)
- wspace = opts.delete(:workspace) || workspace
- vhost = opts.delete(:vhost)
- addr = nil
- port = nil
- name = nil
- serv = nil
-
- if opts[:service] and opts[:service].kind_of?(Service)
- serv = opts[:service]
- else
- addr = opts[:host]
- port = opts[:port]
- name = opts[:ssl] ? 'https' : 'http'
- 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)
- end
- ret = {}
- task = queue(Proc.new {
-
- 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
- serv.save!
- end
-
- host.updated_at = host.created_at
- host.state = HostState::Alive
- host.save!
-
- vhost ||= host.address
- site = WebSite.find_or_initialize_by_vhost_and_service_id(vhost, serv[:id])
- site.options = opts[:options] if opts[:options]
-
- # XXX:
- msf_import_timestamps(opts, site)
- site.save!
- ret[:web_site] = site
- })
- if wait
- return nil if task.wait() != :done
- return ret[:web_site]
- end
- return task
- 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
- wait = opts.delete(:wait)
- 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?(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],
- :wait => true
- )
- if not site
- raise ArgumentError, "report_web_page was unable to create the associated web site"
- end
- end
- ret = {}
- task = queue(Proc.new {
- page = WebPage.find_or_initialize_by_web_site_id_and_path_and_query(site[:id], path, query)
- 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]
- page.ctype = opts[:ctype] if opts[:ctype]
- page.location = opts[:location] if opts[:location]
- msf_import_timestamps(opts, page)
- page.save!
- ret[:web_page] = page
- })
- if wait
- return nil if task.wait() != :done
- return ret[:web_page]
- end
- return task
- end
-
-
- #
- # 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
- wait = opts.delete(:wait)
- 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?(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],
- :wait => true
- )
- if not site
- raise ArgumentError, "report_web_form was unable to create the associated web site"
- end
- end
- ret = {}
- task = queue(Proc.new {
-
- # 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
- WebForm.find_all_by_web_site_id_and_path_and_method_and_query(site[:id], path, meth, quer).each do |xform|
- if xform.params == para
- form = xform
- break
- end
- end
- if not form
- form = 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
- })
- if wait
- return nil if task.wait() != :done
- return ret[:web_form]
- end
- return task
- 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
- wait = opts.delete(:wait)
- 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
-
- 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?(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],
- :wait => true
- )
- if not site
- raise ArgumentError, "report_web_form was unable to create the associated web site"
- end
- end
- ret = {}
- task = queue(Proc.new {
-
- meth = meth.to_s.upcase
-
- vuln = WebVuln.find_or_initialize_by_web_site_id_and_path_and_method_and_pname_and_category_and_query(site[:id], path, meth, pname, cat, quer)
- 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
- msf_import_timestamps(opts, vuln)
- vuln.save!
- ret[:web_vuln] = vuln
- })
- if wait
- return nil if task.wait() != :done
- return ret[:web_vuln]
- end
- return task
- end
- #
- # WMAP
- # Selected host
- #
- def selected_host
- selhost = WmapTarget.find(:first, :conditions => ["selected != 0"] )
- if selhost
- return selhost.host
- else
- return
- end
- end
- #
- # WMAP
- # Selected port
- #
- def selected_port
- WmapTarget.find(:first, :conditions => ["selected != 0"] ).port
- end
- #
- # WMAP
- # Selected ssl
- #
- def selected_ssl
- WmapTarget.find(:first, :conditions => ["selected != 0"] ).ssl
- end
- #
- # WMAP
- # Selected id
- #
- def selected_id
- WmapTarget.find(:first, :conditions => ["selected != 0"] ).object_id
- end
- #
- # WMAP
- # This method iterates the requests table identifiying possible targets
- # This method wiil be remove on second phase of db merging.
- #
- def each_distinct_target(&block)
- request_distinct_targets.each do |target|
- block.call(target)
- end
- end
- #
- # WMAP
- # This method returns a list of all possible targets available in requests
- # This method wiil be remove on second phase of db merging.
- #
- def request_distinct_targets
- WmapRequest.find(:all, :select => 'DISTINCT host,address,port,ssl')
- end
- #
- # WMAP
- # This method iterates the requests table returning a list of all requests of a specific target
- #
- def each_request_target_with_path(&block)
- target_requests('AND wmap_requests.path IS NOT NULL').each do |req|
- block.call(req)
- end
- end
- #
- # WMAP
- # This method iterates the requests table returning a list of all requests of a specific target
- #
- def each_request_target_with_query(&block)
- target_requests('AND wmap_requests.query IS NOT NULL').each do |req|
- block.call(req)
- end
- end
- #
- # WMAP
- # This method iterates the requests table returning a list of all requests of a specific target
- #
- def each_request_target_with_body(&block)
- target_requests('AND wmap_requests.body IS NOT NULL').each do |req|
- block.call(req)
- end
- end
- #
- # WMAP
- # This method iterates the requests table returning a list of all requests of a specific target
- #
- def each_request_target_with_headers(&block)
- target_requests('AND wmap_requests.headers IS NOT NULL').each do |req|
- block.call(req)
- end
- end
- #
- # WMAP
- # This method iterates the requests table returning a list of all requests of a specific target
- #
- def each_request_target(&block)
- target_requests('').each do |req|
- block.call(req)
- end
- end
- #
- # WMAP
- # This method returns a list of all requests from target
- #
- def target_requests(extra_condition)
- WmapRequest.find(:all, :conditions => ["wmap_requests.host = ? AND wmap_requests.port = ? #{extra_condition}",selected_host,selected_port])
- end
- #
- # WMAP
- # This method iterates the requests table calling the supplied block with the
- # request instance of each entry.
- #
- def each_request(&block)
- requests.each do |request|
- block.call(request)
- end
- end
- #
- # WMAP
- # This method allows to query directly the requests table. To be used mainly by modules
- #
- def request_sql(host,port,extra_condition)
- WmapRequest.find(:all, :conditions => ["wmap_requests.host = ? AND wmap_requests.port = ? #{extra_condition}",host,port])
- end
- #
- # WMAP
- # This methods returns a list of all targets in the database
- #
- def requests
- WmapRequest.find(:all)
- end
- #
- # WMAP
- # This method iterates the targets table calling the supplied block with the
- # target instance of each entry.
- #
- def each_target(&block)
- targets.each do |target|
- block.call(target)
- end
- end
- #
- # WMAP
- # This methods returns a list of all targets in the database
- #
- def targets
- WmapTarget.find(:all)
- end
- #
- # WMAP
- # This methods deletes all targets from targets table in the database
- #
- def delete_all_targets
- WmapTarget.delete_all
- end
- #
- # WMAP
- # Find a target matching this id
- #
- def get_target(id)
- target = WmapTarget.find(:first, :conditions => [ "id = ?", id])
- return target
- end
- #
- # WMAP
- # Create a target
- #
- def create_target(host,port,ssl,sel)
- tar = WmapTarget.create(
- :host => host,
- :address => host,
- :port => port,
- :ssl => ssl,
- :selected => sel
- )
- #framework.events.on_db_target(rec)
- end
- #
- # WMAP
- # Create a request (by hand)
- #
- def create_request(host,port,ssl,meth,path,headers,query,body,respcode,resphead,response)
- req = WmapRequest.create(
- :host => host,
- :address => host,
- :port => port,
- :ssl => ssl,
- :meth => meth,
- :path => path,
- :headers => headers,
- :query => query,
- :body => body,
- :respcode => respcode,
- :resphead => resphead,
- :response => response
- )
- #framework.events.on_db_request(rec)
- end
- #
- # WMAP
- # Quick way to query the database (used by wmap_sql)
- #
- def sql_query(sqlquery)
- ActiveRecord::Base.connection.select_all(sqlquery)
- end
- # Returns a REXML::Document from the given data.
- def rexmlify(data)
- if data.kind_of?(REXML::Document)
- return data
- else
- # Make an attempt to recover from a REXML import fail, since
- # it's better than dying outright.
- begin
- return REXML::Document.new(data)
- rescue REXML::ParseException => e
- dlog("REXML error: Badly formatted XML, attempting to recover. Error was: #{e.inspect}")
- return REXML::Document.new(data.gsub(/([\x00-\x08\x0b\x0c\x0e-\x19\x80-\xff])/){ |x| "\\x%.2x" % x.unpack("C*")[0] })
- end
- end
- end
- # Handles timestamps from Metasploit Express imports.
- def msf_import_timestamps(opts,obj)
- obj.created_at = o…
Large files files are truncated, but you can click here to view the full file