hidden-hippo 0.1.0
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 +7 -0
- data/.gitignore +47 -0
- data/.rspec +2 -0
- data/.travis.yml +16 -0
- data/Gemfile +3 -0
- data/HACKING.md +78 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +5 -0
- data/Vagrantfile +24 -0
- data/bin/hh +4 -0
- data/ci/install-tshark.sh +21 -0
- data/config/gui-dev.ru +6 -0
- data/config/mongoid.yml +20 -0
- data/gui/public/favicon.png +0 -0
- data/gui/public/hidden-hippo.css +18 -0
- data/gui/public/user-placeholder.png +0 -0
- data/gui/views/dossier.rhtml +12 -0
- data/gui/views/index.rhtml +25 -0
- data/gui/views/layout.rhtml +31 -0
- data/gui/views/possibilities.rhtml +10 -0
- data/hidden-hippo.gemspec +31 -0
- data/hippo-small.png +0 -0
- data/hippo.png +0 -0
- data/lib/hidden_hippo/cli/app.rb +23 -0
- data/lib/hidden_hippo/cli/database.rb +47 -0
- data/lib/hidden_hippo/cli/gui.rb +51 -0
- data/lib/hidden_hippo/daemon.rb +90 -0
- data/lib/hidden_hippo/dossier.rb +23 -0
- data/lib/hidden_hippo/extractors/dhcp_hostname_extractor.rb +16 -0
- data/lib/hidden_hippo/extractors/dns_history_extractor.rb +25 -0
- data/lib/hidden_hippo/extractors/dns_llmnr_extractor.rb +18 -0
- data/lib/hidden_hippo/extractors/http_request_url_extractor.rb +15 -0
- data/lib/hidden_hippo/extractors/mdns_hostname_extractor.rb +18 -0
- data/lib/hidden_hippo/gui.rb +21 -0
- data/lib/hidden_hippo/packets/dhcp.rb +13 -0
- data/lib/hidden_hippo/packets/dns.rb +23 -0
- data/lib/hidden_hippo/packets/http.rb +13 -0
- data/lib/hidden_hippo/packets/packet.rb +73 -0
- data/lib/hidden_hippo/paths.rb +15 -0
- data/lib/hidden_hippo/possibilities.rb +63 -0
- data/lib/hidden_hippo/reader.rb +36 -0
- data/lib/hidden_hippo/scanner.rb +51 -0
- data/lib/hidden_hippo/update.rb +3 -0
- data/lib/hidden_hippo/updator.rb +49 -0
- data/lib/hidden_hippo/version.rb +3 -0
- data/lib/hidden_hippo.rb +23 -0
- data/spec/db_daemon_spec.rb +7 -0
- data/spec/dns_scanner_spec.rb +41 -0
- data/spec/dossier_spec.rb +72 -0
- data/spec/extractors/dhcp_hostname_extractor_spec.rb +43 -0
- data/spec/extractors/dns_history_extractor_spec.rb +52 -0
- data/spec/extractors/dns_llmnr_extractor_spec.rb +45 -0
- data/spec/extractors/http_request_url_extractor_spec.rb +23 -0
- data/spec/extractors/mdns_hostname_extractor_spec.rb +45 -0
- data/spec/fixtures/dns_elise.pcap +0 -0
- data/spec/fixtures/dns_reddit_eth.pcap +0 -0
- data/spec/fixtures/tcp_noise.pcap +0 -0
- data/spec/gui_daemon_spec.rb +7 -0
- data/spec/hidden_hippo_spec.rb +32 -0
- data/spec/packet_spec.rb +88 -0
- data/spec/possibilities_spec.rb +113 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/support/cli_controller_examples.rb +136 -0
- data/spec/updator_spec.rb +37 -0
- metadata +274 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
require 'hidden_hippo/possibilities'
|
3
|
+
|
4
|
+
module HiddenHippo
|
5
|
+
# Holds all the information for a specific mac address
|
6
|
+
class Dossier
|
7
|
+
include Mongoid::Document
|
8
|
+
|
9
|
+
field :_id, type: String, default: ->{ mac_address }
|
10
|
+
|
11
|
+
field :mac_address, type: String
|
12
|
+
validates_presence_of :mac_address
|
13
|
+
|
14
|
+
field :name, type: Possibilities, default: ->{ Possibilities.new }
|
15
|
+
field :hostname, type: Possibilities, default: ->{ Possibilities.new }
|
16
|
+
field :username, type: Possibilities, default: ->{ Possibilities.new }
|
17
|
+
field :email, type: Possibilities, default: ->{ Possibilities.new }
|
18
|
+
field :device, type: Possibilities, default: ->{ Possibilities.new }
|
19
|
+
field :gender, type: Possibilities, default: ->{ Possibilities.new }
|
20
|
+
field :age, type: Possibilities, default: ->{ Possibilities.new }
|
21
|
+
field :history, type: Possibilities, default: ->{ Possibilities.new }
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'hidden_hippo/update'
|
2
|
+
|
3
|
+
module HiddenHippo
|
4
|
+
module Extractors
|
5
|
+
class DhcpHostnameExtractor
|
6
|
+
def initialize(queue)
|
7
|
+
@queue = queue
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(packet)
|
11
|
+
@queue << Update.new(packet.mac_src,
|
12
|
+
{hostname: packet.fqdn_name || packet.opt_hostname})
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'hidden_hippo/update'
|
2
|
+
|
3
|
+
module HiddenHippo
|
4
|
+
module Extractors
|
5
|
+
class DnsHistoryExtractor
|
6
|
+
def initialize(queue)
|
7
|
+
@queue = queue
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(packet)
|
11
|
+
return unless packet.udp_dest_port == 53
|
12
|
+
return if packet.dns_qry_name.delete(" ") == ""
|
13
|
+
|
14
|
+
if packet.request?
|
15
|
+
@queue << Update.new(packet.mac_src, {history: packet.dns_qry_name})
|
16
|
+
end
|
17
|
+
|
18
|
+
if packet.response?
|
19
|
+
@queue << Update.new(packet.mac_dest, {history: packet.dns_qry_name})
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'hidden_hippo/update'
|
2
|
+
|
3
|
+
module HiddenHippo
|
4
|
+
module Extractors
|
5
|
+
class DnsLlmnrExtractor
|
6
|
+
def initialize(queue)
|
7
|
+
@queue = queue
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(packet)
|
11
|
+
return unless packet.udp_dest_port == 5355
|
12
|
+
return unless packet.request?
|
13
|
+
|
14
|
+
@queue << Update.new(packet.mac_src, {hostname: packet.dns_qry_name})
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'hidden_hippo/update'
|
2
|
+
|
3
|
+
module HiddenHippo
|
4
|
+
module Extractors
|
5
|
+
class HttpRequestUrlExtractor
|
6
|
+
def initialize(queue)
|
7
|
+
@queue = queue
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(packet)
|
11
|
+
@queue << Update.new(packet.mac_src, {history: packet.full_uri})
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'hidden_hippo/update'
|
2
|
+
|
3
|
+
module HiddenHippo
|
4
|
+
module Extractors
|
5
|
+
class MdnsHostnameExtractor
|
6
|
+
def initialize(queue)
|
7
|
+
@queue = queue
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(packet)
|
11
|
+
return unless packet.udp_dest_port == 5353
|
12
|
+
return unless packet.response?
|
13
|
+
|
14
|
+
@queue << Update.new(packet.mac_src, {hostname: packet.ptr})
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'hidden_hippo'
|
3
|
+
require 'hidden_hippo/dossier'
|
4
|
+
|
5
|
+
module HiddenHippo
|
6
|
+
class Gui < Sinatra::Application
|
7
|
+
set :root, proc { (HiddenHippo.gem_root + 'gui').to_s }
|
8
|
+
|
9
|
+
get '/' do
|
10
|
+
dossiers = Dossier.all
|
11
|
+
|
12
|
+
erb :index, locals: {dossiers: dossiers}
|
13
|
+
end
|
14
|
+
|
15
|
+
get '/:mac' do
|
16
|
+
dossier = Dossier.find params[:mac]
|
17
|
+
|
18
|
+
erb :dossier, locals: {dossier: dossier}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'hidden_hippo/packets/packet'
|
2
|
+
|
3
|
+
module HiddenHippo
|
4
|
+
module Packets
|
5
|
+
class Dns < Packet
|
6
|
+
filter 'dns'
|
7
|
+
|
8
|
+
field :a, tshark: 'dns.a'
|
9
|
+
field :ptr, tshark: 'dns.ptr.domain_name'
|
10
|
+
field :dns_qry_name, tshark: 'dns.qry.name'
|
11
|
+
|
12
|
+
field :response, tshark: 'dns.flags.response', conv: ->(f) {f.to_i == 1}
|
13
|
+
|
14
|
+
def response?
|
15
|
+
response
|
16
|
+
end
|
17
|
+
|
18
|
+
def request?
|
19
|
+
!response?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module HiddenHippo
|
2
|
+
module Packets
|
3
|
+
Field = Struct.new :name, :tshark_field, :converter
|
4
|
+
|
5
|
+
class Packet
|
6
|
+
def self.fields
|
7
|
+
unless @fields
|
8
|
+
super_fields = self == Packet ? [] : superclass.fields.dup
|
9
|
+
@fields = super_fields
|
10
|
+
end
|
11
|
+
@fields
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.tshark_fields
|
15
|
+
fields.map &:tshark_field
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.filter(filter=nil)
|
19
|
+
@filter = filter if filter
|
20
|
+
@filter
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.field(name, options={})
|
24
|
+
fields << Field.new(name, options[:tshark], options[:conv])
|
25
|
+
attr_accessor name
|
26
|
+
name
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.parse(hash)
|
30
|
+
instance = new
|
31
|
+
|
32
|
+
reverse_names = Hash[*fields.map{|f| [f.tshark_field, f]}.flatten]
|
33
|
+
|
34
|
+
hash.each do |k, v|
|
35
|
+
field = reverse_names[k]
|
36
|
+
|
37
|
+
next unless field
|
38
|
+
|
39
|
+
conv = converter field
|
40
|
+
instance.public_send "#{field.name}=", conv.(v)
|
41
|
+
end
|
42
|
+
|
43
|
+
instance
|
44
|
+
end
|
45
|
+
|
46
|
+
# default fields
|
47
|
+
field :eth_mac_src, tshark: 'eth.src'
|
48
|
+
field :eth_mac_dest, tshark: 'eth.dst'
|
49
|
+
field :wlan_mac_src, tshark: 'wlan.sa'
|
50
|
+
field :wlan_mac_dest, tshark: 'wlan.da'
|
51
|
+
|
52
|
+
field :udp_src_port, tshark: 'udp.srcport', conv: :to_i
|
53
|
+
field :tcp_src_port, tshark: 'tcp.srcport', conv: :to_i
|
54
|
+
field :udp_dest_port, tshark: 'udp.dstport', conv: :to_i
|
55
|
+
field :tcp_dest_port, tshark: 'tcp.dstport', conv: :to_i
|
56
|
+
|
57
|
+
def mac_src
|
58
|
+
eth_mac_src || wlan_mac_src
|
59
|
+
end
|
60
|
+
|
61
|
+
def mac_dest
|
62
|
+
eth_mac_dest || wlan_mac_dest
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def self.converter(field)
|
68
|
+
conv = field.converter || ->(x){x}
|
69
|
+
conv.is_a?(Symbol) ? conv.to_proc : conv
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module HiddenHippo
|
4
|
+
module Paths
|
5
|
+
def home
|
6
|
+
@home ||= if ENV['HIDDEN_HIPPO_HOME']
|
7
|
+
Pathname.new(ENV['HIDDEN_HIPPO_HOME'])
|
8
|
+
else
|
9
|
+
Pathname.new(ENV['HOME']) + '.hidden-hippo'
|
10
|
+
end
|
11
|
+
@home.mkpath
|
12
|
+
@home
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module HiddenHippo
|
2
|
+
# Represents multiple possible values
|
3
|
+
#
|
4
|
+
# Each possible value has a support count. Adding possible values is done with #<<.
|
5
|
+
# Retrieving the support count for a possible value is done with #[].
|
6
|
+
class Possibilities
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def demongoize(doc)
|
11
|
+
HiddenHippo::Possibilities.new doc
|
12
|
+
end
|
13
|
+
|
14
|
+
def mongoize(value)
|
15
|
+
case value
|
16
|
+
when Possibilities then value.mongoize
|
17
|
+
else value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def evolve(value)
|
22
|
+
case value
|
23
|
+
when Possibilities then value.mongoize
|
24
|
+
else value
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def resizable?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(counts={})
|
34
|
+
@counts = counts
|
35
|
+
@counts.keep_if {|_, c| c.kind_of?(Integer)}
|
36
|
+
@counts.default_proc = proc {0}
|
37
|
+
end
|
38
|
+
|
39
|
+
def [](value)
|
40
|
+
@counts[value]
|
41
|
+
end
|
42
|
+
|
43
|
+
def <<(value)
|
44
|
+
@counts[value.to_s.gsub(/[.$]/, '_')] += 1
|
45
|
+
end
|
46
|
+
|
47
|
+
def each(&block)
|
48
|
+
@counts.sort_by{|c| - c.last}.map(&:first).each &block
|
49
|
+
end
|
50
|
+
|
51
|
+
def each_with_support(&block)
|
52
|
+
@counts.sort_by{|c| - c.last}.each &block
|
53
|
+
end
|
54
|
+
|
55
|
+
def mongoize
|
56
|
+
@counts
|
57
|
+
end
|
58
|
+
|
59
|
+
def resizable?
|
60
|
+
Possibilities.resizable?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'hidden_hippo/scanner'
|
2
|
+
require 'hidden_hippo/updator'
|
3
|
+
|
4
|
+
require 'hidden_hippo/packets/dns'
|
5
|
+
require 'hidden_hippo/packets/dhcp'
|
6
|
+
require 'hidden_hippo/packets/http'
|
7
|
+
|
8
|
+
require 'hidden_hippo/extractors/mdns_hostname_extractor'
|
9
|
+
require 'hidden_hippo/extractors/dhcp_hostname_extractor'
|
10
|
+
require 'hidden_hippo/extractors/http_request_url_extractor'
|
11
|
+
require 'hidden_hippo/extractors/dns_llmnr_extractor'
|
12
|
+
require 'thread'
|
13
|
+
|
14
|
+
module HiddenHippo
|
15
|
+
class Reader
|
16
|
+
def initialize(file)
|
17
|
+
@file = file
|
18
|
+
updator_queue = Queue.new
|
19
|
+
@updator = Updator.new updator_queue
|
20
|
+
@scanners = []
|
21
|
+
@scanners << Scanner.new(file, Packets::Dns,
|
22
|
+
Extractors::MdnsHostnameExtractor.new(updator_queue),
|
23
|
+
Extractors::DnsLlmnrExtractor.new(updator_queue))
|
24
|
+
@scanners << Scanner.new(file, Packets::Dhcp,
|
25
|
+
Extractors::DhcpHostnameExtractor.new(updator_queue))
|
26
|
+
@scanners << Scanner.new(file, Packets::Http,
|
27
|
+
Extractors::HttpRequestUrlExtractor.new(updator_queue))
|
28
|
+
end
|
29
|
+
|
30
|
+
def call
|
31
|
+
@updator.start
|
32
|
+
@scanners.each &:call
|
33
|
+
@updator.stop
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module HiddenHippo
|
4
|
+
class Scanner
|
5
|
+
def initialize(file, packet_class, *extractors)
|
6
|
+
@file = file
|
7
|
+
@extractors = extractors
|
8
|
+
@packet_class = packet_class
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
# call Tshark
|
13
|
+
tshark_fields = @packet_class.tshark_fields
|
14
|
+
|
15
|
+
args = [
|
16
|
+
'-2', '-Tfields', '-q',
|
17
|
+
'-r', @file,
|
18
|
+
'-R', @packet_class.filter,
|
19
|
+
*tshark_fields.map {|f| ['-e', f]}.flatten
|
20
|
+
]
|
21
|
+
|
22
|
+
Open3.popen3(%w(tshark tshark), *args) do |stdin, stdout, stderr, waiter|
|
23
|
+
# we don't need those
|
24
|
+
stdin.close
|
25
|
+
|
26
|
+
stdout.each do |line|
|
27
|
+
if line.count("\t") != tshark_fields.size - 1
|
28
|
+
puts 'Warinig: tshark returned a line of the wrong size. Ignoring it.'
|
29
|
+
puts "Offending line: #{line}"
|
30
|
+
next
|
31
|
+
end
|
32
|
+
|
33
|
+
split_line = line.chomp.split("\t").map {|f| f.empty? ? nil : f}
|
34
|
+
|
35
|
+
assoc = tshark_fields.zip split_line
|
36
|
+
packet = @packet_class.parse Hash[*assoc.flatten]
|
37
|
+
|
38
|
+
@extractors.each do |extractor|
|
39
|
+
extractor.call(packet)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
if waiter.value != 0
|
44
|
+
puts "Warning: tshark exited with status code #{waiter.value}."
|
45
|
+
puts "tshark #{args.join(' ')}"
|
46
|
+
puts stderr.read
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'hidden_hippo/dossier'
|
3
|
+
|
4
|
+
module HiddenHippo
|
5
|
+
class Updator
|
6
|
+
def initialize(queue)
|
7
|
+
@queue = queue
|
8
|
+
end
|
9
|
+
|
10
|
+
def start
|
11
|
+
return if @thread
|
12
|
+
@thread = Thread.new do
|
13
|
+
loop do
|
14
|
+
update = @queue.pop
|
15
|
+
|
16
|
+
break if update == :stop
|
17
|
+
|
18
|
+
dossier = dossier update.mac_address
|
19
|
+
|
20
|
+
update.fields.each do |key, value|
|
21
|
+
dossier.public_send(key) << value
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
dossier.save
|
26
|
+
rescue => e
|
27
|
+
puts 'shit'
|
28
|
+
p e
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def stop
|
35
|
+
@queue << :stop
|
36
|
+
@thread.join
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def dossier(mac_address)
|
42
|
+
if Dossier.where(mac_address: mac_address).exists?
|
43
|
+
Dossier.find mac_address
|
44
|
+
else
|
45
|
+
Dossier.new mac_address: mac_address
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/hidden_hippo.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'hidden_hippo/version'
|
3
|
+
require 'hidden_hippo/cli/app'
|
4
|
+
|
5
|
+
module HiddenHippo
|
6
|
+
def pid_exists?(pid)
|
7
|
+
Process.kill 0, pid
|
8
|
+
return true
|
9
|
+
rescue
|
10
|
+
return false
|
11
|
+
end
|
12
|
+
module_function :pid_exists?
|
13
|
+
|
14
|
+
def configure_db!(env = :production)
|
15
|
+
Mongoid.load! gem_root + 'config/mongoid.yml', env
|
16
|
+
end
|
17
|
+
module_function :configure_db!
|
18
|
+
|
19
|
+
def gem_root
|
20
|
+
Pathname.new(__FILE__) + '../../'
|
21
|
+
end
|
22
|
+
module_function :gem_root
|
23
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'hidden_hippo/scanner'
|
2
|
+
require 'hidden_hippo/packets/dns'
|
3
|
+
|
4
|
+
describe 'HiddenHippo::Scanner(Dns)' do
|
5
|
+
|
6
|
+
describe '#call' do
|
7
|
+
it 'should parse the pcap file' do
|
8
|
+
extractor = double('extractor')
|
9
|
+
scanner = HiddenHippo::Scanner.new('spec/fixtures/dns_elise.pcap', HiddenHippo::Packets::Dns, extractor)
|
10
|
+
|
11
|
+
expect(extractor).to receive(:call) do |packet|
|
12
|
+
expect(packet.ptr).to eq 'Elises-MacBook-Pro.local'
|
13
|
+
expect(packet.mac_src).to eq 'a8:bb:cf:09:9b:bc'
|
14
|
+
expect(packet.mac_dest).to eq '01:00:5e:00:00:fb'
|
15
|
+
end
|
16
|
+
|
17
|
+
scanner.call
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should skip non dns lines' do
|
21
|
+
extractor = double('extractor')
|
22
|
+
scanner = HiddenHippo::Scanner.new 'spec/fixtures/tcp_noise.pcap', HiddenHippo::Packets::Dns, extractor
|
23
|
+
|
24
|
+
expect(extractor).not_to receive(:call)
|
25
|
+
|
26
|
+
scanner.call
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should find the mac addresses in eth traffic' do
|
30
|
+
extractor = double('extractor')
|
31
|
+
scanner = HiddenHippo::Scanner.new('spec/fixtures/dns_reddit_eth.pcap', HiddenHippo::Packets::Dns, extractor)
|
32
|
+
|
33
|
+
expect(extractor).to receive(:call).at_least(:once) do |packet|
|
34
|
+
expect(packet.mac_src).not_to be_nil
|
35
|
+
expect(packet.mac_dest).not_to be_nil
|
36
|
+
end
|
37
|
+
|
38
|
+
scanner.call
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'hidden_hippo/dossier'
|
2
|
+
|
3
|
+
describe HiddenHippo::Dossier, :db do
|
4
|
+
before do
|
5
|
+
HiddenHippo::Dossier.delete_all
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'should require a mac address' do
|
9
|
+
expect(HiddenHippo::Dossier.new).to validate_presence_of :mac_address
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should use the mac address as an id' do
|
13
|
+
dossier = HiddenHippo::Dossier.new mac_address: '00:aa:bb:cc:dd:ee:ff'
|
14
|
+
dossier.save!
|
15
|
+
|
16
|
+
expect(HiddenHippo::Dossier.find('00:aa:bb:cc:dd:ee:ff')).to eq dossier
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should allow possibilities to be modified in place' do
|
20
|
+
dossier = HiddenHippo::Dossier.new mac_address: 'aa:bb:cc:dd:ee:ff:aa'
|
21
|
+
|
22
|
+
dossier.name << 'Steve'
|
23
|
+
|
24
|
+
expect(dossier.name).to contain_exactly 'Steve'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should serialize properly' do
|
28
|
+
dossier = HiddenHippo::Dossier.new(mac_address: '11:00:11:00:11:00:11')
|
29
|
+
|
30
|
+
dossier.name << 'John'
|
31
|
+
dossier.name << 'Billy'
|
32
|
+
dossier.name << 'Billy'
|
33
|
+
dossier.name << 'Steve'
|
34
|
+
|
35
|
+
dossier.username << 'billydakid89'
|
36
|
+
dossier.username << 'billydakid89'
|
37
|
+
dossier.username << 'billydakid89'
|
38
|
+
|
39
|
+
dossier.age << 40
|
40
|
+
dossier.age << 41
|
41
|
+
dossier.age << 41
|
42
|
+
dossier.age << 42
|
43
|
+
|
44
|
+
dossier.save!
|
45
|
+
|
46
|
+
found = HiddenHippo::Dossier.find '11:00:11:00:11:00:11'
|
47
|
+
expect(dossier).to eq found
|
48
|
+
expect(found.name).to contain_exactly 'John', 'Billy', 'Steve'
|
49
|
+
expect(found.username).to contain_exactly 'billydakid89'
|
50
|
+
expect(found.age).to contain_exactly '40', '41', '42'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should deserialize support counts as ints' do
|
54
|
+
dossier = HiddenHippo::Dossier.new(mac_address: '11:00:11:00:11:00:11')
|
55
|
+
|
56
|
+
dossier.name << 'Whoopie'
|
57
|
+
|
58
|
+
dossier.save!
|
59
|
+
|
60
|
+
found = HiddenHippo::Dossier.find '11:00:11:00:11:00:11'
|
61
|
+
expect(found.name['Whoopie']).to eq 1
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should update possibilities' do
|
65
|
+
HiddenHippo::Dossier.create mac_address: 'find me'
|
66
|
+
dossier = HiddenHippo::Dossier.find 'find me'
|
67
|
+
dossier.name << 'thing'
|
68
|
+
dossier.save
|
69
|
+
|
70
|
+
expect(HiddenHippo::Dossier.find('find me').name['thing']).to eq 1
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'hidden_hippo/extractors/dhcp_hostname_extractor'
|
3
|
+
|
4
|
+
describe HiddenHippo::Extractors::DhcpHostnameExtractor do
|
5
|
+
let(:queue) {Queue.new}
|
6
|
+
let(:extractor) {HiddenHippo::Extractors::DhcpHostnameExtractor.new queue}
|
7
|
+
|
8
|
+
describe '#call' do
|
9
|
+
it 'should not have a fqdn_name' do
|
10
|
+
packet = HiddenHippo::Packets::Dhcp.new
|
11
|
+
packet.eth_mac_src = 'dhcp some mac'
|
12
|
+
packet.fqdn_name = nil
|
13
|
+
packet.opt_hostname = 'hidden_hippo_super_mac'
|
14
|
+
|
15
|
+
extractor.call packet
|
16
|
+
out = queue.pop true
|
17
|
+
expect(out.mac_address).to eq 'dhcp some mac'
|
18
|
+
expect(out.fields).to eq ({hostname: 'hidden_hippo_super_mac'})
|
19
|
+
end
|
20
|
+
it 'should use a fqdn_name' do
|
21
|
+
packet = HiddenHippo::Packets::Dhcp.new
|
22
|
+
packet.eth_mac_src = 'dhcp some mac'
|
23
|
+
packet.fqdn_name = 'hidden_hippo_fqdn'
|
24
|
+
packet.opt_hostname = 'hidden_hippo_super_mac'
|
25
|
+
|
26
|
+
extractor.call packet
|
27
|
+
out = queue.pop true
|
28
|
+
expect(out.mac_address).to eq 'dhcp some mac'
|
29
|
+
expect(out.fields).to eq ({hostname: 'hidden_hippo_fqdn'})
|
30
|
+
end
|
31
|
+
it 'should not have a hostname' do
|
32
|
+
packet = HiddenHippo::Packets::Dhcp.new
|
33
|
+
packet.eth_mac_src = 'dhcp some mac'
|
34
|
+
packet.fqdn_name = 'hidden_hippo_super_mac'
|
35
|
+
packet.opt_hostname = nil
|
36
|
+
|
37
|
+
extractor.call packet
|
38
|
+
out = queue.pop true
|
39
|
+
expect(out.mac_address).to eq 'dhcp some mac'
|
40
|
+
expect(out.fields).to eq ({hostname: 'hidden_hippo_super_mac'})
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|