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.
Files changed (75) hide show
  1. data/.gitignore +3 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +9 -0
  4. data/Gemfile.lock +42 -0
  5. data/LICENSE +23 -0
  6. data/README.md +63 -0
  7. data/bin/recog_export.rb +81 -0
  8. data/bin/recog_match.rb +51 -0
  9. data/bin/recog_verify.rb +45 -0
  10. data/features/match.feature +16 -0
  11. data/features/support/env.rb +5 -0
  12. data/features/verify.feature +31 -0
  13. data/features/xml/banners.xml +2 -0
  14. data/features/xml/failing_banners_fingerprints.xml +20 -0
  15. data/features/xml/matching_banners_fingerprints.xml +22 -0
  16. data/features/xml/no_tests.xml +53 -0
  17. data/features/xml/successful_tests.xml +33 -0
  18. data/features/xml/tests_with_failures.xml +10 -0
  19. data/features/xml/tests_with_warnings.xml +10 -0
  20. data/lib/recog.rb +3 -0
  21. data/lib/recog/db.rb +38 -0
  22. data/lib/recog/db_manager.rb +27 -0
  23. data/lib/recog/fingerprint.rb +60 -0
  24. data/lib/recog/formatter.rb +51 -0
  25. data/lib/recog/match_reporter.rb +77 -0
  26. data/lib/recog/matcher.rb +60 -0
  27. data/lib/recog/matcher_factory.rb +14 -0
  28. data/lib/recog/nizer.rb +263 -0
  29. data/lib/recog/verifier.rb +46 -0
  30. data/lib/recog/verifier_factory.rb +13 -0
  31. data/lib/recog/verify_reporter.rb +85 -0
  32. data/lib/recog/version.rb +3 -0
  33. data/recog.gemspec +34 -0
  34. data/spec/data/best_os_match_1.yml +17 -0
  35. data/spec/data/best_os_match_2.yml +17 -0
  36. data/spec/data/best_service_match_1.yml +17 -0
  37. data/spec/data/smb_native_os.txt +31 -0
  38. data/spec/data/test_fingerprints.xml +24 -0
  39. data/spec/lib/db_spec.rb +89 -0
  40. data/spec/lib/formatter_spec.rb +69 -0
  41. data/spec/lib/match_reporter_spec.rb +90 -0
  42. data/spec/lib/nizer_spec.rb +124 -0
  43. data/spec/lib/verify_reporter_spec.rb +112 -0
  44. data/xml/apache_os.xml +295 -0
  45. data/xml/architecture.xml +45 -0
  46. data/xml/ftp_banners.xml +808 -0
  47. data/xml/h323_callresp.xml +701 -0
  48. data/xml/hp_pjl_id.xml +435 -0
  49. data/xml/http_cookies.xml +379 -0
  50. data/xml/http_servers.xml +3326 -0
  51. data/xml/http_wwwauth.xml +412 -0
  52. data/xml/imap_banners.xml +267 -0
  53. data/xml/nntp_banners.xml +51 -0
  54. data/xml/ntp_banners.xml +538 -0
  55. data/xml/pop_banners.xml +452 -0
  56. data/xml/rsh_resp.xml +90 -0
  57. data/xml/sip_banners.xml +14 -0
  58. data/xml/smb_native_os.xml +385 -0
  59. data/xml/smtp_banners.xml +1738 -0
  60. data/xml/smtp_debug.xml +45 -0
  61. data/xml/smtp_ehlo.xml +53 -0
  62. data/xml/smtp_expn.xml +95 -0
  63. data/xml/smtp_help.xml +212 -0
  64. data/xml/smtp_mailfrom.xml +24 -0
  65. data/xml/smtp_noop.xml +45 -0
  66. data/xml/smtp_quit.xml +31 -0
  67. data/xml/smtp_rcptto.xml +33 -0
  68. data/xml/smtp_rset.xml +23 -0
  69. data/xml/smtp_turn.xml +23 -0
  70. data/xml/smtp_vrfy.xml +109 -0
  71. data/xml/snmp_sysdescr.xml +8008 -0
  72. data/xml/snmp_sysobjid.xml +284 -0
  73. data/xml/ssh_banners.xml +790 -0
  74. data/xml/upnp_banners.xml +590 -0
  75. 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
@@ -0,0 +1,3 @@
1
+ module Recog
2
+ VERSION = "0.01"
3
+ end
@@ -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>
@@ -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