metasploit_data_models 0.21.0 → 0.21.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/Gemfile +3 -3
- data/app/models/mdm/host.rb +2 -2
- data/lib/mdm/host/operating_system_normalization.rb +803 -888
- data/lib/metasploit_data_models/version.rb +1 -1
- data/metasploit_data_models.gemspec +3 -0
- data/spec/app/models/mdm/host_spec.rb +337 -127
- data/spec/dummy/db/structure.sql +679 -0
- metadata +17 -172
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YjcyNDEyZmVhZDIwMWFkYmM2ODg0NzhkY2IyNzQyOGVkNjU1ODNjMw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
M2U0ZjMwMzdkZTgwZmQ4YTBhNDVjNTY2OTVjMzIxY2M5NjRhMWZlZg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Y2ExMGI1YjgxZTBmY2IxNzVlZTYyMDRlNWVmZGFhZTcxMWNiMDc0MDk4NTJm
|
10
|
+
ODBkYjRkNmQ2NGYzNWY1MTk4YTViNDEyZGQ1NDkzZjE1ODQ4ZWJiMjU5M2Fl
|
11
|
+
ZDg2YThmMTFlZDlkN2NlZWQ1MGI4ZDJlMjM2MDI1NTM4ZGY2ZDE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NWJhNzAxMzdkYmQ4MWVkOGUyNDJkYTIxZDQ4NjJjZDQxYzQwNzM5NDdiNWE4
|
14
|
+
Yzg2YjQwMzU1ZDhhM2I4YzZlYWE2Y2I4MWFhNWVjOGMyZjFhZTE5YmMyZjEy
|
15
|
+
Zjk5MjQ4NTQ1YTMzODliNDU3YTNjZjlmMzc1NjdkY2QxZjA5OTk=
|
data/Gemfile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
source "
|
1
|
+
source "https://rubygems.org"
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in metasploit_data_models.gemspec
|
4
4
|
gemspec
|
@@ -10,6 +10,8 @@ end
|
|
10
10
|
|
11
11
|
# used by dummy application
|
12
12
|
group :development, :test do
|
13
|
+
# Upload coverage reports to coveralls.io
|
14
|
+
gem 'coveralls', require: false
|
13
15
|
# supplies factories for producing model instance for specs
|
14
16
|
# Version 4.1.0 or newer is needed to support generate calls without the 'FactoryGirl.' in factory definitions syntax.
|
15
17
|
gem 'factory_girl', '>= 4.1.0'
|
@@ -24,8 +26,6 @@ group :development, :test do
|
|
24
26
|
end
|
25
27
|
|
26
28
|
group :test do
|
27
|
-
# Upload coverage reports to coveralls.io
|
28
|
-
gem 'coveralls', require: false
|
29
29
|
# In a full rails project, factory_girl_rails would be in both the :development, and :test group, but since we only
|
30
30
|
# want rails in :test, factory_girl_rails must also only be in :test.
|
31
31
|
# add matchers from shoulda, such as validates_presence_of, which are useful for testing validations
|
data/app/models/mdm/host.rb
CHANGED
@@ -359,7 +359,7 @@ class Mdm::Host < ActiveRecord::Base
|
|
359
359
|
# The flavor of {#os_name}.
|
360
360
|
#
|
361
361
|
# @example Windows XP
|
362
|
-
# host.os_name = '
|
362
|
+
# host.os_name = 'Windows'
|
363
363
|
# host.os_flavor = 'XP'
|
364
364
|
#
|
365
365
|
# @return [String]
|
@@ -379,7 +379,7 @@ class Mdm::Host < ActiveRecord::Base
|
|
379
379
|
# The service pack of the {#os_flavor} of the {#os_name}.
|
380
380
|
#
|
381
381
|
# @example Windows XP SP2
|
382
|
-
# host.os_name = '
|
382
|
+
# host.os_name = 'Windows'
|
383
383
|
# host.os_flavor = 'XP'
|
384
384
|
# host.os_sp = 'SP2'
|
385
385
|
#
|
@@ -1,705 +1,292 @@
|
|
1
|
+
#
|
2
|
+
# Leverage the Recog gem as much as possible for sane fingerprint management
|
3
|
+
#
|
4
|
+
require 'recog'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Rules for operating system fingerprinting in Metasploit
|
8
|
+
#
|
9
|
+
# The `os.product` key identifies the common-name of a specific operating system
|
10
|
+
# Examples include: Linux, Windows XP, Mac OS X, IOS, AIX, HP-UX, VxWorks
|
11
|
+
#
|
12
|
+
# The `os.version` key identifies the service pack or version of the operating system
|
13
|
+
# Sometimes this means a kernel or firmware version when the distribution or OS
|
14
|
+
# version is not available.
|
15
|
+
# Examples include: SP2, 10.04, 2.6.47, 10.6.1
|
16
|
+
#
|
17
|
+
# The `os.vendor` key identifies the manufacturer of the operating system
|
18
|
+
# Examples include: Microsoft, Ubuntu, Cisco, HP, IBM, Wind River
|
19
|
+
#
|
20
|
+
# The `os.family` key identifies the group of the operating system. This is often a
|
21
|
+
# duplicate of os.product, unless a more specific product name is available.
|
22
|
+
# Examples include: Windows, Linux, IOS, HP-UX, AIX
|
23
|
+
#
|
24
|
+
# The `os.edition` key identifies the specific variant of the operating system
|
25
|
+
# Examples include: Enterprise, Professional, Starter, Evaluation, Home, Datacenter
|
26
|
+
#
|
27
|
+
# An example breakdown of a common operating system is shown below
|
28
|
+
#
|
29
|
+
# * Microsoft Windows XP Professional Service Pack 3 English (x86)
|
30
|
+
# - os.product = 'Windows XP'
|
31
|
+
# - os.edition = 'Professional'
|
32
|
+
# - os.vendor = 'Microsoft'
|
33
|
+
# - os.version = 'SP3'
|
34
|
+
# - os.language = 'English'
|
35
|
+
# - os.arch = 'x86'
|
36
|
+
#
|
37
|
+
# These rules are then mapped to the {Mdm::Host} attributes below:
|
38
|
+
#
|
39
|
+
# * os_name - Maps to a normalized os.product key
|
40
|
+
# * os_flavor - Maps to a normalized os.edition key
|
41
|
+
# * os_sp - Maps to a normalized os.version key (soon os_version)
|
42
|
+
# * os_lang - Maps to a normalized os.language key
|
43
|
+
# * arch - Maps to a normalized os.arch key
|
44
|
+
#
|
45
|
+
# Additional rules include the following mappings:
|
46
|
+
#
|
47
|
+
# * name - Maps to the host.name key
|
48
|
+
# * mac - Maps to the host.mac key
|
49
|
+
#
|
50
|
+
# The following keys are not mapped to {Mdm::Host} at this time (but should be):
|
51
|
+
#
|
52
|
+
# * os.vendor
|
53
|
+
#
|
54
|
+
# In order to execute these rules, this module is responsible for mapping various
|
55
|
+
# fingerprint sources to {Mdm::Host} values. This requires some ugly glue code to
|
56
|
+
# account for differences between each supported input (external scanners), the
|
57
|
+
# Recog gem and associated databases, and how Metasploit itself likes to handle
|
58
|
+
# these values. Getting a mapping wrong is often harmless, but can impact the
|
59
|
+
# automatic targetting capabilities of certain exploit modules.
|
60
|
+
#
|
61
|
+
# In other words, this is a best-effort attempt to rationalize multiple competing
|
62
|
+
# sources of information about a host and come up with the values representing a
|
63
|
+
# normalized assessment of the system. The use of `Recog` and multiple scanner
|
64
|
+
# fingerprints can result in a comprehensive (and confident) identification of the
|
65
|
+
# remote operating system and associated services.
|
66
|
+
#
|
67
|
+
# Historically, there are direct conflicts between certain Metasploit modules,
|
68
|
+
# certain scanners, and external fingerprint databases in terms of how a
|
69
|
+
# particular OS and patch level is represented. This module attempts to fix what
|
70
|
+
# it can and serve as documentation and live workarounds for the rest.
|
71
|
+
#
|
72
|
+
# Examples of known conflicts that are still in progress:
|
73
|
+
#
|
74
|
+
# * Metasploit defines an OS constant of 'win'/'windows' as Microsoft Windows
|
75
|
+
#
|
76
|
+
# - Scanner modules report a mix of 'Microsoft Windows' and 'Windows'
|
77
|
+
# - Nearly all exploit modules reference 'Windows <Release> SP<Version>'
|
78
|
+
# - Nmap (and other scanners) also prefix the vendor before Windows
|
79
|
+
#
|
80
|
+
#
|
81
|
+
# * Windows service packs represented as 'Service Pack X' or 'SPX'
|
82
|
+
#
|
83
|
+
# - The preferred form is to set os.version to 'SPX'
|
84
|
+
# - Many external scanners & Recog prefer 'Service Pack X'
|
85
|
+
#
|
86
|
+
# * Apple Mac OS X, Cisco IOS, IBM AIX, Ubuntu Linux, all reported with vendor prefix
|
87
|
+
#
|
88
|
+
# - The preferred form is to remove the vendor from os.product
|
89
|
+
# - {Mdm::Host} currently has no vendor field, so this information is lost today
|
90
|
+
# - Many scanners report leading vendor strings and require normalization
|
91
|
+
#
|
92
|
+
# * The os_flavor field is used in contradictory ways across Metasploit
|
93
|
+
#
|
94
|
+
# - The preferred form is to be a 'display only' field
|
95
|
+
# - Some Recog fingerprints still append the edition to os.product
|
96
|
+
# - Many scanners report the edition as a trailing suffix to os.product
|
97
|
+
#
|
98
|
+
#
|
99
|
+
#
|
100
|
+
#
|
101
|
+
# Maintenance:
|
102
|
+
#
|
103
|
+
# 1. Ensure that the latest Recog gem is present and installed
|
104
|
+
# 2. For new operating system releases, update relevant sections
|
105
|
+
# a) Windows releases will require updates to a few methods
|
106
|
+
# 1) parse_windows_os_str()
|
107
|
+
# 2) normalize_nmap_fingerprint()
|
108
|
+
# 3) normalize_nexpose_fingerprint()
|
109
|
+
# 4) Other scanner normalizers
|
110
|
+
# b) Mobile operating systems are minimally recognized
|
111
|
+
#
|
112
|
+
#
|
113
|
+
# @todo Handle OS icon incompatiblities with new fingerprint names
|
114
|
+
# Note that VMWare ESX(i) was special cased before as well, make sure it still works
|
115
|
+
# 1) Cisco IOS -> IOS breaks the icon mapping in MSP/MSCE of /cisco/
|
116
|
+
# 2) Ubuntu Linux -> Linux breaks the distro selection
|
117
|
+
# The real solution is to add os_vendor and take this into account for icons
|
118
|
+
#
|
119
|
+
# @todo Implement rspec coverage for normalize_os()
|
120
|
+
# @todo Implement smb.generic fingerprint database (replace {#parse_windows_os_str}?)
|
121
|
+
# @todo Implement Samba version matching for specific distributions and OS versions
|
122
|
+
# @todo Implement DD-WRT and various embedded device signatures currently missing
|
123
|
+
# @todo Correct inconsistencies in os_name use by removing the vendor string (Microsoft Windows -> Windows)
|
124
|
+
# This applies to MSF core and a handful of modules, not to mention some Recog fingerprints.
|
125
|
+
# @todo Rename host.os_sp to host.os_version
|
126
|
+
# @todo Add host.os_vendor
|
127
|
+
# @todo Add host.os_confidence
|
128
|
+
# @todo Add host.domain
|
129
|
+
#
|
1
130
|
module Mdm::Host::OperatingSystemNormalization
|
131
|
+
|
132
|
+
# Cap nmap certainty at 0.84 until we update it more frequently
|
133
|
+
# XXX: Without this, Nmap will beat the default certainty of recog
|
134
|
+
# matches and its less-confident guesses will take precedence
|
135
|
+
# over service-based fingerprints.
|
136
|
+
MAX_NMAP_CERTAINTY = 0.84
|
137
|
+
|
2
138
|
#
|
3
139
|
# Normalize the operating system fingerprints provided by various scanners
|
4
|
-
# (nmap, nexpose, retina, nessus,
|
140
|
+
# (nmap, nexpose, retina, nessus, metasploit modules, and more!)
|
5
141
|
#
|
6
|
-
# These are stored as notes (instead of directly in the os_*
|
7
|
-
# specifically for this purpose.
|
142
|
+
# These are stored as {Mdm::Note notes} (instead of directly in the os_*
|
143
|
+
# fields) specifically for this purpose.
|
144
|
+
#
|
145
|
+
# The goal is to infer as much as we can about the OS of the device and the
|
146
|
+
# various {Mdm::Service services} offered using the Recog gem and some glue
|
147
|
+
# logic to determine the best weights. This method can result in changes to
|
148
|
+
# the recorded {#os_name}, {#os_flavor}, {#os_sp}, {#os_lang}, {#purpose},
|
149
|
+
# {#name}, {#arch}, and the {Mdm::Service service details}.
|
8
150
|
#
|
9
151
|
def normalize_os
|
10
|
-
host
|
11
|
-
|
12
|
-
wname = {} # os_name == Linux, Windows, Mac OS X, VxWorks
|
13
|
-
wtype = {} # purpose == server, client, device
|
14
|
-
wflav = {} # os_flavor == Ubuntu, Debian, 2003, 10.5, JetDirect
|
15
|
-
wvers = {} # os_sp == 9.10, SP2, 10.5.3, 3.05
|
16
|
-
warch = {} # arch == x86, PPC, SPARC, MIPS, ''
|
17
|
-
wlang = {} # os_lang == English, ''
|
18
|
-
whost = {} # hostname
|
152
|
+
host = self
|
153
|
+
matches = []
|
19
154
|
|
20
155
|
# Note that we're already restricting the query to this host by using
|
21
156
|
# host.notes instead of Note, so don't need a host_id in the
|
22
157
|
# conditions.
|
23
158
|
fingerprintable_notes = self.notes.where("ntype like '%%fingerprint'")
|
24
|
-
fingerprintable_notes.each do |
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
wname[norm[:os_name]] = wname[norm[:os_name]].to_i + (100 * norm[:certainty])
|
29
|
-
wflav[norm[:os_flavor]] = wflav[norm[:os_flavor]].to_i + (100 * norm[:certainty])
|
30
|
-
warch[norm[:arch]] = warch[norm[:arch]].to_i + (100 * norm[:certainty])
|
31
|
-
whost[norm[:name]] = whost[norm[:name]].to_i + (100 * norm[:certainty])
|
32
|
-
wtype[norm[:type]] = wtype[norm[:type]].to_i + (100 * norm[:certainty])
|
33
|
-
end
|
34
|
-
|
35
|
-
# Grab service information and assign scores. Some services are
|
36
|
-
# more trustworthy than others. If more services agree than not,
|
37
|
-
# than that should be considered as well.
|
38
|
-
# Each service has a starting number of points. Services that
|
39
|
-
# are more difficult to fake are awarded more points. The points
|
40
|
-
# represent a running total, not a fixed score.
|
41
|
-
# XXX: This needs to be refactored in a big way. Tie-breaking is
|
42
|
-
# pretty arbitrary, it would be nice to explicitly believe some
|
43
|
-
# services over others, but that means recording which service
|
44
|
-
# has an opinion and which doesn't. It would also be nice to
|
45
|
-
# identify "impossible" combinations of services and alert that
|
46
|
-
# something funny is going on.
|
159
|
+
fingerprintable_notes.each do |fp_note|
|
160
|
+
matches += recog_matches_for_note(fp_note)
|
161
|
+
end
|
162
|
+
|
47
163
|
# XXX: This hack solves the memory leak generated by self.services.each {}
|
48
164
|
fingerprintable_services = self.services.where("name is not null and name != '' and info is not null and info != ''")
|
49
165
|
fingerprintable_services.each do |s|
|
50
|
-
|
51
|
-
|
52
|
-
when 'smb'
|
53
|
-
points = 210
|
54
|
-
case s.info
|
55
|
-
when /\.el([23456])(\s+|$)/ # Match Samba 3.0.33-0.30.el4 as RHEL4
|
56
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
57
|
-
wflav["RHEL" + $1] = wflav["RHEL" + $1].to_i + points
|
58
|
-
wtype['server'] = wtype['server'].to_i + points
|
59
|
-
when /(ubuntu|debian|fedora|red ?hat|rhel)/i
|
60
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
61
|
-
wflav[$1.capitalize] = wflav[$1.capitalize].to_i + points
|
62
|
-
wtype['server'] = wtype['server'].to_i + points
|
63
|
-
when /^Windows/
|
64
|
-
win_sp = nil
|
65
|
-
win_flav = nil
|
66
|
-
win_lang = nil
|
67
|
-
|
68
|
-
ninfo = s.info
|
69
|
-
ninfo.gsub!('(R)', '')
|
70
|
-
ninfo.gsub!('(TM)', '')
|
71
|
-
ninfo.gsub!(/\s+/, ' ')
|
72
|
-
ninfo.gsub!('No Service Pack', 'Service Pack 0')
|
73
|
-
|
74
|
-
# Windows (R) Web Server 2008 6001 Service Pack 1 (language: Unknown) (name:PG-WIN2008WEB) (domain:WORKGROUP)
|
75
|
-
# Windows XP Service Pack 3 (language: English) (name:EGYPT-B3E55BF3C) (domain:EGYPT-B3E55BF3C)
|
76
|
-
# Windows 7 Ultimate (Build 7600) (language: Unknown) (name:WIN7) (domain:WORKGROUP)
|
77
|
-
# Windows 2003 No Service Pack (language: Unknown) (name:VMWIN2003) (domain:PWNME)
|
78
|
-
|
79
|
-
#if ninfo =~ /^Windows ([^\s]+)(.*)(Service Pack |\(Build )([^\(]+)\(/
|
80
|
-
if ninfo =~ /^Windows (.*)(Service Pack [^\s]+|\(Build [^\)]+\))/
|
81
|
-
win_flav = $1.strip
|
82
|
-
win_sp = ($2).strip
|
83
|
-
win_sp.gsub!(/with.*/, '')
|
84
|
-
win_sp.gsub!('Service Pack', 'SP')
|
85
|
-
win_sp.gsub!('Build', 'b')
|
86
|
-
win_sp.gsub!(/\s+/, '')
|
87
|
-
win_sp.tr!("()", '')
|
88
|
-
else
|
89
|
-
if ninfo =~ /^Windows ([^\s+]+)([^\(]+)\(/
|
90
|
-
win_flav = $2.strip
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
|
95
|
-
if ninfo =~ /name: ([^\)]+)\)/
|
96
|
-
hostname = $1.strip
|
97
|
-
end
|
98
|
-
|
99
|
-
if ninfo =~ /language: ([^\)]+)\)/
|
100
|
-
win_lang = $1.strip
|
101
|
-
end
|
102
|
-
|
103
|
-
win_lang = nil if win_lang =~ /unknown/i
|
104
|
-
win_vers = win_sp
|
105
|
-
|
106
|
-
wname['Microsoft Windows'] = wname['Microsoft Windows'].to_i + points
|
107
|
-
wlang[win_lang] = wlang[win_lang].to_i + points if win_lang
|
108
|
-
wflav[win_flav] = wflav[win_flav].to_i + points if win_flav
|
109
|
-
wvers[win_vers] = wvers[win_vers].to_i + points if win_vers
|
110
|
-
whost[hostname] = whost[hostname].to_i + points if hostname
|
111
|
-
|
112
|
-
case win_flav
|
113
|
-
when /NT|2003|2008/
|
114
|
-
win_type = 'server'
|
115
|
-
else
|
116
|
-
win_type = 'client'
|
117
|
-
end
|
118
|
-
wtype[win_type] = wtype[win_type].to_i + points
|
119
|
-
end
|
166
|
+
matches += recog_matches_for_service(s)
|
167
|
+
end
|
120
168
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
wflav['Ubuntu'] = wflav['Ubuntu'].to_i + points
|
131
|
-
wtype['server'] = wtype['server'].to_i + points
|
132
|
-
when /debian/i
|
133
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
134
|
-
wflav['Debian'] = wflav['Debian'].to_i + points
|
135
|
-
wtype['server'] = wtype['server'].to_i + points
|
136
|
-
when /FreeBSD/
|
137
|
-
wname['FreeBSD'] = wname['FreeBSD'].to_i + points
|
138
|
-
wtype['server'] = wtype['server'].to_i + points
|
139
|
-
when /sun_ssh/i
|
140
|
-
wname['Sun Solaris'] = wname['Sun Solaris'].to_i + points
|
141
|
-
wtype['server'] = wtype['server'].to_i + points
|
142
|
-
when /vshell|remotelyanywhere|freessh/i
|
143
|
-
wname['Microsoft Windows'] = wname['Microsoft Windows'].to_i + points
|
144
|
-
wtype['server'] = wtype['server'].to_i + points
|
145
|
-
|
146
|
-
when /radware/i
|
147
|
-
wname['RadWare'] = wname['RadWare'].to_i + points
|
148
|
-
wtype['device'] = wtype['device'].to_i + points
|
149
|
-
|
150
|
-
when /dropbear/i
|
151
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
152
|
-
wtype['device'] = wtype['device'].to_i + points
|
153
|
-
|
154
|
-
when /netscreen/i
|
155
|
-
wname['NetScreen'] = wname['NetScreen'].to_i + points
|
156
|
-
wtype['device'] = wtype['device'].to_i + points
|
157
|
-
|
158
|
-
when /vpn3/
|
159
|
-
wname['Cisco VPN 3000'] = wname['Cisco VPN 3000'].to_i + points
|
160
|
-
wtype['device'] = wtype['device'].to_i + points
|
161
|
-
|
162
|
-
when /cisco/i
|
163
|
-
wname['Cisco IOS'] = wname['Cisco IOS'].to_i + points
|
164
|
-
wtype['device'] = wtype['device'].to_i + points
|
165
|
-
|
166
|
-
when /mpSSH/
|
167
|
-
wname['HP iLO'] = wname['HP iLO'].to_i + points
|
168
|
-
wtype['server'] = wtype['server'].to_i + points
|
169
|
-
end
|
170
|
-
when 'http'
|
171
|
-
points = 99
|
172
|
-
case s.info
|
173
|
-
when /iSeries/
|
174
|
-
wname['IBM iSeries'] = wname['IBM iSeries'].to_i + points
|
175
|
-
wtype['server'] = wtype['server'].to_i + points
|
176
|
-
|
177
|
-
when /Mandrake/i
|
178
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
179
|
-
wflav['Mandrake'] = wflav['Mandrake'].to_i + points
|
180
|
-
wtype['server'] = wtype['server'].to_i + points
|
181
|
-
|
182
|
-
when /Mandriva/i
|
183
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
184
|
-
wflav['Mandrake'] = wflav['Mandrake'].to_i + points
|
185
|
-
wtype['server'] = wtype['server'].to_i + points
|
186
|
-
|
187
|
-
when /Ubuntu/i
|
188
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
189
|
-
wflav['Ubuntu'] = wflav['Ubuntu'].to_i + points
|
190
|
-
wtype['server'] = wtype['server'].to_i + points
|
191
|
-
|
192
|
-
when /Debian/i
|
193
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
194
|
-
wflav['Debian'] = wflav['Debian'].to_i + points
|
195
|
-
wtype['server'] = wtype['server'].to_i + points
|
196
|
-
|
197
|
-
when /Fedora/i
|
198
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
199
|
-
wflav['Fedora'] = wflav['Fedora'].to_i + points
|
200
|
-
wtype['server'] = wtype['server'].to_i + points
|
201
|
-
|
202
|
-
when /CentOS/i
|
203
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
204
|
-
wflav['CentOS'] = wflav['CentOS'].to_i + points
|
205
|
-
wtype['server'] = wtype['server'].to_i + points
|
206
|
-
|
207
|
-
when /RHEL/i
|
208
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
209
|
-
wflav['RHEL'] = wflav['RHEL'].to_i + points
|
210
|
-
wtype['server'] = wtype['server'].to_i + points
|
211
|
-
|
212
|
-
when /Red.?Hat/i
|
213
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
214
|
-
wflav['Red Hat'] = wflav['Red Hat'].to_i + points
|
215
|
-
wtype['server'] = wtype['server'].to_i + points
|
216
|
-
|
217
|
-
when /SuSE/i
|
218
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
219
|
-
wflav['SUSE'] = wflav['SUSE'].to_i + points
|
220
|
-
wtype['server'] = wtype['server'].to_i + points
|
221
|
-
|
222
|
-
when /TurboLinux/i
|
223
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
224
|
-
wflav['TurboLinux'] = wflav['TurboLinux'].to_i + points
|
225
|
-
wtype['server'] = wtype['server'].to_i + points
|
226
|
-
|
227
|
-
when /Gentoo/i
|
228
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
229
|
-
wflav['Gentoo'] = wflav['Gentoo'].to_i + points
|
230
|
-
wtype['server'] = wtype['server'].to_i + points
|
231
|
-
|
232
|
-
when /Conectiva/i
|
233
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
234
|
-
wflav['Conectiva'] = wflav['Conectiva'].to_i + points
|
235
|
-
wtype['server'] = wtype['server'].to_i + points
|
236
|
-
|
237
|
-
when /Asianux/i
|
238
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
239
|
-
wflav['Asianux'] = wflav['Asianux'].to_i + points
|
240
|
-
wtype['server'] = wtype['server'].to_i + points
|
241
|
-
|
242
|
-
when /Trustix/i
|
243
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
244
|
-
wflav['Trustix'] = wflav['Trustix'].to_i + points
|
245
|
-
wtype['server'] = wtype['server'].to_i + points
|
246
|
-
|
247
|
-
when /White Box/
|
248
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
249
|
-
wflav['White Box'] = wflav['White Box'].to_i + points
|
250
|
-
wtype['server'] = wtype['server'].to_i + points
|
251
|
-
|
252
|
-
when /UnitedLinux/
|
253
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
254
|
-
wflav['UnitedLinux'] = wflav['UnitedLinux'].to_i + points
|
255
|
-
wtype['server'] = wtype['server'].to_i + points
|
256
|
-
|
257
|
-
when /PLD\/Linux/
|
258
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
259
|
-
wflav['PLD/Linux'] = wflav['PLD/Linux'].to_i + points
|
260
|
-
wtype['server'] = wtype['server'].to_i + points
|
261
|
-
|
262
|
-
when /Vine\/Linux/
|
263
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
264
|
-
wflav['Vine/Linux'] = wflav['Vine/Linux'].to_i + points
|
265
|
-
wtype['server'] = wtype['server'].to_i + points
|
266
|
-
|
267
|
-
when /rPath/
|
268
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
269
|
-
wflav['rPath'] = wflav['rPath'].to_i + points
|
270
|
-
wtype['server'] = wtype['server'].to_i + points
|
271
|
-
|
272
|
-
when /StartCom/
|
273
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
274
|
-
wflav['StartCom'] = wflav['StartCom'].to_i + points
|
275
|
-
wtype['server'] = wtype['server'].to_i + points
|
276
|
-
|
277
|
-
when /linux/i
|
278
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
279
|
-
wtype['server'] = wtype['server'].to_i + points
|
280
|
-
|
281
|
-
when /PalmOS/
|
282
|
-
wname['PalmOS'] = wname['PalmOS'].to_i + points
|
283
|
-
wtype['device'] = wtype['device'].to_i + points
|
284
|
-
|
285
|
-
when /Microsoft[\x20\x2d]IIS\/[234]\.0/
|
286
|
-
wname['Microsoft Windows NT 4.0'] = wname['Microsoft Windows NT 4.0'].to_i + points
|
287
|
-
wtype['server'] = wtype['server'].to_i + points
|
288
|
-
|
289
|
-
when /Microsoft[\x20\x2d]IIS\/5\.0/
|
290
|
-
wname['Microsoft Windows 2000'] = wname['Microsoft Windows 2000'].to_i + points
|
291
|
-
wtype['server'] = wtype['server'].to_i + points
|
292
|
-
|
293
|
-
when /Microsoft[\x20\x2d]IIS\/5\.1/
|
294
|
-
wname['Microsoft Windows XP'] = wname['Microsoft Windows XP'].to_i + points
|
295
|
-
wtype['server'] = wtype['server'].to_i + points
|
296
|
-
|
297
|
-
when /Microsoft[\x20\x2d]IIS\/6\.0/
|
298
|
-
wname['Microsoft Windows 2003'] = wname['Microsoft Windows 2003'].to_i + points
|
299
|
-
wtype['server'] = wtype['server'].to_i + points
|
300
|
-
|
301
|
-
when /Microsoft[\x20\x2d]IIS\/7\.0/
|
302
|
-
wname['Microsoft Windows 2008'] = wname['Microsoft Windows 2008'].to_i + points
|
303
|
-
wtype['server'] = wtype['server'].to_i + points
|
304
|
-
|
305
|
-
when /Win32/i
|
306
|
-
wname['Microsoft Windows'] = wname['Microsoft Windows'].to_i + points
|
307
|
-
wtype['server'] = wtype['server'].to_i + points
|
308
|
-
|
309
|
-
when /DD\-WRT ([^\s]+) /i
|
310
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
311
|
-
wflav['DD-WRT'] = wflav['DD-WRT'].to_i + points
|
312
|
-
wvers[$1.strip] = wvers[$1.strip].to_i + points
|
313
|
-
wtype['server'] = wtype['server'].to_i + points
|
314
|
-
|
315
|
-
when /Darwin/
|
316
|
-
wname['Apple Mac OS X'] = wname['Apple Mac OS X'].to_i + points
|
317
|
-
|
318
|
-
when /FreeBSD/i
|
319
|
-
wname['FreeBSD'] = wname['FreeBSD'].to_i + points
|
320
|
-
|
321
|
-
when /OpenBSD/i
|
322
|
-
wname['OpenBSD'] = wname['OpenBSD'].to_i + points
|
323
|
-
|
324
|
-
when /NetBSD/i
|
325
|
-
wname['NetBSD'] = wname['NetBSD'].to_i + points
|
326
|
-
|
327
|
-
when /NetWare/i
|
328
|
-
wname['Novell NetWare'] = wname['Novell NetWare'].to_i + points
|
329
|
-
|
330
|
-
when /OpenVMS/i
|
331
|
-
wname['OpenVMS'] = wname['OpenVMS'].to_i + points
|
332
|
-
|
333
|
-
when /SunOS|Solaris/i
|
334
|
-
wname['Sun Solaris'] = wname['Sun Solaris'].to_i + points
|
169
|
+
#
|
170
|
+
# Look for generic fingerprint.match notes that generate a match hash from modules
|
171
|
+
# This handles ad-hoc os.language, host.name, etc identifications
|
172
|
+
#
|
173
|
+
generated_matches = self.notes.where(ntype: 'fingerprint.match')
|
174
|
+
generated_matches.each do |m|
|
175
|
+
next unless (m.data and m.data.kind_of?(::Hash))
|
176
|
+
matches << m.data.dup
|
177
|
+
end
|
335
178
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
whost[$1] = whost[$1].to_i + points
|
353
|
-
wname['Linux ' + $2] = wname['Linux ' + $2].to_i + points
|
354
|
-
wvers[$2] = wvers[$2].to_i + points
|
355
|
-
arch = get_arch_from_string(s.info)
|
356
|
-
warch[arch] = warch[arch].to_i + points if arch
|
357
|
-
wtype['server'] = wtype['server'].to_i + points
|
358
|
-
|
359
|
-
when /^Novell NetWare ([^\s]+)/
|
360
|
-
wname['Novell NetWare ' + $1] = wname['Novell NetWare ' + $1].to_i + points
|
361
|
-
wvers[$1] = wvers[$1].to_i + points
|
362
|
-
arch = "x86"
|
363
|
-
warch[arch] = warch[arch].to_i + points
|
364
|
-
wtype['server'] = wtype['server'].to_i + points
|
365
|
-
|
366
|
-
when /^Novell UnixWare ([^\s]+)/
|
367
|
-
wname['Novell UnixWare ' + $1] = wname['Novell UnixWare ' + $1].to_i + points
|
368
|
-
wvers[$1] = wvers[$1].to_i + points
|
369
|
-
arch = "x86"
|
370
|
-
warch[arch] = warch[arch].to_i + points
|
371
|
-
wtype['server'] = wtype['server'].to_i + points
|
372
|
-
|
373
|
-
when /^HP-UX ([^\s]+) ([^\s]+) /
|
374
|
-
# XXX
|
375
|
-
wname['HP-UX ' + $2] = wname['HP-UX ' + $2].to_i + points
|
376
|
-
wvers[$1] = wvers[$1].to_i + points
|
377
|
-
wtype['server'] = wtype['server'].to_i + points
|
378
|
-
|
379
|
-
when /^IBM PowerPC.*Base Operating System Runtime AIX version: (\d+\.\d+)/
|
380
|
-
wname['IBM AIX ' + $1] = wname['IBM AIX ' + $1].to_i + points
|
381
|
-
wvers[$1] = wvers[$1].to_i + points
|
382
|
-
wtype['server'] = wtype['server'].to_i + points
|
383
|
-
|
384
|
-
when /^SCO TCP\/IP Runtime Release ([^\s]+)/
|
385
|
-
wname['SCO UnixWare ' + $1] = wname['SCO UnixWare ' + $1].to_i + points
|
386
|
-
wvers[$1] = wvers[$1].to_i + points
|
387
|
-
wtype['server'] = wtype['server'].to_i + points
|
388
|
-
|
389
|
-
when /.* IRIX version ([^\s]+)/
|
390
|
-
wname['SGI IRIX ' + $1] = wname['SGI IRIX ' + $1].to_i + points
|
391
|
-
wvers[$1] = wvers[$1].to_i + points
|
392
|
-
wtype['server'] = wtype['server'].to_i + points
|
393
|
-
|
394
|
-
when /^Unisys ([^\s]+) version ([^\s]+) kernel/
|
395
|
-
wname['Unisys ' + $2] = wname['Unisys ' + $2].to_i + points
|
396
|
-
wvers[$2] = wvers[$2].to_i + points
|
397
|
-
whost[$1] = whost[$1].to_i + points
|
398
|
-
wtype['server'] = wtype['server'].to_i + points
|
399
|
-
|
400
|
-
when /.*OpenVMS V([^\s]+) /
|
401
|
-
# XXX
|
402
|
-
wname['OpenVMS ' + $1] = wname['OpenVMS ' + $1].to_i + points
|
403
|
-
wvers[$1] = wvers[$1].to_i + points
|
404
|
-
wtype['server'] = wtype['server'].to_i + points
|
405
|
-
|
406
|
-
when /^Hardware:.*Software: Windows NT Version ([^\s]+) /
|
407
|
-
wname['Microsoft Windows NT ' + $1] = wname['Microsoft Windows NT ' + $1].to_i + points
|
408
|
-
wtype['server'] = wtype['server'].to_i + points
|
409
|
-
|
410
|
-
when /^Hardware:.*Software: Windows 2000 Version 5\.0/
|
411
|
-
wname['Microsoft Windows 2000'] = wname['Microsoft Windows 2000'].to_i + points
|
412
|
-
wtype['server'] = wtype['server'].to_i + points
|
413
|
-
|
414
|
-
when /^Hardware:.*Software: Windows 2000 Version 5\.1/
|
415
|
-
wname['Microsoft Windows XP'] = wname['Microsoft Windows XP'].to_i + points
|
416
|
-
wtype['server'] = wtype['server'].to_i + points
|
417
|
-
|
418
|
-
when /^Hardware:.*Software: Windows Version 5\.2/
|
419
|
-
wname['Microsoft Windows 2003'] = wname['Microsoft Windows 2003'].to_i + points
|
420
|
-
wtype['server'] = wtype['server'].to_i + points
|
421
|
-
|
422
|
-
# XXX: TODO 2008, Vista, Windows 7
|
423
|
-
|
424
|
-
when /^Microsoft Windows CE Version ([^\s]+)+/
|
425
|
-
wname['Microsoft Windows CE ' + $1] = wname['Microsoft Windows CE ' + $1].to_i + points
|
426
|
-
wtype['client'] = wtype['client'].to_i + points
|
427
|
-
|
428
|
-
when /^IPSO ([^\s]+) ([^\s]+) /
|
429
|
-
whost[$1] = whost[$1].to_i + points
|
430
|
-
wname['Nokia IPSO ' + $2] = wname['Nokia IPSO ' + $2].to_i + points
|
431
|
-
wvers[$2] = wvers[$2].to_i + points
|
432
|
-
arch = get_arch_from_string(s.info)
|
433
|
-
warch[arch] = warch[arch].to_s + points if arch
|
434
|
-
wtype['device'] = wtype['device'].to_i + points
|
435
|
-
|
436
|
-
when /^Sun StorEdge/
|
437
|
-
wname['Sun StorEdge'] = wname['Sun StorEdge'].to_i + points
|
438
|
-
wtype['device'] = wtype['device'].to_i + points
|
439
|
-
|
440
|
-
when /^HP StorageWorks/
|
441
|
-
wname['HP StorageWorks'] = wname['HP StorageWorks'].to_i + points
|
442
|
-
wtype['device'] = wtype['device'].to_i + points
|
443
|
-
|
444
|
-
when /^Network Storage/
|
445
|
-
# XXX
|
446
|
-
wname['Network Storage Router'] = wname['Network Storage Router'].to_i + points
|
447
|
-
wtype['device'] = wtype['device'].to_i + points
|
448
|
-
|
449
|
-
when /Cisco Internetwork Operating System.*Version ([^\s]+)/
|
450
|
-
vers = $1.split(/[,^\s]/)[0]
|
451
|
-
wname['Cisco IOS ' + vers] = wname['Cisco IOS ' + vers].to_i + points
|
452
|
-
wvers[vers] = wvers[vers].to_i + points
|
453
|
-
wtype['device'] = wtype['device'].to_i + points
|
454
|
-
|
455
|
-
when /Cisco Catalyst.*Version ([^\s]+)/
|
456
|
-
vers = $1.split(/[,^\s]/)[0]
|
457
|
-
wname['Cisco CatOS ' + vers] = wname['Cisco CatOS ' + vers].to_i + points
|
458
|
-
wvers[vers] = wvers[vers].to_i + points
|
459
|
-
wtype['device'] = wtype['device'].to_i + points
|
460
|
-
|
461
|
-
when /Cisco 761.*Version ([^\s]+)/
|
462
|
-
vers = $1.split(/[,^\s]/)[0]
|
463
|
-
wname['Cisco 761 ' + vers] = wname['Cisco 761 ' + vers].to_i + points
|
464
|
-
wvers[vers] = wvers[vers].to_i + points
|
465
|
-
wtype['device'] = wtype['device'].to_i + points
|
466
|
-
|
467
|
-
when /Network Analysis Module.*Version ([^\s]+)/
|
468
|
-
vers = $1.split(/[,^\s]/)[0]
|
469
|
-
wname['Cisco NAM ' + vers] = wname['Cisco NAM ' + vers].to_i + points
|
470
|
-
wvers[vers] = wvers[vers].to_i + points
|
471
|
-
wtype['device'] = wtype['device'].to_i + points
|
472
|
-
|
473
|
-
when /VPN 3000 Concentrator Series Version ([^\s]+)/
|
474
|
-
vers = $1.split(/[,^\s]/)[0]
|
475
|
-
wname['Cisco VPN 3000 ' + vers] = wname['Cisco VPN 3000 ' + vers].to_i + points
|
476
|
-
wvers[vers] = wvers[vers].to_i + points
|
477
|
-
wtype['device'] = wtype['device'].to_i + points
|
478
|
-
|
479
|
-
when /ProCurve.*Switch/
|
480
|
-
wname['3Com ProCurve Switch'] = wname['3Com ProCurve Switch'].to_i + points
|
481
|
-
wtype['device'] = wtype['device'].to_i + points
|
482
|
-
|
483
|
-
when /ProCurve.*Access Point/
|
484
|
-
wname['3Com Access Point'] = wname['3Com Access Point'].to_i + points
|
485
|
-
wtype['device'] = wtype['device'].to_i + points
|
486
|
-
|
487
|
-
when /3Com.*Access Point/i
|
488
|
-
wname['3Com Access Point'] = wname['3Com Access Point'].to_i + points
|
489
|
-
wtype['device'] = wtype['device'].to_i + points
|
490
|
-
|
491
|
-
when /ShoreGear/
|
492
|
-
wname['ShoreTel Appliance'] = wname['ShoreTel Appliance'].to_i + points
|
493
|
-
wtype['device'] = wtype['device'].to_i + points
|
494
|
-
|
495
|
-
when /firewall/i
|
496
|
-
wname['Unknown Firewall'] = wname['Unknown Firewall'].to_i + points
|
497
|
-
wtype['device'] = wtype['device'].to_i + points
|
498
|
-
|
499
|
-
when /phone/i
|
500
|
-
wname['Unknown Phone'] = wname['Unknown Phone'].to_i + points
|
501
|
-
wtype['device'] = wtype['device'].to_i + points
|
502
|
-
|
503
|
-
when /router/i
|
504
|
-
wname['Unknown Router'] = wname['Unknown Router'].to_i + points
|
505
|
-
wtype['device'] = wtype['device'].to_i + points
|
506
|
-
|
507
|
-
when /switch/i
|
508
|
-
wname['Unknown Switch'] = wname['Unknown Switch'].to_i + points
|
509
|
-
wtype['device'] = wtype['device'].to_i + points
|
510
|
-
#
|
511
|
-
# Printer Signatures
|
512
|
-
#
|
513
|
-
when /^HP ETHERNET MULTI-ENVIRONMENT/
|
514
|
-
wname['HP Printer'] = wname['HP Printer'].to_i + points
|
515
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
516
|
-
when /Canon/i
|
517
|
-
wname['Canon Printer'] = wname['Canon Printer'].to_i + points
|
518
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
519
|
-
when /Epson/i
|
520
|
-
wname['Epson Printer'] = wname['Epson Printer'].to_i + points
|
521
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
522
|
-
when /ExtendNet/i
|
523
|
-
wname['ExtendNet Printer'] = wname['ExtendNet Printer'].to_i + points
|
524
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
525
|
-
when /Fiery/i
|
526
|
-
wname['Fiery Printer'] = wname['Fiery Printer'].to_i + points
|
527
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
528
|
-
when /Konica/i
|
529
|
-
wname['Konica Printer'] = wname['Konica Printer'].to_i + points
|
530
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
531
|
-
when /Lanier/i
|
532
|
-
wname['Lanier Printer'] = wname['Lanier Printer'].to_i + points
|
533
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
534
|
-
when /Lantronix/i
|
535
|
-
wname['Lantronix Printer'] = wname['Lantronix Printer'].to_i + points
|
536
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
537
|
-
when /Lexmark/i
|
538
|
-
wname['Lexmark Printer'] = wname['Lexmark Printer'].to_i + points
|
539
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
540
|
-
when /Magicolor/i
|
541
|
-
wname['Magicolor Printer'] = wname['Magicolor Printer'].to_i + points
|
542
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
543
|
-
when /Minolta/i
|
544
|
-
wname['Minolta Printer'] = wname['Minolta Printer'].to_i + points
|
545
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
546
|
-
when /NetJET/i
|
547
|
-
wname['NetJET Printer'] = wname['NetJET Printer'].to_i + points
|
548
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
549
|
-
when /OKILAN/i
|
550
|
-
wname['OKILAN Printer'] = wname['OKILAN Printer'].to_i + points
|
551
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
552
|
-
when /Phaser/i
|
553
|
-
wname['Phaser Printer'] = wname['Phaser Printer'].to_i + points
|
554
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
555
|
-
when /PocketPro/i
|
556
|
-
wname['PocketPro Printer'] = wname['PocketPro Printer'].to_i + points
|
557
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
558
|
-
when /Ricoh/i
|
559
|
-
wname['Ricoh Printer'] = wname['Ricoh Printer'].to_i + points
|
560
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
561
|
-
when /Savin/i
|
562
|
-
wname['Savin Printer'] = wname['Savin Printer'].to_i + points
|
563
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
564
|
-
when /SHARP AR/i
|
565
|
-
wname['SHARP Printer'] = wname['SHARP Printer'].to_i + points
|
566
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
567
|
-
when /Star Micronix/i
|
568
|
-
wname['Star Micronix Printer'] = wname['Star Micronix Printer'].to_i + points
|
569
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
570
|
-
when /Source Tech/i
|
571
|
-
wname['Source Tech Printer'] = wname['Source Tech Printer'].to_i + points
|
572
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
573
|
-
when /Xerox/i
|
574
|
-
wname['Xerox Printer'] = wname['Xerox Printer'].to_i + points
|
575
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
576
|
-
when /^Brother/i
|
577
|
-
wname['Brother Printer'] = wname['Brother Printer'].to_i + points
|
578
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
579
|
-
when /^Axis.*Network Print/i
|
580
|
-
wname['Axis Printer'] = wname['Axis Printer'].to_i + points
|
581
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
582
|
-
when /^Prestige/i
|
583
|
-
wname['Prestige Printer'] = wname['Prestige Printer'].to_i + points
|
584
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
585
|
-
when /^ZebraNet/i
|
586
|
-
wname['ZebraNet Printer'] = wname['ZebraNet Printer'].to_i + points
|
587
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
588
|
-
when /e\-STUDIO/i
|
589
|
-
wname['eStudio Printer'] = wname['eStudio Printer'].to_i + points
|
590
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
591
|
-
when /^Gestetner/i
|
592
|
-
wname['Gestetner Printer'] = wname['Gestetner Printer'].to_i + points
|
593
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
594
|
-
when /IBM.*Print/i
|
595
|
-
wname['IBM Printer'] = wname['IBM Printer'].to_i + points
|
596
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
597
|
-
when /HP (Color|LaserJet|InkJet)/i
|
598
|
-
wname['HP Printer'] = wname['HP Printer'].to_i + points
|
599
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
600
|
-
when /Dell (Color|Laser|Ink)/i
|
601
|
-
wname['Dell Printer'] = wname['Dell Printer'].to_i + points
|
602
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
603
|
-
when /Print/i
|
604
|
-
wname['Unknown Printer'] = wname['Unknown Printer'].to_i + points
|
605
|
-
wtype['printer'] = wtype['printer'].to_i + points
|
606
|
-
end # End of s.info for SNMP
|
607
|
-
|
608
|
-
when 'telnet'
|
609
|
-
points = 105
|
610
|
-
case s.info
|
611
|
-
when /IRIX/
|
612
|
-
wname['SGI IRIX'] = wname['SGI IRIX'].to_i + points
|
613
|
-
when /AIX/
|
614
|
-
wname['IBM AIX'] = wname['IBM AIX'].to_i + points
|
615
|
-
when /(FreeBSD|OpenBSD|NetBSD)\/(.*) /
|
616
|
-
wname[$1] = wname[$1].to_i + points
|
617
|
-
arch = get_arch_from_string($2)
|
618
|
-
warch[arch] = warch[arch].to_i + points
|
619
|
-
when /Ubuntu (\d+(\.\d+)+)/
|
620
|
-
wname['Linux'] = wname['Linux'].to_i + points
|
621
|
-
wflav['Ubuntu'] = wflav['Ubuntu'].to_i + points
|
622
|
-
wvers[$1] = wvers[$1].to_i + points
|
623
|
-
when /User Access Verification/
|
624
|
-
wname['Cisco IOS'] = wname['Cisco IOS'].to_i + points
|
625
|
-
when /Microsoft/
|
626
|
-
wname['Microsoft Windows'] = wname['Microsoft Windows'].to_i + points
|
627
|
-
end # End of s.info for TELNET
|
628
|
-
wtype['server'] = wtype['server'].to_i + points
|
629
|
-
|
630
|
-
when 'smtp'
|
631
|
-
points = 103
|
632
|
-
case s.info
|
633
|
-
when /ESMTP.*SGI\.8/
|
634
|
-
wname['SGI IRIX'] = wname['SGI IRIX'].to_i + points
|
635
|
-
wtype['server'] = wtype['server'].to_i + points
|
636
|
-
end # End of s.info for SMTP
|
637
|
-
|
638
|
-
when 'https'
|
639
|
-
points = 101
|
640
|
-
case s.info
|
641
|
-
when /(VMware\s(ESXi?)).*\s([\d\.]+)/
|
642
|
-
# Very reliable fingerprinting from our own esx_fingerprint module
|
643
|
-
wname[$1] = wname[$1].to_i + (points * 5)
|
644
|
-
wflav[$3] = wflav[$3].to_i + (points * 5)
|
645
|
-
wtype['device'] = wtype['device'].to_i + points
|
646
|
-
end # End of s.info for HTTPS
|
647
|
-
|
648
|
-
when 'netbios'
|
649
|
-
points = 201
|
650
|
-
case s.info
|
651
|
-
when /W2K3/i
|
652
|
-
wname['Microsoft Windows 2003'] = wname['Microsoft Windows 2003'].to_i + points
|
653
|
-
wtype['server'] = wtype['server'].to_i + points
|
654
|
-
when /W2K8/i
|
655
|
-
wname['Microsoft Windows 2008'] = wname['Microsoft Windows 2008'].to_i + points
|
656
|
-
wtype['server'] = wtype['server'].to_i + points
|
657
|
-
end # End of s.info for NETBIOS
|
658
|
-
|
659
|
-
when 'dns'
|
660
|
-
points = 101
|
661
|
-
case s.info
|
662
|
-
when 'Microsoft DNS'
|
663
|
-
wname['Microsoft Windows'] = wname['Microsoft Windows'].to_i + points
|
664
|
-
wtype['server'] = wtype['server'].to_i + points
|
665
|
-
end # End of s.info for DNS
|
666
|
-
end # End of s.name case
|
667
|
-
# End of Services
|
179
|
+
# Normalize matches for consistency during the ranking phase
|
180
|
+
matches = matches.map{ |m| normalize_match(m) }
|
181
|
+
|
182
|
+
# Calculate the best OS match based on fingerprint hits
|
183
|
+
match = Recog::Nizer.best_os_match(matches)
|
184
|
+
|
185
|
+
# Merge and normalize the best match to the host object
|
186
|
+
apply_match_to_host(match) if match
|
187
|
+
|
188
|
+
# Handle cases where the flavor contains the base name (legacy parsing, etc)
|
189
|
+
# TODO: Remove this once we are sure it is no longer needed
|
190
|
+
if host.os_name && host.os_flavor && host.os_flavor.index(host.os_name)
|
191
|
+
dlog("Host #{host.address} has os_flavor that contains os_name")
|
192
|
+
dlog("os_flavor: #{host.os_flavor}")
|
193
|
+
dlog("os_name: #{host.os_name}")
|
194
|
+
host.os_flavor = host.os_flavor.gsub(host.os_name, '').strip
|
668
195
|
end
|
669
196
|
|
197
|
+
# Set some sane defaults if needed
|
198
|
+
host.os_name ||= 'Unknown'
|
199
|
+
host.purpose ||= 'device'
|
200
|
+
|
201
|
+
host.save if host.changed?
|
202
|
+
end
|
203
|
+
|
204
|
+
def recog_matches_for_service(s)
|
670
205
|
#
|
671
|
-
#
|
206
|
+
# We assume that the service.info field contains certain types of probe
|
207
|
+
# replies and associate these with one or more Recog databases. The mapping
|
208
|
+
# of service.name to a specific database only fits into so many places and
|
209
|
+
# Mdm currently serves that role.
|
672
210
|
#
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
#
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
[
|
696
|
-
|
697
|
-
if
|
698
|
-
|
211
|
+
|
212
|
+
service_match_keys = Hash.new { [] }
|
213
|
+
service_match_keys.merge({
|
214
|
+
# TODO: Implement smb.generic fingerprint database
|
215
|
+
# 'smb' => [ 'smb.generic' ], # Distinct from smb.fingerprint, use os.certainty to choose best match
|
216
|
+
# 'netbios' => [ 'smb.generic' ], # Distinct from smb.fingerprint, use os.certainty to choose best match
|
217
|
+
|
218
|
+
'ssh' => [ 'ssh.banner' ], # Recog expects just the vendor string, not the protocol version
|
219
|
+
'http' => [ 'http_header.server', 'apache_os'], # The 'Apache' fingerprints try to infer OS/distribution from the extra information in the Server header
|
220
|
+
'https' => [ 'http_header.server', 'apache_os'], # XXX: verify vmware esx(i) case on https (TODO: normalize https to http, track SSL elsewhere, such as a new set of fields)
|
221
|
+
'snmp' => [ 'snmp.sys_description' ],
|
222
|
+
'telnet' => [ 'telnet.banner' ],
|
223
|
+
'smtp' => [ 'smtp.banner' ],
|
224
|
+
'imap' => [ 'imap4.banner' ], # Metasploit reports 143/993 as imap (TODO: normalize imap to imap4)
|
225
|
+
'pop3' => [ 'pop3.banner' ], # Metasploit reports 110/995 as pop3
|
226
|
+
'nntp' => [ 'nntp.banner' ],
|
227
|
+
'ftp' => [ 'ftp.banner' ],
|
228
|
+
'ssdp' => [ 'ssdp_header.server' ]
|
229
|
+
})
|
230
|
+
|
231
|
+
matches = []
|
232
|
+
|
233
|
+
service_match_keys[s.name].each do |rdb|
|
234
|
+
banner = s.info
|
235
|
+
if self.respond_to?("service_banner_recog_filter_#{s.name}")
|
236
|
+
banner = self.send("service_banner_recog_filter_#{s.name}", banner)
|
699
237
|
end
|
238
|
+
res = Recog::Nizer.match(rdb, banner)
|
239
|
+
matches << res if res
|
700
240
|
end
|
701
241
|
|
702
|
-
|
242
|
+
matches
|
243
|
+
end
|
244
|
+
|
245
|
+
def recog_matches_for_note(note)
|
246
|
+
# Skip notes that are missing the correct structure or have been blacklisted
|
247
|
+
return [] if not validate_fingerprint_data(note)
|
248
|
+
|
249
|
+
#
|
250
|
+
# These rules define the relationship between fingerprint note keys
|
251
|
+
# and specific Recog databases for detailed matching. Notes that do
|
252
|
+
# not match a rule are passed to the generic matcher.
|
253
|
+
#
|
254
|
+
fingerprint_note_match_keys = {
|
255
|
+
'smb.fingerprint' => {
|
256
|
+
:native_os => [ 'smb.native_os' ],
|
257
|
+
},
|
258
|
+
'http.fingerprint' => {
|
259
|
+
:header_server => [ 'http_header.server', 'apache_os' ],
|
260
|
+
:header_set_cookie => [ 'http_header.cookie' ],
|
261
|
+
:header_www_authenticate => [ 'http_header.wwwauth' ],
|
262
|
+
# TODO: Candidates for future Recog support
|
263
|
+
# :content => 'http_body'
|
264
|
+
# :code => 'http_response_code'
|
265
|
+
# :message => 'http_response_message'
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
matches = []
|
270
|
+
|
271
|
+
# Look for a specific Recog database for this type and data key
|
272
|
+
if fingerprint_note_match_keys.has_key?( note.ntype )
|
273
|
+
fingerprint_note_match_keys[ note.ntype ].each_pair do |k,rdbs|
|
274
|
+
if note.data.has_key?(k)
|
275
|
+
rdbs.each do |rdb|
|
276
|
+
res = Recog::Nizer.match(rdb, note.data[k])
|
277
|
+
matches << res if res
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
else
|
282
|
+
# Add all generic match results to the overall match array
|
283
|
+
normalize_scanner_fp(note).each do |m|
|
284
|
+
next unless m
|
285
|
+
matches << m
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
matches
|
703
290
|
end
|
704
291
|
|
705
292
|
# Determine if the fingerprint data is readable. If not, it nearly always
|
@@ -720,198 +307,514 @@ module Mdm::Host::OperatingSystemNormalization
|
|
720
307
|
end
|
721
308
|
end
|
722
309
|
|
723
|
-
|
310
|
+
#
|
311
|
+
# Normalize matches in order to handle inconsistencies between fingerprint
|
312
|
+
# sources and our desired usage in Metasploit. This amounts to yet more
|
313
|
+
# duct tape, but the situation should improve as the fingerprint sources
|
314
|
+
# are updated and enhanced. In the future, this method will no longer
|
315
|
+
# be needed (or at least, doing less and less work)
|
316
|
+
#
|
317
|
+
def normalize_match(m)
|
318
|
+
# Normalize os.version strings containing 'Service Pack X' to just 'SPX'
|
319
|
+
if m['os.version'] and m['os.version'].index('Service Pack ') == 0
|
320
|
+
m['os.version'] = m['os.version'].gsub(/Service Pack /, 'SP')
|
321
|
+
end
|
322
|
+
|
323
|
+
if m['os.product']
|
324
|
+
|
325
|
+
# Normalize Apple Mac OS X to just Mac OS X
|
326
|
+
if m['os.product'] =~ /^Apple Mac/
|
327
|
+
m['os.product'] = m['os.product'].gsub(/Apple Mac/, 'Mac')
|
328
|
+
m['os.vendor'] ||= 'Apple'
|
329
|
+
end
|
330
|
+
|
331
|
+
# Normalize Sun Solaris/Sun SunOS to just Solaris/SunOS
|
332
|
+
if m['os.product'] =~ /^Sun (Solaris|SunOS)/
|
333
|
+
m['os.product'] = m['os.product'].gsub(/^Sun /, '')
|
334
|
+
m['os.vendor'] ||= 'Oracle'
|
335
|
+
end
|
336
|
+
|
337
|
+
# Normalize Microsoft Windows to just Windows to catch any stragglers
|
338
|
+
if m['os.product'] =~ /^Microsoft Windows/
|
339
|
+
m['os.product'] = m['os.product'].gsub(/Microsoft Windows/, 'Windows')
|
340
|
+
m['os.vendor'] ||= 'Microsoft'
|
341
|
+
end
|
342
|
+
|
343
|
+
# Normalize Windows Server to just Windows to match Metasploit target names
|
344
|
+
if m['os.product'] =~ /^Windows Server/
|
345
|
+
m['os.product'] = m['os.product'].gsub(/Windows Server/, 'Windows')
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
m
|
350
|
+
end
|
724
351
|
|
725
352
|
#
|
726
|
-
#
|
353
|
+
# Recog assumes that the protocol version of the SSH banner has been removed
|
727
354
|
#
|
728
|
-
|
729
|
-
|
730
|
-
|
355
|
+
def service_banner_recog_filter_ssh(banner)
|
356
|
+
if banner =~ /^SSH-\d+\.\d+-(.*)/
|
357
|
+
$1
|
358
|
+
else
|
359
|
+
banner
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
731
363
|
#
|
732
|
-
#
|
733
|
-
#
|
734
|
-
#
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
when "Windows"
|
787
|
-
ret[:os_name] = "Microsoft Windows"
|
788
|
-
if data[:product]
|
789
|
-
if data[:product][/2008/] && data[:version].to_i == 7
|
790
|
-
ret[:os_flavor] = "Windows 7"
|
791
|
-
ret[:type] = "client"
|
792
|
-
else
|
793
|
-
ret[:os_flavor] = data[:product].gsub("Windows", '').strip
|
794
|
-
ret[:os_sp] = data[:version] if data[:version]
|
795
|
-
if data[:product]
|
796
|
-
ret[:type] = "server" if data[:product][/Server/]
|
797
|
-
ret[:type] = "client" if data[:product][/^(XP|ME)$/]
|
798
|
-
end
|
799
|
-
end
|
800
|
-
end
|
801
|
-
when "embedded"
|
802
|
-
ret[:os_name] = data[:vendor]
|
803
|
-
else
|
804
|
-
ret[:os_name] = data[:vendor]
|
805
|
-
end
|
806
|
-
ret[:arch] = get_arch_from_string(data[:arch]) if data[:arch]
|
807
|
-
ret[:arch] ||= get_arch_from_string(data[:desc]) if data[:desc]
|
808
|
-
|
809
|
-
when 'host.os.retina_fingerprint'
|
810
|
-
# :os=>"Windows Server 2003 (X64), Service Pack 2"
|
811
|
-
case data[:os]
|
812
|
-
when /Windows/
|
813
|
-
ret.update(parse_windows_os_str(data[:os]))
|
814
|
-
else
|
815
|
-
# No idea what this looks like if it isn't windows. Just store
|
816
|
-
# the whole thing and hope for the best. XXX: Ghetto. =/
|
817
|
-
ret[:os_name] = data[:os]
|
364
|
+
# Examine the assertations of the merged best match and map these
|
365
|
+
# back to fields of {Mdm::Host}. Take particular care not to leave
|
366
|
+
# related fields (os_*) in a conflicting state, leverage existing
|
367
|
+
# values where possible, and use the most confident values we have.
|
368
|
+
#
|
369
|
+
def apply_match_to_host(match)
|
370
|
+
host = self
|
371
|
+
|
372
|
+
# These values in a match always override the current value unless
|
373
|
+
# the host attribute has been explicitly locked by the user
|
374
|
+
|
375
|
+
if match.has_key?('host.mac') and ! host.attribute_locked?(:mac)
|
376
|
+
host.mac = sanitize(match['host.mac'])
|
377
|
+
end
|
378
|
+
|
379
|
+
if match.has_key?('host.name') and ! host.attribute_locked?(:name)
|
380
|
+
host.name = sanitize(match['host.name'])
|
381
|
+
end
|
382
|
+
|
383
|
+
# Select the os architecture if available
|
384
|
+
if match.has_key?('os.arch') and ! host.attribute_locked?(:arch)
|
385
|
+
host.arch = sanitize(match['os.arch'])
|
386
|
+
end
|
387
|
+
|
388
|
+
# Guess the purpose using some basic heuristics
|
389
|
+
if ! host.attribute_locked?(:purpose)
|
390
|
+
host.purpose = guess_purpose_from_match(match)
|
391
|
+
end
|
392
|
+
|
393
|
+
#
|
394
|
+
# Map match fields from Recog fingerprint style to Metasploit style
|
395
|
+
#
|
396
|
+
|
397
|
+
# os.build: Examples: 9001, 2600, 7602
|
398
|
+
# os.device: Examples: General, ADSL Modem, Broadband router, Cable Modem, Camera, Copier, CSU/DSU
|
399
|
+
# os.edition: Examples: Web, Storage, HPC, MultiPoint, Enterprise, Home, Starter, Professional
|
400
|
+
# os.family: Examples: Windows, Linux, Solaris, NetWare, ProCurve, Mac OS X, HP-UX, AIX
|
401
|
+
# os.product: Examples: Windows, Linux, Windows Server 2008 R2, Windows XP, Enterprise Linux, NEO Tape Library
|
402
|
+
# os.vendor: Examples: Microsoft, HP, IBM, Sun, 3Com, Ricoh, Novell, Ubuntu, Apple, Cisco, Xerox
|
403
|
+
# os.version: Examples: SP1, SP2, 6.5 SP3 CPR, 10.04, 8.04, 12.10, 4.0, 6.1, 8.5
|
404
|
+
# os.language: Examples: English, Arabic, German
|
405
|
+
# linux.kernel.version: Examples: 2.6.32
|
406
|
+
|
407
|
+
# Metasploit currently ignores os.build, os.device, and os.vendor as separate fields.
|
408
|
+
|
409
|
+
# Select the OS name from os.name, fall back to os.family
|
410
|
+
if ! host.attribute_locked?(:os_name)
|
411
|
+
# Try to fill this value from os.product first if it exists
|
412
|
+
if match.has_key?('os.product')
|
413
|
+
host.os_name = sanitize(match['os.product'])
|
414
|
+
else
|
415
|
+
# Fall back to os.family otherwise, if available
|
416
|
+
if match.has_key?('os.family')
|
417
|
+
host.os_name = sanitize(match['os.family'])
|
818
418
|
end
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
# Select the flavor from os.edition if available
|
423
|
+
if match.has_key?('os.edition') and ! host.attribute_locked?(:os_flavor)
|
424
|
+
host.os_flavor = sanitize(match['os.edition'])
|
425
|
+
end
|
426
|
+
|
427
|
+
# Select an OS version as os.version, fall back to linux.kernel.version
|
428
|
+
if ! host.attribute_locked?(:os_sp)
|
429
|
+
if match['os.version']
|
430
|
+
host.os_sp = sanitize(match['os.version'])
|
431
|
+
else
|
432
|
+
if match['linux.kernel.version']
|
433
|
+
host.os_sp = sanitize(match['linux.kernel.version'])
|
834
434
|
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
# Select the os language if available
|
439
|
+
if match.has_key?('os.language') and ! host.attribute_locked?(:os_lang)
|
440
|
+
host.os_lang = sanitize(match['os.language'])
|
441
|
+
end
|
442
|
+
|
443
|
+
# Normalize MAC addresses to lower-case colon-delimited format
|
444
|
+
if host.mac and ! host.attribute_locked?(:mac)
|
445
|
+
host.mac = host.mac.downcase
|
446
|
+
if host.mac =~ /^[a-f0-9]{12}$/
|
447
|
+
host.mac = host.mac.scan(/../).join(':')
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
end
|
452
|
+
|
453
|
+
#
|
454
|
+
# Loosely guess the purpose of a device based on available
|
455
|
+
# match values. In the future, also take into account the
|
456
|
+
# exposed services and rename to guess_purpose_with_match()
|
457
|
+
#
|
458
|
+
def guess_purpose_from_match(match)
|
459
|
+
# Create a string based on all match values
|
460
|
+
pstr = match.values.join(' ').downcase
|
461
|
+
|
462
|
+
# Loosely map keywords to specific purposes
|
463
|
+
case pstr
|
464
|
+
when /windows server|windows (nt|20)/
|
465
|
+
'server'
|
466
|
+
when /windows (xp|vista|[78])/
|
467
|
+
'client'
|
468
|
+
when /printer|print server/
|
469
|
+
'printer'
|
470
|
+
when /router/
|
471
|
+
'router'
|
472
|
+
when /firewall/
|
473
|
+
'firewall'
|
474
|
+
when /linux/
|
475
|
+
'server'
|
476
|
+
else
|
477
|
+
'device'
|
478
|
+
end
|
479
|
+
end
|
835
480
|
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
481
|
+
# Ensure that the host attribute is using ascii safe text
|
482
|
+
# and escapes any other byte value.
|
483
|
+
def sanitize(text)
|
484
|
+
Rex::Text.ascii_safe_hex(text)
|
485
|
+
end
|
486
|
+
|
487
|
+
#
|
488
|
+
# Normalize data from Meterpreter's client.sys.config.sysinfo()
|
489
|
+
#
|
490
|
+
def normalize_session_fingerprint(data)
|
491
|
+
ret = {}
|
492
|
+
case data[:os]
|
493
|
+
when /Windows/
|
494
|
+
ret.update(parse_windows_os_str(data[:os]))
|
495
|
+
# Switch to this code block once the multi-meterpreter code review is complete
|
496
|
+
=begin
|
497
|
+
|
498
|
+
when /^(Windows \w+)\s*\(Build (\d+)(.*)\)/
|
499
|
+
ret['os.product'] = $1
|
500
|
+
ret['os.build'] = $2
|
501
|
+
ret['os.vendor'] = 'Microsoft'
|
502
|
+
possible_sp = $3
|
503
|
+
if possible_sp =~ /Service Pack (\d+)/
|
504
|
+
ret['os.version'] = 'SP' + $1
|
857
505
|
end
|
506
|
+
=end
|
507
|
+
when /Linux (\d+\.\d+\.\d+\S*)\s* \((\w*)\)/
|
508
|
+
ret['os.product'] = "Linux"
|
509
|
+
ret['os.version'] = $1
|
510
|
+
ret['os.arch'] = get_arch_from_string($2)
|
511
|
+
else
|
512
|
+
ret['os.product'] = data[:os]
|
513
|
+
end
|
514
|
+
ret['os.arch'] = data[:arch] if data[:arch]
|
515
|
+
ret['host.name'] = data[:name] if data[:name]
|
516
|
+
[ ret ]
|
517
|
+
end
|
518
|
+
|
519
|
+
#
|
520
|
+
# Normalize data from Nmap fingerprints
|
521
|
+
#
|
522
|
+
def normalize_nmap_fingerprint(data)
|
523
|
+
ret = {}
|
524
|
+
|
525
|
+
# :os_vendor=>"Microsoft" :os_family=>"Windows" :os_version=>"2000" :os_accuracy=>"94"
|
526
|
+
ret['os.certainty'] = ( data[:os_accuracy].to_f / 100.0 ).to_s if data[:os_accuracy]
|
527
|
+
if (data[:os_vendor] == data[:os_family])
|
528
|
+
ret['os.product'] = data[:os_family]
|
529
|
+
else
|
530
|
+
ret['os.product'] = data[:os_family]
|
531
|
+
ret['os.vendor'] = data[:os_vendor]
|
532
|
+
end
|
533
|
+
|
534
|
+
# Nmap places the type of Windows (XP, 7, etc) into the version field
|
535
|
+
if ret['os.product'] == 'Windows' and data[:os_version]
|
536
|
+
ret['os.product'] = ret['os.product'] + ' ' + data[:os_version].to_s
|
537
|
+
else
|
538
|
+
ret['os.version'] = data[:os_version]
|
539
|
+
end
|
540
|
+
|
541
|
+
ret['host.name'] = data[:hostname] if data[:hostname]
|
542
|
+
|
543
|
+
if ret['os.certainty']
|
544
|
+
ret['os.certainty'] = [ ret['os.certainty'].to_f, MAX_NMAP_CERTAINTY ].min.to_s
|
545
|
+
end
|
546
|
+
|
547
|
+
[ ret ]
|
548
|
+
end
|
549
|
+
|
550
|
+
#
|
551
|
+
# Normalize data from MBSA fingerprints
|
552
|
+
#
|
553
|
+
def normalize_mbsa_fingerprint(data)
|
554
|
+
ret = {}
|
555
|
+
# :os_match=>"Microsoft Windows Vista SP0 or SP1, Server 2008, or Windows 7 Ultimate (build 7000)"
|
556
|
+
# :os_vendor=>"Microsoft" :os_family=>"Windows" :os_version=>"7" :os_accuracy=>"100"
|
557
|
+
ret['os.certainty'] = ( data[:os_accuracy].to_f / 100.0 ).to_s if data[:os_accuracy]
|
558
|
+
ret['os.family'] = data[:os_family] if data[:os_family]
|
559
|
+
ret['os.vendor'] = data[:os_vendor] if data[:os_vendor]
|
560
|
+
|
561
|
+
if data[:os_family] and data[:os_version]
|
562
|
+
ret['os.product'] = data[:os_family] + " " + data[:os_version]
|
563
|
+
end
|
564
|
+
|
565
|
+
ret['host.name'] = data[:hostname] if data[:hostname]
|
566
|
+
|
567
|
+
[ ret ]
|
568
|
+
end
|
569
|
+
|
570
|
+
|
571
|
+
#
|
572
|
+
# Normalize data from Nexpose fingerprints
|
573
|
+
#
|
574
|
+
def normalize_nexpose_fingerprint(data)
|
575
|
+
ret = {}
|
576
|
+
# :family=>"Windows" :certainty=>"0.85" :vendor=>"Microsoft" :product=>"Windows 7 Ultimate Edition"
|
577
|
+
# :family=>"Windows" :certainty=>"0.67" :vendor=>"Microsoft" :arch=>"x86" :product=>'Windows 7' :version=>'SP1'
|
578
|
+
# :family=>"Linux" :certainty=>"0.64" :vendor=>"Linux" :product=>"Linux"
|
579
|
+
# :family=>"Linux" :certainty=>"0.80" :vendor=>"Ubuntu" :product=>"Linux"
|
580
|
+
# :family=>"IOS" :certainty=>"0.80" :vendor=>"Cisco" :product=>"IOS"
|
581
|
+
# :family=>"embedded" :certainty=>"0.61" :vendor=>"Linksys" :product=>"embedded"
|
582
|
+
|
583
|
+
ret['os.certainty'] = data[:certainty] if data[:certainty]
|
584
|
+
ret['os.family'] = data[:family] if data[:family]
|
585
|
+
ret['os.vendor'] = data[:vendor] if data[:vendor]
|
586
|
+
|
587
|
+
case data[:product]
|
588
|
+
when /^Windows/
|
589
|
+
|
590
|
+
# TODO: Verify Windows CE and Windows 8 RT fingerprints
|
591
|
+
# Translate the version into the representation we want
|
592
|
+
|
593
|
+
case data[:version].to_s
|
594
|
+
|
595
|
+
# These variants are normalized to just 'Windows <Version>'
|
596
|
+
when "NT", "2000", "95", "ME", "XP", "Vista", "7", "8", "8.1"
|
597
|
+
ret['os.product'] = "Windows #{data[:version]}"
|
598
|
+
|
599
|
+
# Service pack in the version field should be recognized
|
600
|
+
when /^SP\d+/, /^Service Pack \d+/
|
601
|
+
ret['os.product'] = data[:product]
|
602
|
+
ret['os.version'] = data[:version]
|
603
|
+
|
604
|
+
# No version means the version is part of the product already
|
605
|
+
when nil, ''
|
606
|
+
# Trim any 'Server' suffix and use as it is
|
607
|
+
ret['os.product'] = data[:product].sub(/ Server$/, '')
|
608
|
+
|
609
|
+
# Otherwise, we assume a Server version of Windows
|
610
|
+
else
|
611
|
+
ret['os.product'] = "Windows Server #{data[:version]}"
|
612
|
+
end
|
613
|
+
|
614
|
+
# Extract the edition string if it is present
|
615
|
+
if data[:product] =~ /(XP|Vista|\d+(?:\.\d+)) (\w+|\w+ \w+|\w+ \w+ \w+) Edition/
|
616
|
+
ret['os.edition'] = $2
|
617
|
+
end
|
618
|
+
|
619
|
+
when nil, 'embedded'
|
620
|
+
# Use the family or vendor name when the product is empty or 'embedded'
|
621
|
+
ret['os.product'] = data[:family] unless data[:family] == 'embedded'
|
622
|
+
ret['os.product'] ||= data[:vendor]
|
623
|
+
ret['os.version'] = data[:version] if data[:version]
|
624
|
+
else
|
625
|
+
# Default to using the product name reported by Nexpose
|
626
|
+
ret['os.product'] = data[:product] if data[:product]
|
627
|
+
end
|
628
|
+
|
629
|
+
ret['os.arch'] = get_arch_from_string(data[:arch]) if data[:arch]
|
630
|
+
ret['os.arch'] ||= get_arch_from_string(data[:desc]) if data[:desc]
|
631
|
+
|
632
|
+
[ ret ]
|
633
|
+
end
|
634
|
+
|
635
|
+
|
636
|
+
#
|
637
|
+
# Normalize data from Retina fingerprints
|
638
|
+
#
|
639
|
+
def normalize_retina_fingerprint(data)
|
640
|
+
ret = {}
|
641
|
+
# :os=>"Windows Server 2003 (X64), Service Pack 2"
|
642
|
+
case data[:os]
|
643
|
+
when /Windows/
|
644
|
+
ret.update(parse_windows_os_str(data[:os]))
|
645
|
+
else
|
646
|
+
# No idea what this looks like if it isn't windows. Just store
|
647
|
+
# the whole thing and hope for the best.
|
648
|
+
# TODO: Add examples of non-Windows results
|
649
|
+
ret['os.product'] = data[:os] if data[:os]
|
650
|
+
end
|
651
|
+
[ ret ]
|
652
|
+
end
|
653
|
+
|
654
|
+
|
655
|
+
#
|
656
|
+
# Normalize data from Nessus fingerprints
|
657
|
+
#
|
658
|
+
def normalize_nessus_fingerprint(data)
|
659
|
+
ret = {}
|
660
|
+
# :os=>"Microsoft Windows 2000 Advanced Server (English)"
|
661
|
+
# :os=>"Microsoft Windows 2000\nMicrosoft Windows XP"
|
662
|
+
# :os=>"Linux Kernel 2.6"
|
663
|
+
# :os=>"Sun Solaris 8"
|
664
|
+
# :os=>"IRIX 6.5"
|
665
|
+
|
666
|
+
# Nessus sometimes jams multiple OS names together with a newline.
|
667
|
+
oses = data[:os].split(/\n/)
|
668
|
+
if oses.length > 1
|
669
|
+
# Multiple fingerprints means Nessus wasn't really sure, reduce
|
670
|
+
# the certainty accordingly
|
671
|
+
ret['os.certainty'] = 0.5
|
672
|
+
else
|
673
|
+
ret['os.certainty'] = 0.8
|
674
|
+
end
|
858
675
|
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
676
|
+
# Since there is no confidence associated with them, the best we
|
677
|
+
# can do is just take the first one.
|
678
|
+
case oses.first
|
679
|
+
when /^(Microsoft |)Windows/
|
680
|
+
ret.update(parse_windows_os_str(data[:os]))
|
681
|
+
|
682
|
+
when /(2\.[46]\.\d+[-a-zA-Z0-9]+)/
|
683
|
+
# Look for older Linux kernel versions
|
684
|
+
ret['os.product'] = "Linux"
|
685
|
+
ret['os.version'] = $1
|
686
|
+
|
687
|
+
when /^Linux Kernel ([\d\.]+)(.*)/
|
688
|
+
# Look for strings like "Linux Kernel 2.6 on Ubuntu 9.10 (karmic)"
|
689
|
+
# Ex: Linux Kernel 2.2 on Red Hat Linux release 6.2 (Zoot)
|
690
|
+
# Ex: Linux Kernel 2.6 on Ubuntu Linux 8.04 (hardy)
|
691
|
+
ret['os.product'] = "Linux"
|
692
|
+
ret['os.version'] = $1
|
693
|
+
|
694
|
+
vendor = $2.to_s
|
695
|
+
|
696
|
+
# Try to snag the vendor name as well
|
697
|
+
if vendor =~ /on (\w+|\w+ \w+|\w+ \w+ \w+) (Linux|\d)/
|
698
|
+
ret['os.vendor'] = $1
|
875
699
|
end
|
876
|
-
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
896
|
-
|
700
|
+
|
701
|
+
when /(.*) ([0-9\.]+)$/
|
702
|
+
# Then we don't necessarily know what the os is, but this fingerprint has
|
703
|
+
# some version information at the end, pull it off, treat the first part
|
704
|
+
# as the OS, and the rest as the version.
|
705
|
+
ret['os.product'] = $1.gsub("Kernel", '').strip
|
706
|
+
ret['os.version'] = $2
|
707
|
+
else
|
708
|
+
# TODO: Return each OS guess as a separate match
|
709
|
+
ret['os.product'] = oses.first
|
710
|
+
end
|
711
|
+
|
712
|
+
ret['host.name'] = data[:hname] if data[:hname]
|
713
|
+
[ ret ]
|
714
|
+
end
|
715
|
+
|
716
|
+
#
|
717
|
+
# Normalize data from Qualys fingerprints
|
718
|
+
#
|
719
|
+
def normalize_qualys_fingerprint(data)
|
720
|
+
ret = {}
|
721
|
+
# :os=>"Microsoft Windows 2000"
|
722
|
+
# :os=>"Windows 2003"
|
723
|
+
# :os=>"Microsoft Windows XP Professional SP3"
|
724
|
+
# :os=>"Ubuntu Linux"
|
725
|
+
# :os=>"Cisco IOS 12.0(3)T3"
|
726
|
+
# :os=>"Red-Hat Linux 6.0"
|
727
|
+
case data[:os]
|
728
|
+
when /Windows/
|
729
|
+
ret.update(parse_windows_os_str(data[:os]))
|
730
|
+
|
731
|
+
when /^(Cisco) (IOS) (\d+[^\s]+)/
|
732
|
+
ret['os.product'] = $2
|
733
|
+
ret['os.vendor'] = $1
|
734
|
+
ret['os.version'] = $3
|
735
|
+
|
736
|
+
when /^([^\s]+) (Linux)(.*)/
|
737
|
+
ret['os.product'] = $2
|
738
|
+
ret['os.vendor'] = $1
|
739
|
+
|
740
|
+
ver = $3.to_s.strip.split(/\s+/).first
|
741
|
+
if ver =~ /^\d+\./
|
742
|
+
ret['os.version'] = ver
|
897
743
|
end
|
898
|
-
|
899
|
-
ret[:name] = data[:name] if data[:name]
|
744
|
+
|
900
745
|
else
|
901
|
-
|
902
|
-
|
903
|
-
ret[
|
904
|
-
ret[
|
905
|
-
ret[
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
746
|
+
parts = data[:os].split(/\s+/, 3)
|
747
|
+
ret['os.product'] = "Unknown"
|
748
|
+
ret['os.product'] = parts[0] if parts[0]
|
749
|
+
ret['os.product'] << " " + parts[1] if parts[1]
|
750
|
+
ret['os.version'] = parts[2] if parts[2]
|
751
|
+
end
|
752
|
+
[ ret ]
|
753
|
+
end
|
754
|
+
|
755
|
+
#
|
756
|
+
# Normalize data from FusionVM fingerprints
|
757
|
+
#
|
758
|
+
def normalize_fusionvm_fingerprint(data)
|
759
|
+
ret = {}
|
760
|
+
case data[:os]
|
761
|
+
when /Windows/
|
762
|
+
ret.update(parse_windows_os_str(data[:os]))
|
763
|
+
when /Linux ([^[:space:]]*) ([^[:space:]]*) .* (\(.*\))/
|
764
|
+
ret['os.product'] = "Linux"
|
765
|
+
ret['host.name'] = $1
|
766
|
+
ret['os.version'] = $2
|
767
|
+
ret['os.arch'] = get_arch_from_string($3)
|
768
|
+
else
|
769
|
+
ret['os.product'] = data[:os]
|
770
|
+
end
|
771
|
+
ret['os.arch'] = data[:arch] if data[:arch]
|
772
|
+
ret['host.name'] = data[:name] if data[:name]
|
773
|
+
[ ret ]
|
774
|
+
end
|
775
|
+
|
776
|
+
#
|
777
|
+
# Normalize data from generic fingerprints
|
778
|
+
#
|
779
|
+
def normalize_generic_fingerprint(data)
|
780
|
+
ret = {}
|
781
|
+
ret['os.product'] = data[:os_name] || data[:os] || data[:os_fingerprint] || "Unknown"
|
782
|
+
ret['os.arch'] = data[:os_arch] if data[:os_arch]
|
783
|
+
ret['os.certainty'] = data[:os_certainty] || 0.5
|
784
|
+
[ ret ]
|
785
|
+
end
|
786
|
+
|
787
|
+
#
|
788
|
+
# Convert a host.os.*_fingerprint Note into a hash containing 'os.*' and 'host.*' fields
|
789
|
+
#
|
790
|
+
# Also includes a os.certainty which is a float from 0 - 1.00 indicating the
|
791
|
+
# scanner's confidence in its fingerprint. If the particular scanner does
|
792
|
+
# not provide such information, default to 0.80.
|
793
|
+
#
|
794
|
+
def normalize_scanner_fp(fp)
|
795
|
+
hits = []
|
796
|
+
|
797
|
+
return hits if not validate_fingerprint_data(fp)
|
798
|
+
|
799
|
+
case fp.ntype
|
800
|
+
when /^host\.os\.(.*_fingerprint)$/
|
801
|
+
pname = $1
|
802
|
+
pmeth = 'normalize_' + pname
|
803
|
+
if self.respond_to?(pmeth)
|
804
|
+
hits = self.send(pmeth, fp.data)
|
805
|
+
else
|
806
|
+
hits = normalize_generic_fingerprint(fp.data)
|
807
|
+
end
|
808
|
+
end
|
809
|
+
hits.each {|hit| hit['os.certainty'] ||= 0.80}
|
810
|
+
hits
|
910
811
|
end
|
911
812
|
|
912
813
|
#
|
913
814
|
# Take a windows version string and return a hash with fields suitable for
|
914
|
-
# Host this object's version fields.
|
815
|
+
# Host this object's version fields. This is used as a fall-back to parse
|
816
|
+
# external fingerprints and should eventually be replaced by per-source
|
817
|
+
# mappings.
|
915
818
|
#
|
916
819
|
# A few example strings that this will have to parse:
|
917
820
|
# sessions
|
@@ -933,52 +836,64 @@ module Mdm::Host::OperatingSystemNormalization
|
|
933
836
|
def parse_windows_os_str(str)
|
934
837
|
ret = {}
|
935
838
|
|
936
|
-
|
937
|
-
|
938
|
-
ret[
|
839
|
+
# Set some reasonable defaults for Windows
|
840
|
+
ret['os.vendor'] = 'Microsoft'
|
841
|
+
ret['os.product'] = 'Windows'
|
939
842
|
|
940
|
-
|
941
|
-
ret[:os_sp] = "SP#{$2}"
|
942
|
-
end
|
943
|
-
|
944
|
-
# Flavor
|
843
|
+
# Determine the actual Windows product name
|
945
844
|
case str
|
946
845
|
when /\.NET Server/
|
947
|
-
ret[
|
948
|
-
when /(
|
949
|
-
ret[
|
846
|
+
ret['os.product'] << ' Server 2003'
|
847
|
+
when / (2000|2003|2008|2012)/
|
848
|
+
ret['os.product'] << ' Server ' + $1
|
849
|
+
when / (NT (?:3\.51|4\.0))/
|
850
|
+
ret['os.product'] << ' ' + $1
|
851
|
+
when /Windows (95|98|ME|XP|Vista|[\d\.]+)/
|
852
|
+
ret['os.product'] << ' ' + $1
|
950
853
|
else
|
951
854
|
# If we couldn't pull out anything specific for the flavor, just cut
|
952
855
|
# off the stuff we know for sure isn't it and hope for the best
|
953
|
-
ret[
|
856
|
+
ret['os.product'] = (ret['os.product'] + ' ' + str.gsub(/(Microsoft )|(Windows )|(Service Pack|SP) ?(\d+)/i, '').strip).strip
|
857
|
+
|
858
|
+
# Make sure the product name doesn't include any non-alphanumeric stuff
|
859
|
+
# This fixes cases where the above code leaves 'Windows XX (Build 3333,)...'
|
860
|
+
ret['os.product'] = ret['os.product'].split(/[^a-zA-Z0-9 ]/).first.strip
|
861
|
+
|
862
|
+
end
|
863
|
+
|
864
|
+
# Take a guess at the architecture
|
865
|
+
arch = get_arch_from_string(str)
|
866
|
+
ret['os.arch'] = arch if arch
|
867
|
+
|
868
|
+
# Extract any service pack value in the string
|
869
|
+
if str =~ /(Service Pack|SP) ?(\d+)/i
|
870
|
+
ret['os.version'] = "SP#{$2}"
|
871
|
+
end
|
872
|
+
|
873
|
+
# Extract any build ID found in the string
|
874
|
+
if str =~ /build (\d+)/i
|
875
|
+
ret['os.build'] = $1
|
954
876
|
end
|
955
877
|
|
956
|
-
|
957
|
-
|
878
|
+
# Extract the OS edition if available
|
879
|
+
if str =~ /(\d+|\d+\.\d+) (\w+|\w+ \w+|\w+ \w+ \w+) Edition/
|
880
|
+
ret['os.edition'] = $2
|
958
881
|
else
|
959
|
-
|
882
|
+
if str =~ /(Professional|Enterprise|Pro|Home|Start|Datacenter|Web|Storage|MultiPoint)/
|
883
|
+
ret['os.edition'] = $1
|
884
|
+
end
|
960
885
|
end
|
961
886
|
|
962
887
|
ret
|
963
888
|
end
|
964
889
|
|
965
|
-
#
|
890
|
+
#
|
891
|
+
# Return a normalized architecture based on patterns in the input string.
|
892
|
+
# This will identify things like sparc, powerpc, x86_x64, and i686
|
893
|
+
#
|
966
894
|
def get_arch_from_string(str)
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
when /x86|i[3456]86/i
|
971
|
-
"x86"
|
972
|
-
when /PowerPC|PPC|POWER|ppc/
|
973
|
-
"ppc"
|
974
|
-
when /SPARC/i
|
975
|
-
"sparc"
|
976
|
-
when /MIPS/i
|
977
|
-
"mips"
|
978
|
-
when /ARM/i
|
979
|
-
"arm"
|
980
|
-
else
|
981
|
-
nil
|
982
|
-
end
|
895
|
+
res = Recog::Nizer.match("architecture", str)
|
896
|
+
return unless (res and res['os.arch'])
|
897
|
+
res['os.arch']
|
983
898
|
end
|
984
|
-
end
|
899
|
+
end
|