class Rex::Parser::NmapDocument

Public Instance Methods

better_os_match(orig_hash,new_hash) click to toggle source

Compare OS fingerprinting data

# File lib/rex/parser/nmap_nokogiri.rb, line 25
def better_os_match(orig_hash,new_hash)
  return false unless new_hash.has_key? "accuracy"
  return true unless orig_hash.has_key? "accuracy"
  new_hash["accuracy"].to_i > orig_hash["accuracy"].to_i
end
collect_host_data() click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 229
def collect_host_data
  if @state[:host_alive]
    @report_data[:state] = Msf::HostState::Alive
  else
    @report_data[:state] = Msf::HostState::Dead
  end
  if @state[:addresses]
    if @state[:addresses].has_key? "ipv4"
      @report_data[:host] = @state[:addresses]["ipv4"]
    elsif @state[:addresses].has_key? "ipv6"
      @report_data[:host] = @state[:addresses]["ipv6"]
    end
  end
  if @state[:addresses] and @state[:addresses].has_key?("mac")
    @report_data[:mac] = @state[:addresses]["mac"]
  end
  if @state[:hostname]
    @report_data[:name] = @state[:hostname]
  end
  if @state[:uptime]
    @report_data[:last_boot] = @state[:uptime]["lastboot"]
  end
  if @state[:trace] and @state[:trace].has_key?(:hops)
    @report_data[:traceroute] = @state[:trace]
  end
  if @state[:scripts]
    @report_data[:scripts] = @state[:scripts]
  end
end
collect_os_data() click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 211
def collect_os_data
  return unless in_tag("host")
  if @state[:os]
    @report_data[:os_fingerprint] = {
      :type => "host.os.nmap_fingerprint",
      :data => {
        :os_vendor => @state[:os]["vendor"],
        :os_family => @state[:os]["osfamily"],
        :os_version => @state[:os]["osgen"],
        :os_accuracy => @state[:os]["accuracy"].to_i
      }
    }
    if @state[:os].has_key? "osmatch"
      @report_data[:os_fingerprint][:data][:os_match] = @state[:os]["osmatch"]
    end
  end
end
collect_port_data() click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 259
def collect_port_data
  return unless in_tag("host")
  if @args[:fix_services]
    if @state[:port]["state"] == "filtered"
      return
    end
  end
  @report_data[:ports] ||= []
  port_hash = {}
  extra = []
  @state[:port].each do |k,v|
    case k
    when "protocol"
      port_hash[:proto] = v
    when "portid"
      port_hash[:port] = v
    when "state"
      port_hash[:state] = determine_port_state(v)
    when "name"
      port_hash[:name] = v
    when "tunnel"
      port_hash[:name] = "#{v}/#{port_hash[:name] || 'unknown'}"
    when "reason"
      port_hash[:reason] = v
    when "product"
      extra[0] = v
    when "version"
      extra[1] = v
    when "extrainfo"
      extra[2] = v
    when :scripts
      port_hash[:scripts] = v
    end
  end
  port_hash[:info] = extra.compact.join(" ") unless extra.empty?
  # Skip localhost port results when they're unknown
  if( port_hash[:reason] == "localhost-response" &&
      port_hash[:state] == Msf::ServiceState::Unknown )
    @report_data[:ports]
  else
    @report_data[:ports] << port_hash
  end
end
determine_port_state(v) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 11
def determine_port_state(v)
  case v
  when "open"
    Msf::ServiceState::Open
  when "closed"
    Msf::ServiceState::Closed
  when "filtered"
    Msf::ServiceState::Filtered
  else
    Msf::ServiceState::Unknown
  end
end
end_element(name=nil) click to toggle source

When we exit a tag, this is triggered.

