Module: Msf::DBManager::Import::Nmap

Included in:
Msf::DBManager::Import
Defined in:
lib/msf/core/db_manager/import/nmap.rb

Instance Method Summary collapse

Instance Method Details

#import_nmap_noko_stream(args, &block) ⇒ Object


5
6
7
8
9
10
11
12
13
# File 'lib/msf/core/db_manager/import/nmap.rb', line 5

def import_nmap_noko_stream(args, &block)
  if block
    doc = Rex::Parser::NmapDocument.new(args,framework.db) {|type, data| yield type,data }
  else
    doc = Rex::Parser::NmapDocument.new(args,self)
  end
  parser = ::Nokogiri::XML::SAX::Parser.new(doc)
  parser.parse(args[:data])
end

#import_nmap_xml(args = {}) {|:parser, parser.class.name| ... } ⇒ Object

If you have Nokogiri installed, you'll be shunted over to that. Otherwise, you'll hit the old NmapXMLStreamParser.

Yields:

  • (:parser, parser.class.name)

17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/msf/core/db_manager/import/nmap.rb', line 17

def import_nmap_xml(args={}, &block)
  return nil if args[:data].nil? or args[:data].empty?
  wspace = Msf::Util::DBManager.process_opts_workspace(args, framework)
  bl = validate_ips(args[:blacklist]) ? args[:blacklist].split : []

  if Rex::Parser.nokogiri_loaded
    noko_args = args.dup
    noko_args[:blacklist] = bl
    noko_args[:workspace] = wspace
    if block
      yield(:parser, "Nokogiri v#{::Nokogiri::VERSION}")
      import_nmap_noko_stream(noko_args) {|type, data| yield type,data }
    else
      import_nmap_noko_stream(noko_args)
    end
    return true
  end

  # XXX: Legacy nmap xml parser starts here.

  fix_services = args[:fix_services]
  data = args[:data]

  # Use a stream parser instead of a tree parser so we can deal with
  # huge results files without running out of memory.
  parser = Rex::Parser::NmapXMLStreamParser.new
  yield(:parser, parser.class.name) if block

  # Whenever the parser pulls a host out of the nmap results, store
  # it, along with any associated services, in the database.
  parser.on_found_host = Proc.new { |h|
    hobj = nil
    data = {:workspace => wspace}
    if (h["addrs"].has_key?("ipv4"))
      addr = h["addrs"]["ipv4"]
    elsif (h["addrs"].has_key?("ipv6"))
      addr = h["addrs"]["ipv6"]
    else
      # Can't report it if it doesn't have an IP
      raise RuntimeError, "At least one IPv4 or IPv6 address is required"
    end
    next if bl.include? addr
    data[:host] = addr
    if (h["addrs"].has_key?("mac"))
      data[:mac] = h["addrs"]["mac"]
    end
    data[:state] = (h["status"] == "up") ? Msf::HostState::Alive : Msf::HostState::Dead
    data[:task] = args[:task]

    if ( h["reverse_dns"] )
      data[:name] = h["reverse_dns"]
    end

    # Only report alive hosts with ports to speak of.
    if(data[:state] != Msf::HostState::Dead)
      if h["ports"].size > 0
        if fix_services
          port_states = h["ports"].map {|p| p["state"]}.reject {|p| p == "filtered"}
          next if port_states.compact.empty?
        end
        yield(:address,data[:host]) if block
        hobj = report_host(data)
        report_import_note(wspace,hobj)
      end
    end

    if( h["os_vendor"] )
      note = {
        :workspace => wspace,
        :host => hobj || addr,
        :type => 'host.os.nmap_fingerprint',
        :task => args[:task],
        :data => {
          :os_vendor   => h["os_vendor"],
          :os_family   => h["os_family"],
          :os_version  => h["os_version"],
          :os_accuracy => h["os_accuracy"]
        }
      }

      if(h["os_match"])
        note[:data][:os_match] = h['os_match']
      end

      report_note(note)
    end

    if (h["last_boot"])
      report_note(
        :workspace => wspace,
        :host => hobj || addr,
        :type => 'host.last_boot',
        :task => args[:task],
        :data => {
          :time => h["last_boot"]
        }
      )
    end

    if (h["trace"])
      hops = []
      h["trace"]["hops"].each do |hop|
        hops << {
          "ttl"     => hop["ttl"].to_i,
          "address" => hop["ipaddr"].to_s,
          "rtt"     => hop["rtt"].to_f,
          "name"    => hop["host"].to_s
        }
      end
      report_note(
        :workspace => wspace,
        :host => hobj || addr,
        :type => 'host.nmap.traceroute',
        :task => args[:task],
        :data => {
          'port'  => h["trace"]["port"].to_i,
          'proto' => h["trace"]["proto"].to_s,
          'hops'  => hops
        }
      )
    end


    # Put all the ports, regardless of state, into the db.
    h["ports"].each { |p|
      # Localhost port results are pretty unreliable -- if it's
      # unknown, it's no good (possibly Windows-only)
      if (
        p["state"] == "unknown" &&
        h["status_reason"] == "localhost-response"
      )
        next
      end
      extra = ""
      extra << p["product"]   + " " if p["product"]
      extra << p["version"]   + " " if p["version"]
      extra << p["extrainfo"] + " " if p["extrainfo"]

      data = {}
      data[:workspace] = wspace
      if fix_services
        data[:proto] = nmap_msf_service_map(p["protocol"])
      else
        data[:proto] = p["protocol"].downcase
      end
      data[:port]  = p["portid"].to_i
      data[:state] = p["state"]
      data[:host]  = hobj || addr
      data[:info]  = extra if not extra.empty?
      data[:task]  = args[:task]
      data[:name]  = p['tunnel'] ? "#{p['tunnel']}/#{p['name'] || 'unknown'}" : p['name']
      report_service(data)
    }
    #Parse the scripts output
    if h["scripts"]
      h["scripts"].each do |key,val|
        if key == "smb-check-vulns"
          if val =~ /MS08-067: VULNERABLE/
            vuln_info = {
              :workspace => wspace,
              :task => args[:task],
              :host =>  hobj || addr,
              :port => 445,
              :proto => 'tcp',
              :name => 'MS08-067',
              :info => 'Microsoft Windows Server Service Crafted RPC Request Handling Unspecified Remote Code Execution',
              :refs =>['CVE-2008-4250',
                'BID-31874',
                'CWE-94',
                'MSFT-MS08-067',
                'MSF-Microsoft Server Service Relative Path Stack Corruption',
                'NSS-34476']
            }
            report_vuln(vuln_info)
          end
          if val =~ /MS06-025: VULNERABLE/
            vuln_info = {
              :workspace => wspace,
              :task => args[:task],
              :host =>  hobj || addr,
              :port => 445,
              :proto => 'tcp',
              :name => 'MS06-025',
              :info => 'Vulnerability in Routing and Remote Access Could Allow Remote Code Execution',
              :refs =>['CVE-2006-2370',
                'CVE-2006-2371',
                'BID-18325',
                'BID-18358',
                'BID-18424',
                'MSFT-MS06-025',
                'MSF-Microsoft RRAS Service RASMAN Registry Overflow',
                'NSS-21689']
            }
            report_vuln(vuln_info)
          end
          # This one has NOT been  Tested , remove this comment if confirmed working
          if val =~ /MS07-029: VULNERABLE/
            vuln_info = {
              :workspace => wspace,
              :task => args[:task],
              :host =>  hobj || addr,
              :port => 445,
              :proto => 'tcp',
              :name => 'MS07-029',
              :info => 'Vulnerability in Windows DNS RPC Interface Could Allow Remote Code Execution',
              # Add more refs based on nessus/nexpose .. results
              :refs =>['CVE-2007-1748',
                'MSF-Microsoft DNS RPC Service extractQuotedChar()',
                'NSS-25168']
            }
            report_vuln(vuln_info)
          end
        end
      end
    end
  }

  # XXX: Legacy nmap xml parser ends here.

  REXML::Document.parse_stream(data, parser)
end

#import_nmap_xml_file(args = {}) ⇒ Object

Import Nmap's -oX xml output


242
243
244
245
246
247
248
249
250
# File 'lib/msf/core/db_manager/import/nmap.rb', line 242

def import_nmap_xml_file(args={})
  filename = args[:filename]

  data = ""
  ::File.open(filename, 'rb') do |f|
    data = f.read(f.stat.size)
  end
  import_nmap_xml(args.merge(:data => data))
end

#nmap_msf_service_map(proto) ⇒ Object


252
253
254
# File 'lib/msf/core/db_manager/import/nmap.rb', line 252

def nmap_msf_service_map(proto)
  service_name_map(proto)
end