recog 0.01
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +3 -0
- data/.rspec +2 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +42 -0
- data/LICENSE +23 -0
- data/README.md +63 -0
- data/bin/recog_export.rb +81 -0
- data/bin/recog_match.rb +51 -0
- data/bin/recog_verify.rb +45 -0
- data/features/match.feature +16 -0
- data/features/support/env.rb +5 -0
- data/features/verify.feature +31 -0
- data/features/xml/banners.xml +2 -0
- data/features/xml/failing_banners_fingerprints.xml +20 -0
- data/features/xml/matching_banners_fingerprints.xml +22 -0
- data/features/xml/no_tests.xml +53 -0
- data/features/xml/successful_tests.xml +33 -0
- data/features/xml/tests_with_failures.xml +10 -0
- data/features/xml/tests_with_warnings.xml +10 -0
- data/lib/recog.rb +3 -0
- data/lib/recog/db.rb +38 -0
- data/lib/recog/db_manager.rb +27 -0
- data/lib/recog/fingerprint.rb +60 -0
- data/lib/recog/formatter.rb +51 -0
- data/lib/recog/match_reporter.rb +77 -0
- data/lib/recog/matcher.rb +60 -0
- data/lib/recog/matcher_factory.rb +14 -0
- data/lib/recog/nizer.rb +263 -0
- data/lib/recog/verifier.rb +46 -0
- data/lib/recog/verifier_factory.rb +13 -0
- data/lib/recog/verify_reporter.rb +85 -0
- data/lib/recog/version.rb +3 -0
- data/recog.gemspec +34 -0
- data/spec/data/best_os_match_1.yml +17 -0
- data/spec/data/best_os_match_2.yml +17 -0
- data/spec/data/best_service_match_1.yml +17 -0
- data/spec/data/smb_native_os.txt +31 -0
- data/spec/data/test_fingerprints.xml +24 -0
- data/spec/lib/db_spec.rb +89 -0
- data/spec/lib/formatter_spec.rb +69 -0
- data/spec/lib/match_reporter_spec.rb +90 -0
- data/spec/lib/nizer_spec.rb +124 -0
- data/spec/lib/verify_reporter_spec.rb +112 -0
- data/xml/apache_os.xml +295 -0
- data/xml/architecture.xml +45 -0
- data/xml/ftp_banners.xml +808 -0
- data/xml/h323_callresp.xml +701 -0
- data/xml/hp_pjl_id.xml +435 -0
- data/xml/http_cookies.xml +379 -0
- data/xml/http_servers.xml +3326 -0
- data/xml/http_wwwauth.xml +412 -0
- data/xml/imap_banners.xml +267 -0
- data/xml/nntp_banners.xml +51 -0
- data/xml/ntp_banners.xml +538 -0
- data/xml/pop_banners.xml +452 -0
- data/xml/rsh_resp.xml +90 -0
- data/xml/sip_banners.xml +14 -0
- data/xml/smb_native_os.xml +385 -0
- data/xml/smtp_banners.xml +1738 -0
- data/xml/smtp_debug.xml +45 -0
- data/xml/smtp_ehlo.xml +53 -0
- data/xml/smtp_expn.xml +95 -0
- data/xml/smtp_help.xml +212 -0
- data/xml/smtp_mailfrom.xml +24 -0
- data/xml/smtp_noop.xml +45 -0
- data/xml/smtp_quit.xml +31 -0
- data/xml/smtp_rcptto.xml +33 -0
- data/xml/smtp_rset.xml +23 -0
- data/xml/smtp_turn.xml +23 -0
- data/xml/smtp_vrfy.xml +109 -0
- data/xml/snmp_sysdescr.xml +8008 -0
- data/xml/snmp_sysobjid.xml +284 -0
- data/xml/ssh_banners.xml +790 -0
- data/xml/upnp_banners.xml +590 -0
- metadata +190 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
module Recog
|
2
|
+
class Verifier
|
3
|
+
attr_reader :fingerprints, :reporter
|
4
|
+
|
5
|
+
def initialize(fingerprints, reporter)
|
6
|
+
@fingerprints = fingerprints
|
7
|
+
@reporter = reporter
|
8
|
+
end
|
9
|
+
|
10
|
+
def verify_tests
|
11
|
+
reporter.report(fingerprints.count) do
|
12
|
+
fingerprints.each do |fp|
|
13
|
+
reporter.print_name fp
|
14
|
+
|
15
|
+
if fp.tests.length == 0
|
16
|
+
warning = "'#{fp.name}' has no test cases"
|
17
|
+
reporter.warning "WARN: #{warning}"
|
18
|
+
end
|
19
|
+
|
20
|
+
fp.tests.each do |test|
|
21
|
+
m = test.match(fp.regex)
|
22
|
+
unless m
|
23
|
+
failure = "'#{fp.name}' failed to match #{test.inspect} with #{fp.regex.to_s}'"
|
24
|
+
reporter.failure("FAIL: #{failure}")
|
25
|
+
else
|
26
|
+
info = { }
|
27
|
+
fp.params.each_pair do |k,v|
|
28
|
+
if v[0] == 0
|
29
|
+
info[k] = v[1]
|
30
|
+
else
|
31
|
+
info[k] = m[ v[0] ]
|
32
|
+
if m[ v[0] ].to_s.empty?
|
33
|
+
warning = "'#{fp.name}' failed to match #{test.inspect} key '#{k}'' with #{fp.regex.to_s}'"
|
34
|
+
reporter.warning "WARN: #{warning}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
reporter.success(test)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'recog/verifier'
|
2
|
+
require 'recog/formatter'
|
3
|
+
require 'recog/verify_reporter'
|
4
|
+
|
5
|
+
module Recog
|
6
|
+
module VerifierFactory
|
7
|
+
def self.build(options)
|
8
|
+
formatter = Formatter.new(options, $stdout)
|
9
|
+
reporter = VerifyReporter.new(options, formatter)
|
10
|
+
Verifier.new(options.fingerprints, reporter)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Recog
|
2
|
+
class VerifyReporter
|
3
|
+
attr_reader :formatter
|
4
|
+
attr_reader :success_count, :warning_count, :failure_count
|
5
|
+
|
6
|
+
def initialize(options, formatter)
|
7
|
+
@options = options
|
8
|
+
@formatter = formatter
|
9
|
+
reset_counts
|
10
|
+
end
|
11
|
+
|
12
|
+
def report(fingerprint_count)
|
13
|
+
reset_counts
|
14
|
+
yield self
|
15
|
+
summarize(fingerprint_count)
|
16
|
+
end
|
17
|
+
|
18
|
+
def success(text)
|
19
|
+
@success_count += 1
|
20
|
+
formatter.success_message("#{padding}#{text}") if detail?
|
21
|
+
end
|
22
|
+
|
23
|
+
def warning(text)
|
24
|
+
@warning_count += 1
|
25
|
+
formatter.warning_message("#{padding}#{text}")
|
26
|
+
end
|
27
|
+
|
28
|
+
def failure(text)
|
29
|
+
@failure_count += 1
|
30
|
+
formatter.failure_message("#{padding}#{text}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def print_name(fingerprint)
|
34
|
+
if detail? && fingerprint.tests.any?
|
35
|
+
name = fingerprint.name.empty? ? '[unnamed]' : fingerprint.name
|
36
|
+
formatter.status_message("\n#{name}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def summarize(fingerprint_count)
|
41
|
+
print_fingerprint_count(fingerprint_count) if detail?
|
42
|
+
print_summary
|
43
|
+
end
|
44
|
+
|
45
|
+
def print_fingerprint_count(count)
|
46
|
+
formatter.status_message("\nVerified #{count} fingerprints:")
|
47
|
+
end
|
48
|
+
|
49
|
+
def print_summary
|
50
|
+
colorize_summary(summary_line)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def reset_counts
|
56
|
+
@success_count = @failure_count = @warning_count = 0
|
57
|
+
end
|
58
|
+
|
59
|
+
def detail?
|
60
|
+
@options.detail
|
61
|
+
end
|
62
|
+
|
63
|
+
def padding
|
64
|
+
' ' if @options.detail
|
65
|
+
end
|
66
|
+
|
67
|
+
def summary_line
|
68
|
+
summary = "SUMMARY: Test completed with "
|
69
|
+
summary << "#{@success_count} successful"
|
70
|
+
summary << ", #{@warning_count} warnings"
|
71
|
+
summary << ", and #{@failure_count} failures"
|
72
|
+
summary
|
73
|
+
end
|
74
|
+
|
75
|
+
def colorize_summary(summary)
|
76
|
+
if @failure_count > 0
|
77
|
+
formatter.failure_message(summary)
|
78
|
+
elsif @warning_count > 0
|
79
|
+
formatter.warning_message(summary)
|
80
|
+
else
|
81
|
+
formatter.success_message(summary)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/recog.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'recog/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'recog'
|
7
|
+
s.version = Recog::VERSION
|
8
|
+
s.authors = [
|
9
|
+
'Rapid7 Research'
|
10
|
+
]
|
11
|
+
s.email = [
|
12
|
+
'research@rapid7.com'
|
13
|
+
]
|
14
|
+
s.homepage = "https://www.github.com/rapid7/recog"
|
15
|
+
s.summary = %q{Network service fingerprint database, classes, and utilities}
|
16
|
+
s.description = %q{
|
17
|
+
Recog is a framework for identifying products, services, operating systems, and hardware by matching
|
18
|
+
fingerprints against data returned from various network probes. Recog makes it simply to extract useful
|
19
|
+
information from web server banners, snmp system description fields, and a whole lot more.
|
20
|
+
}.gsub(/\s+/, ' ').strip
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ['lib']
|
26
|
+
|
27
|
+
# ---- Dependencies ----
|
28
|
+
|
29
|
+
s.add_development_dependency 'rspec'
|
30
|
+
s.add_development_dependency 'cucumber'
|
31
|
+
s.add_development_dependency 'aruba'
|
32
|
+
|
33
|
+
s.add_runtime_dependency 'nokogiri'
|
34
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
---
|
2
|
+
- os.product: Windows 2008
|
3
|
+
os.vendor: Microsoft
|
4
|
+
os.version: Service Pack 2
|
5
|
+
os.certainty: 0.5
|
6
|
+
- os.product: Windows 2008
|
7
|
+
os.vendor: Microsoft
|
8
|
+
os.version: Service Pack 1
|
9
|
+
os.certainty: 0.4
|
10
|
+
- os.product: Windows 2008
|
11
|
+
os.vendor: Microsoft
|
12
|
+
os.certainty: 0.3
|
13
|
+
os.language: English
|
14
|
+
- os.product: Windows 2012
|
15
|
+
os.vendor: Microsoft
|
16
|
+
os.certainty: 0.4
|
17
|
+
os.language: Arabic
|
@@ -0,0 +1,17 @@
|
|
1
|
+
---
|
2
|
+
- os.product: Windows 2008
|
3
|
+
os.vendor: Microsoft
|
4
|
+
os.version: Service Pack 2
|
5
|
+
os.certainty: 1.0
|
6
|
+
- os.product: Windows 2012
|
7
|
+
os.vendor: Microsoft
|
8
|
+
os.version: Service Pack 1
|
9
|
+
os.certainty: 0.7
|
10
|
+
- os.product: Windows 2008
|
11
|
+
os.vendor: Microsoft
|
12
|
+
os.certainty: 0.3
|
13
|
+
os.language: English
|
14
|
+
- os.product: Windows 2012
|
15
|
+
os.vendor: Microsoft
|
16
|
+
os.certainty: 0.8
|
17
|
+
os.language: Arabic
|
@@ -0,0 +1,17 @@
|
|
1
|
+
---
|
2
|
+
- service.product: IIS
|
3
|
+
service.vendor: Microsoft
|
4
|
+
service.version: 6.0
|
5
|
+
service.certainty: 1.0
|
6
|
+
- service.product: Apache
|
7
|
+
service.vendor: Linux
|
8
|
+
service.version: 2.2.4
|
9
|
+
service.certainty: 0.5
|
10
|
+
- service.product: IIS
|
11
|
+
service.vendor: Microsoft
|
12
|
+
service.certainty: 0.5
|
13
|
+
service.language: English
|
14
|
+
- service.product: IIS
|
15
|
+
service.vendor: Microsoft
|
16
|
+
service.certainty: 0.4
|
17
|
+
service.language: Arabic
|
@@ -0,0 +1,31 @@
|
|
1
|
+
Windows Web Server 2008 R2 7601 Service Pack 1
|
2
|
+
Windows Vista (TM) Home Premium 6002 Service Pack 2
|
3
|
+
Windows Server (R) 2008 Standard 6002 Service Pack 2
|
4
|
+
Windows Server (R) 2008 Enterprise without Hyper-V 6001 Service Pack 1
|
5
|
+
Windows Server (R) 2008 Enterprise 6002 Service Pack 2
|
6
|
+
Windows Server (R) 2008 Enterprise 6001 Service Pack 1
|
7
|
+
Windows Server 2012 Standard 9200
|
8
|
+
Windows Server 2012 R2 Standard 9600
|
9
|
+
Windows Server 2008 R2 Standard 7601 Service Pack 1
|
10
|
+
Windows Server 2008 R2 Standard 7600
|
11
|
+
Windows Server 2008 R2 Enterprise 7601 Service Pack 1
|
12
|
+
Windows Server 2008 R2 Enterprise 7600
|
13
|
+
Windows Server 2008 HPC Edition 7600
|
14
|
+
Windows Server 2003 R2 3790 Service Pack 2
|
15
|
+
Windows Server 2003 3790 Service Pack 2
|
16
|
+
Windows (R) Web Server 2008 6002 Service Pack 2
|
17
|
+
Windows MultiPoint Server 2012 Premium 9200
|
18
|
+
Windows 8 Enterprise 9200
|
19
|
+
Windows 8.1 Enterprise 9600
|
20
|
+
Windows 7 Ultimate 7601 Service Pack 1
|
21
|
+
Windows 7 Ultimate 7600
|
22
|
+
Windows 7 Starter 7601 Service Pack 1
|
23
|
+
Windows 7 Home Premium 7600
|
24
|
+
Windows 7 Enterprise 7601 Service Pack 1
|
25
|
+
Windows 7 Enterprise 7600
|
26
|
+
Samba 3.6.9-151.el6_4.1
|
27
|
+
Samba 3.6.6
|
28
|
+
Samba 3.6.3
|
29
|
+
Samba 3.0.32-0.2-2210-SUSE-SL10.3
|
30
|
+
Samba 3.0.28a
|
31
|
+
Samba 3.0.24
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<fingerprints>
|
3
|
+
<fingerprint pattern=".*\(iSeries\).*">
|
4
|
+
</fingerprint>
|
5
|
+
|
6
|
+
<fingerprint pattern=".*\(PalmOS\).*">
|
7
|
+
<description>PalmOS</description>
|
8
|
+
<param pos="1" name="os.vendor" value="Palm"/>
|
9
|
+
<param pos="2" name="os.device" value="General"/>
|
10
|
+
</fingerprint>
|
11
|
+
|
12
|
+
<fingerprint pattern="(designjet \S+)" flags="REG_ICASE">
|
13
|
+
<description>HP Designjet printer</description>
|
14
|
+
<description>I should be ignored</description>
|
15
|
+
<param pos="0" name="service.vendor" value="HP"/>
|
16
|
+
</fingerprint>
|
17
|
+
|
18
|
+
<fingerprint pattern="laserjet (.*)(?: series)?" flags="REG_ICASE">
|
19
|
+
<description>HP JetDirect Printer</description>
|
20
|
+
<example>HP LaserJet 4100 Series</example>
|
21
|
+
<example>HP LaserJet 2200</example>
|
22
|
+
<param pos="0" name="service.vendor" value="HP"/>
|
23
|
+
</fingerprint>
|
24
|
+
</fingerprints>
|
data/spec/lib/db_spec.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative '../../lib/recog/db'
|
2
|
+
|
3
|
+
describe Recog::DB do
|
4
|
+
let(:xml_file) { File.expand_path File.join('spec', 'data', 'test_fingerprints.xml') }
|
5
|
+
subject { Recog::DB.new(xml_file) }
|
6
|
+
|
7
|
+
describe "#fingerprints" do
|
8
|
+
context "with only a pattern" do
|
9
|
+
let(:entry) { subject.fingerprints[0] }
|
10
|
+
|
11
|
+
it "has a blank name with no description" do
|
12
|
+
expect(entry.name).to be_empty
|
13
|
+
end
|
14
|
+
|
15
|
+
it "has a pattern" do
|
16
|
+
expect(entry.regex.source).to eq(".*\\(iSeries\\).*")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "has no params" do
|
20
|
+
expect(entry.params).to be_empty
|
21
|
+
end
|
22
|
+
|
23
|
+
it "has no tests" do
|
24
|
+
expect(entry.tests).to be_empty
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "with params" do
|
29
|
+
let(:entry) { subject.fingerprints[1] }
|
30
|
+
|
31
|
+
it "has a name" do
|
32
|
+
expect(entry.name).to eq('PalmOS')
|
33
|
+
end
|
34
|
+
|
35
|
+
it "has a pattern" do
|
36
|
+
expect(entry.regex.source).to eq(".*\\(PalmOS\\).*")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "has params" do
|
40
|
+
expect(entry.params).to eq({"os.vendor"=>[1, "Palm"], "os.device"=>[2, "General"]})
|
41
|
+
end
|
42
|
+
|
43
|
+
it "has no tests" do
|
44
|
+
expect(entry.tests).to be_empty
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with pattern flags" do
|
49
|
+
let(:entry) { subject.fingerprints[2] }
|
50
|
+
|
51
|
+
it "has a name and only uses the first value" do
|
52
|
+
expect(entry.name).to eq('HP Designjet printer')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "has a pattern" do
|
56
|
+
expect(entry.regex.options).to eq(Regexp::NOENCODING | Regexp::IGNORECASE)
|
57
|
+
expect(entry.regex.source).to eq("(designjet \\S+)")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "has params" do
|
61
|
+
expect(entry.params).to eq({"service.vendor"=>[0, "HP"]})
|
62
|
+
end
|
63
|
+
|
64
|
+
it "has no tests" do
|
65
|
+
expect(entry.tests).to be_empty
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "with test" do
|
70
|
+
let(:entry) { subject.fingerprints[3] }
|
71
|
+
|
72
|
+
it "has a name" do
|
73
|
+
expect(entry.name).to eq('HP JetDirect Printer')
|
74
|
+
end
|
75
|
+
|
76
|
+
it "has a pattern" do
|
77
|
+
expect(entry.regex.source).to eq("laserjet (.*)(?: series)?")
|
78
|
+
end
|
79
|
+
|
80
|
+
it "has params" do
|
81
|
+
expect(entry.params).to eq({"service.vendor"=>[0, "HP"]})
|
82
|
+
end
|
83
|
+
|
84
|
+
it "has no tests" do
|
85
|
+
expect(entry.tests).to match_array(["HP LaserJet 4100 Series", "HP LaserJet 2200"])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative '../../lib/recog/formatter'
|
2
|
+
|
3
|
+
describe Recog::Formatter do
|
4
|
+
let(:output) { StringIO.new }
|
5
|
+
|
6
|
+
context "with no color" do
|
7
|
+
subject { Recog::Formatter.new(double(color: false), output) }
|
8
|
+
|
9
|
+
describe "#message" do
|
10
|
+
it "outputs the text" do
|
11
|
+
subject.status_message 'some text'
|
12
|
+
expect(output.string).to eq("some text\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#success_message" do
|
17
|
+
it "outputs the text" do
|
18
|
+
subject.success_message 'a success'
|
19
|
+
expect(output.string).to eq("a success\n")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#warning_message" do
|
24
|
+
it "outputs the text" do
|
25
|
+
subject.warning_message 'a warning'
|
26
|
+
expect(output.string).to eq("a warning\n")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#failure_message" do
|
31
|
+
it "outputs the text" do
|
32
|
+
subject.failure_message 'a failure'
|
33
|
+
expect(output.string).to eq("a failure\n")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with color" do
|
39
|
+
subject { Recog::Formatter.new(double(color: true), output) }
|
40
|
+
|
41
|
+
describe "#message" do
|
42
|
+
it "outputs the text in white" do
|
43
|
+
subject.status_message 'some text'
|
44
|
+
expect(output.string).to eq("\e[15msome text\e[0m\n")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#success_message" do
|
49
|
+
it "outputs the text in green" do
|
50
|
+
subject.success_message 'a success'
|
51
|
+
expect(output.string).to eq("\e[32ma success\e[0m\n")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#warning_message" do
|
56
|
+
it "outputs the text in yellow" do
|
57
|
+
subject.warning_message 'a warning'
|
58
|
+
expect(output.string).to eq("\e[33ma warning\e[0m\n")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#failure_message" do
|
63
|
+
it "outputs the text in red" do
|
64
|
+
subject.failure_message 'a failure'
|
65
|
+
expect(output.string).to eq("\e[31ma failure\e[0m\n")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|