# File lib/rex/parser/nmap_nokogiri.rb, line 69
def end_element(name=nil)
  block = @block
  case name
  when "os"
    collect_os_data
    @state[:os] = {}
  when "port"
    collect_port_data
    @state[:port] = {}
  when "host" # Roll everything up now
    collect_host_data
    host_object = report_host &block
    if host_object
      db.report_import_note(@args[:wspace],host_object)
      report_services(host_object,&block)
      report_fingerprint(host_object)
      report_uptime(host_object)
      report_traceroute(host_object)
    end
    @state.delete_if {|k| k != :current_tag}
    @report_data = {:wspace => @args[:wspace]}
  end
  @state[:current_tag].delete name
end
record_address(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 196
def record_address(attrs)
  return unless in_tag("host")
  @state[:addresses] ||= {}
  address = nil
  type = nil
  attrs.each do |k,v|
    if k == "addr"
      address = v
    elsif k == "addrtype"
      type = v
    end
  end
  @state[:addresses][type] = address
end
record_host_hop(attrs) click to toggle source

We can certainly get fancier with self.send() magic, but leaving this pretty simple for now.

# File lib/rex/parser/nmap_nokogiri.rb, line 97
def record_host_hop(attrs)
  return unless in_tag("host")
  return unless in_tag("trace")
  hops = attr_hash(attrs)
  hops["name"] = hops.delete "host"
  @state[:trace][:hops] << hops
end
record_host_osclass(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 126
def record_host_osclass(attrs)
  return unless in_tag("host")
  return unless in_tag("os")
  @state[:os] ||= {}
  temp_hash = attr_hash(attrs)
  if better_os_match(@state[:os],temp_hash)
    @state[:os] = temp_hash
  end
end
record_host_osmatch(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 116
def record_host_osmatch(attrs)
  return unless in_tag("host")
  return unless in_tag("os")
  temp_hash = attr_hash(attrs)
  if temp_hash["accuracy"].to_i == 100
    @state[:os] ||= {}
    @state[:os]["osmatch"] = temp_hash["name"]
  end
end
record_host_script(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 143
def record_host_script(attrs)
  return unless in_tag("host")
  return if in_tag("port")
  temp_hash = attr_hash(attrs)

  if temp_hash["id"] and temp_hash["output"]
    @state[:scripts] ||= []
    @state[:scripts] << { temp_hash["id"] => temp_hash["output"] }
  end
end
record_host_status(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 188
def record_host_status(attrs)
  return unless in_tag("host")
  attrs.each do |k,v|
    next unless k == "state"
    @state[:host_alive] = (v == "up")
  end
end
record_host_trace(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 105
def record_host_trace(attrs)
  return unless in_tag("host")
  @state[:trace] = attr_hash(attrs)
  @state[:trace][:hops] = []
end
record_host_uptime(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 111
def record_host_uptime(attrs)
  return unless in_tag("host")
  @state[:uptime] = attr_hash(attrs)
end
record_hostname(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 136
def record_hostname(attrs)
  return unless in_tag("host")
  if attr_hash(attrs)["type"] == "PTR"
    @state[:hostname] = attr_hash(attrs)["name"]
  end
end
record_port(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 181
def record_port(attrs)
  return unless in_tag("host")
  @state[:port] ||= {}
  svc = attr_hash(attrs)
  @state[:port] = @state[:port].merge(svc)
end
record_port_script(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 154
def record_port_script(attrs)
  return unless in_tag("host")
  return unless in_tag("port")
  temp_hash = attr_hash(attrs)
  if temp_hash["id"] and temp_hash["output"]
    @state[:port][:scripts] ||= []
    @state[:port][:scripts] << { temp_hash["id"] => temp_hash["output"] }
  end
end
record_port_service(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 164
def record_port_service(attrs)
  return unless in_tag("host")
  return unless in_tag("port")
  svc = attr_hash(attrs)
  if svc["name"] && @args[:fix_services]
    svc["name"] = db.nmap_msf_service_map(svc["name"])
  end
  @state[:port] = @state[:port].merge(svc)
end
record_port_state(attrs) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 174
def record_port_state(attrs)
  return unless in_tag("host")
  return unless in_tag("port")
  temp_hash = attr_hash(attrs)
  @state[:port] = @state[:port].merge(temp_hash)
end
report_fingerprint(host_object) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 329
def report_fingerprint(host_object)
  return unless host_object.kind_of? ::Mdm::Host
  return unless @report_data[:os_fingerprint]
  fp_note = @report_data[:os_fingerprint].merge(
    {
    :workspace => host_object.workspace,
    :host => host_object
  })
  db_report(:note, fp_note)
end
report_host(&block) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 340
def report_host(&block)
  if host_is_okay
    scripts = @report_data.delete(:scripts) || []
    host_object = db_report(:host, @report_data.merge( :workspace => @args[:wspace] ) )
    db.emit(:address,@report_data[:host],&block) if block

    scripts.each do |script|
      script.each_pair do |k,v|
        ntype =
        nse_note = {
          :workspace => host_object.workspace,
          :host => host_object,
          :type => "nmap.nse.#{k}.host",
          :data => { 'output' => v },
          :update => :unique_data
        }
        db_report(:note, nse_note)
      end
    end

    host_object
  end
end
report_services(host_object,&block) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 364
def report_services(host_object,&block)
  return unless host_object.kind_of? ::Mdm::Host
  return unless @report_data[:ports]
  return if @report_data[:ports].empty?
  reported = []
  @report_data[:ports].each do |svc|
    scripts = svc.delete(:scripts) || []
    svc_obj = db_report(:service, svc.merge(:host => host_object))
    scripts.each do |script|
      script.each_pair do |k,v|
        ntype =
        nse_note = {
          :workspace => host_object.workspace,
          :host => host_object,
          :service => svc_obj,
          :type => "nmap.nse.#{k}." + (svc[:proto] || "tcp") +".#{svc[:port]}",
          :data => { 'output' => v },
          :update => :unique_data
        }
        db_report(:note, nse_note)
      end
    end
    reported << svc_obj
  end
  reported
end
report_traceroute(host_object) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 303
def report_traceroute(host_object)
  return unless host_object.kind_of? ::Mdm::Host
  return unless @report_data[:traceroute]
  tr_note = {
    :workspace => host_object.workspace,
    :host => host_object,
    :type => "host.nmap.traceroute",
    :data => { 'port' => @report_data[:traceroute]["port"].to_i,
      'proto' => @report_data[:traceroute]["proto"].to_s,
      'hops' => @report_data[:traceroute][:hops] }
  }
  db_report(:note, tr_note)
end
report_uptime(host_object) click to toggle source
# File lib/rex/parser/nmap_nokogiri.rb, line 317
def report_uptime(host_object)
  return unless host_object.kind_of? ::Mdm::Host
  return unless @report_data[:last_boot]
  up_note = {
    :workspace => host_object.workspace,
    :host => host_object,
    :type => "host.last_boot",
    :data => { :time => @report_data[:last_boot] }
  }
  db_report(:note, up_note)
end
start_element(name=nil,attrs=[]) click to toggle source

Triggered every time a new element is encountered. We keep state ourselves with the @state variable, turning things on when we get here (and turning things off when we exit in end_element()).

# File lib/rex/parser/nmap_nokogiri.rb, line 34
def start_element(name=nil,attrs=[])
  attrs = normalize_attrs(attrs)
  block = @block
  @state[:current_tag][name] = true
  case name
  when "status"
    record_host_status(attrs)
  when "address"
    record_address(attrs)
  when "osclass"
    record_host_osclass(attrs)
  when "osmatch"
    record_host_osmatch(attrs)
  when "uptime"
    record_host_uptime(attrs)
  when "hostname"
    record_hostname(attrs)
  when "port"
    record_port(attrs)
  when "state"
    record_port_state(attrs)
  when "service"
    record_port_service(attrs)
  when "script" # Not actually used in import?
    record_port_script(attrs)
    record_host_script(attrs)
    # Ignoring post scripts completely
  when "trace"
    record_host_trace(attrs)
  when "hop"
    record_host_hop(attrs)
  end
end