recog 0.01
Sign up to get free protection for your applications and to get access to all the features.
- 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
|