recog 2.0.15 → 2.0.16
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/bin/recog_match +5 -1
- data/features/data/multiple_banners_fingerprints.xml +30 -0
- data/features/match.feature +16 -0
- data/lib/recog/matcher.rb +25 -7
- data/lib/recog/matcher_factory.rb +1 -1
- data/lib/recog/nizer.rb +18 -0
- data/lib/recog/version.rb +1 -1
- data/spec/lib/recog/nizer_spec.rb +48 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebf510b58ac33138c4fdcf5ee549e8a357f705af
|
4
|
+
data.tar.gz: d66456b16899f7af045ab574181bc344ac67a7f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86f98b254c38d880fa44c03a2be7d5ba5264163b6b119890fc4a91b40e37b1958e4a201ed84a8b47b5e1ee33a2be52db9920ecfedb243a271f80c6043c978309
|
7
|
+
data.tar.gz: b81423e03118c816b4402c1fc69e42732d5907996889b95d1e28fda0f8cd9dbb945b1fbe4e06a756b0e2bdd54d59d587c44941caa4282a84b88525ab0d8fc5e5
|
data/.gitignore
CHANGED
data/bin/recog_match
CHANGED
@@ -6,7 +6,7 @@ require 'ostruct'
|
|
6
6
|
require 'recog'
|
7
7
|
require 'recog/matcher_factory'
|
8
8
|
|
9
|
-
options = OpenStruct.new(color: false, detail: false, fail_fast: false)
|
9
|
+
options = OpenStruct.new(color: false, detail: false, fail_fast: false, multi_match: false)
|
10
10
|
|
11
11
|
option_parser = OptionParser.new do |opts|
|
12
12
|
opts.banner = "Usage: #{$0} [options] XML_FINGERPRINT_FILE [BANNERS_FILE]"
|
@@ -33,6 +33,10 @@ option_parser = OptionParser.new do |opts|
|
|
33
33
|
options.color = true
|
34
34
|
end
|
35
35
|
|
36
|
+
opts.on("--[no-]multi-match", "Enable or disable multiple matches (defaults to disabled)") do |o|
|
37
|
+
options.multi_match = o
|
38
|
+
end
|
39
|
+
|
36
40
|
opts.on("-h", "--help", "Show this message.") do
|
37
41
|
puts opts
|
38
42
|
exit
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<fingerprints>
|
3
|
+
<fingerprint pattern="FTP">
|
4
|
+
<example>---- FTP Stuff ----</example>
|
5
|
+
<example>FTP server</example>
|
6
|
+
<description>Generic FTP,
|
7
|
+
Checks for the existence of the word FTP in the line
|
8
|
+
</description>
|
9
|
+
<!-- Asserting nothing -->
|
10
|
+
</fingerprint>
|
11
|
+
<fingerprint pattern="^-{10} Welcome to Pure-FTPd (.*)-{10}$">
|
12
|
+
<example>---------- Welcome to Pure-FTPd ----------</example>
|
13
|
+
<description>Pure-FTPd
|
14
|
+
Config data can be zero or more of: [privsep] [TLS]
|
15
|
+
</description>
|
16
|
+
<param pos="1" name="pureftpd.config"/>
|
17
|
+
<param pos="0" name="service.family" value="Pure-FTPd"/>
|
18
|
+
<param pos="0" name="service.product" value="Pure-FTPd"/>
|
19
|
+
</fingerprint>
|
20
|
+
<fingerprint pattern="^(\S+) FTP Server \(SunOS (\S+)\) ready\.?$" flags="REG_ICASE">
|
21
|
+
<description>SunOS/Solaris</description>
|
22
|
+
<example>example.com FTP server (SunOS 5.7) ready.</example>
|
23
|
+
<param pos="0" name="os.vendor" value="Sun"/>
|
24
|
+
<param pos="0" name="os.family" value="Solaris"/>
|
25
|
+
<param pos="0" name="os.product" value="Solaris"/>
|
26
|
+
<param pos="0" name="os.device" value="General"/>
|
27
|
+
<param pos="1" name="host.name"/>
|
28
|
+
<param pos="2" name="os.version"/>
|
29
|
+
</fingerprint>
|
30
|
+
</fingerprints>
|
data/features/match.feature
CHANGED
@@ -14,3 +14,19 @@ Feature: Match
|
|
14
14
|
FAIL: ---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
|
15
15
|
FAIL: polaris FTP server (SunOS 5.8) ready
|
16
16
|
"""
|
17
|
+
|
18
|
+
Scenario: Finds multiple matches
|
19
|
+
When I run `recog_match multiple_banners_fingerprints.xml sample_banner.txt --multi-match`
|
20
|
+
Then it should pass with:
|
21
|
+
"""
|
22
|
+
MATCHES: {"matched"=>"Generic FTP, Checks for the existence of the word FTP in the line", "data"=>"---------- Welcome to Pure-FTPd [privsep] [TLS] ----------"},{"matched"=>"Pure-FTPd Config data can be zero or more of: [privsep] [TLS]", "pureftpd.config"=>"[privsep] [TLS] ", "service.family"=>"Pure-FTPd", "service.product"=>"Pure-FTPd", "data"=>"---------- Welcome to Pure-FTPd [privsep] [TLS] ----------"}
|
23
|
+
MATCHES: {"matched"=>"Generic FTP, Checks for the existence of the word FTP in the line", "data"=>"polaris FTP server (SunOS 5.8) ready."},{"matched"=>"SunOS/Solaris", "os.vendor"=>"Sun", "os.family"=>"Solaris", "os.product"=>"Solaris", "os.device"=>"General", "host.name"=>"polaris", "os.version"=>"5.8", "data"=>"polaris FTP server (SunOS 5.8) ready."}
|
24
|
+
"""
|
25
|
+
|
26
|
+
Scenario: Finds first matches using no-multi-match flag
|
27
|
+
When I run `recog_match multiple_banners_fingerprints.xml sample_banner.txt --no-multi-match`
|
28
|
+
Then it should pass with:
|
29
|
+
"""
|
30
|
+
MATCH: {"matched"=>"Generic FTP, Checks for the existence of the word FTP in the line", "data"=>"---------- Welcome to Pure-FTPd [privsep] [TLS] ----------"}
|
31
|
+
MATCH: {"matched"=>"Generic FTP, Checks for the existence of the word FTP in the line", "data"=>"polaris FTP server (SunOS 5.8) ready."}
|
32
|
+
"""
|
data/lib/recog/matcher.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
module Recog
|
2
2
|
class Matcher
|
3
|
-
attr_reader :fingerprints, :reporter
|
3
|
+
attr_reader :fingerprints, :reporter, :multi_match
|
4
4
|
|
5
|
-
|
5
|
+
# @param fingerprints Array of [Recog::Fingerprint] The list of fingerprints from the Recog DB to find possible matches.
|
6
|
+
# @param reporter [Recog::MatchReporter] The reporting structure that holds the matches and fails
|
7
|
+
# @param multi_match [Boolean] specifies whether or not to use multi-match (true) or not (false)
|
8
|
+
def initialize(fingerprints, reporter, multi_match)
|
6
9
|
@fingerprints = fingerprints
|
7
10
|
@reporter = reporter
|
11
|
+
@multi_match = multi_match
|
8
12
|
end
|
9
13
|
|
14
|
+
# @param banners_file [String] The source of banners to attempt to match against the Recog DB.
|
10
15
|
def match_banners(banners_file)
|
11
16
|
reporter.report do
|
12
17
|
|
@@ -22,14 +27,26 @@ class Matcher
|
|
22
27
|
reporter.increment_line_count
|
23
28
|
|
24
29
|
line = line.to_s.unpack("C*").pack("C*").strip.gsub(/\\[rn]/, '')
|
25
|
-
|
30
|
+
found_extractions = false
|
31
|
+
|
32
|
+
all_extractions = []
|
26
33
|
fingerprints.each do |fp|
|
27
|
-
|
34
|
+
extractions = fp.match(line)
|
35
|
+
if extractions
|
36
|
+
found_extractions = true
|
37
|
+
extractions['data'] = line
|
38
|
+
if multi_match
|
39
|
+
all_extractions << extractions
|
40
|
+
else
|
41
|
+
reporter.match "MATCH: #{extractions.inspect}"
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
28
45
|
end
|
29
46
|
|
30
|
-
if
|
31
|
-
|
32
|
-
reporter.match "
|
47
|
+
if found_extractions
|
48
|
+
match_prefix = all_extractions.size > 1 ? 'MATCHES' : 'MATCH'
|
49
|
+
reporter.match "#{match_prefix}: #{all_extractions.map(&:inspect).join(',')}" if multi_match
|
33
50
|
else
|
34
51
|
reporter.failure "FAIL: #{line}"
|
35
52
|
end
|
@@ -37,6 +54,7 @@ class Matcher
|
|
37
54
|
if reporter.stop?
|
38
55
|
break
|
39
56
|
end
|
57
|
+
|
40
58
|
end
|
41
59
|
|
42
60
|
fd.close if file_source
|
@@ -8,7 +8,7 @@ module MatcherFactory
|
|
8
8
|
def self.build(options)
|
9
9
|
formatter = Formatter.new(options, $stdout)
|
10
10
|
reporter = MatchReporter.new(options, formatter)
|
11
|
-
Matcher.new(options.fingerprints, reporter)
|
11
|
+
Matcher.new(options.fingerprints, reporter, options.multi_match)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/recog/nizer.rb
CHANGED
@@ -41,6 +41,24 @@ class Nizer
|
|
41
41
|
nil
|
42
42
|
end
|
43
43
|
|
44
|
+
def self.multi_match(match_key, match_string)
|
45
|
+
match_string = match_string.to_s.unpack("C*").pack("C*")
|
46
|
+
@@db_manager ||= Recog::DBManager.new
|
47
|
+
|
48
|
+
matches = Array.new #array to hold all fingerprint matches
|
49
|
+
|
50
|
+
@@db_manager.databases.each do |db|
|
51
|
+
next unless db.match_key == match_key
|
52
|
+
|
53
|
+
db.fingerprints.each do |fp|
|
54
|
+
m = fp.match(match_string)
|
55
|
+
matches.push(m) if m
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
return matches
|
60
|
+
end
|
61
|
+
|
44
62
|
#
|
45
63
|
# Consider an array of match outputs, choose the best result, taking into
|
46
64
|
# account the granularity of OS vs Version vs SP vs Language. Only consider
|
data/lib/recog/version.rb
CHANGED
@@ -26,6 +26,54 @@ describe Recog::Nizer do
|
|
26
26
|
|
27
27
|
end
|
28
28
|
end
|
29
|
+
|
30
|
+
line = 'non-existent'
|
31
|
+
context "with non-existent match" do
|
32
|
+
let(:match_result) {subject.match('smb.native_os', line) }
|
33
|
+
it "returns a nil" do
|
34
|
+
expect(match_result).to be_nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe ".multi_match" do
|
40
|
+
File.readlines(File.expand_path(File.join('spec', 'data', 'smb_native_os.txt'))).each do |line|
|
41
|
+
data = line.strip
|
42
|
+
|
43
|
+
context "with smb_native_os:#{data}" do
|
44
|
+
let(:match_results) {subject.multi_match('smb.native_os', data) }
|
45
|
+
|
46
|
+
it "returns an array" do
|
47
|
+
expect(match_results.class).to eq(::Array)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "returns at least one successful match" do
|
51
|
+
expect(match_results.size).to be > 0
|
52
|
+
end
|
53
|
+
|
54
|
+
it "correctly matches service or os" do
|
55
|
+
match_results do |mr|
|
56
|
+
if data =~ /^Windows/
|
57
|
+
expect(mr['os.product']).to match(/^Windows/)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
line = 'non-existent'
|
66
|
+
context "with non-existent match" do
|
67
|
+
let(:match_results) {subject.multi_match('smb.native_os', line) }
|
68
|
+
|
69
|
+
it "returns an array" do
|
70
|
+
expect(match_results.class).to eq(::Array)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns an empty array" do
|
74
|
+
expect(match_results).to be_empty
|
75
|
+
end
|
76
|
+
end
|
29
77
|
end
|
30
78
|
|
31
79
|
describe ".best_os_match" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: recog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rapid7 Research
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -136,6 +136,7 @@ files:
|
|
136
136
|
- bin/recog_verify
|
137
137
|
- features/data/failing_banners_fingerprints.xml
|
138
138
|
- features/data/matching_banners_fingerprints.xml
|
139
|
+
- features/data/multiple_banners_fingerprints.xml
|
139
140
|
- features/data/no_tests.xml
|
140
141
|
- features/data/sample_banner.txt
|
141
142
|
- features/data/successful_tests.xml
|