recog 0.01

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