hooray 0.0.1
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/README.md +81 -0
- data/Rakefile +9 -0
- data/bin/hoo +9 -0
- data/lib/hooray.rb +20 -0
- data/lib/hooray/cli.rb +114 -0
- data/lib/hooray/node.rb +38 -0
- data/lib/hooray/port.rb +7 -0
- data/lib/hooray/scan.rb +19 -0
- data/lib/hooray/seek.rb +79 -0
- data/lib/hooray/settings.rb +59 -0
- data/lib/hooray/settings/devices.yml +9 -0
- data/lib/hooray/settings/nmap-mac-prefixes +19651 -0
- data/lib/hooray/settings/services.yml +11 -0
- data/lib/hooray/settings/settings.yml +7 -0
- data/lib/hooray/version.rb +3 -0
- data/spec/hooray/node_spec.rb +24 -0
- data/spec/hooray/seek_spec.rb +16 -0
- data/spec/spec_helper.rb +15 -0
- metadata +133 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e7ef2ad4641d2f2c855dbd7269067c578e0810c4
|
4
|
+
data.tar.gz: 63abe0efb004ead77086adf20558e42d788b1ca6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6feb8a504adfb80d0310f63d9805f2e0572d58f7d498ca642c7b10702ba4c59e4b2485d9163afe9e02e2bd8fd1215055ed3fbdb18bc27a489d1941f6a21f8a6e
|
7
|
+
data.tar.gz: f59f4a37cc59cd917d5ccd0efb843a98d294b48de8eee7d63e91aebe1aba3fecd915cb0429b36d0c59da2aa81e1f91bb69572abcf5f0e0722ccd6801ec0da9c5
|
data/README.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
Hooray
|
2
|
+
======
|
3
|
+
|
4
|
+
Find devices around you.
|
5
|
+
|
6
|
+
```
|
7
|
+
hoo list
|
8
|
+
```
|
9
|
+
```
|
10
|
+
Motorola Mobility | 192.168.1.70 | 34:bb:XX:XX:XX:XX
|
11
|
+
Giga-byte Technology Co. | 192.168.1.71 | 94:de:XX:XX:XX:XX
|
12
|
+
Veih's Android | 192.168.1.72 | 04:46:XX:XX:XX:XX
|
13
|
+
Nery's LG | 192.168.1.73 | cc:fa:XX:XX:XX:XX
|
14
|
+
nofxx's iPhone | 192.168.1.77 | 64:a3:XX:XX:XX:XX
|
15
|
+
LG Electronics | 192.168.1.79 | cc:fa:XX:XX:XX:XX
|
16
|
+
TP-Link | 192.168.1.253 | f8:d1:XX:XX:XX:XX
|
17
|
+
Siemens Subscriber Networks | 192.168.1.254 | 00:0b:XX:XX:XX:XX
|
18
|
+
---
|
19
|
+
8 devices @ 2014-12-11 13:32:04 -0200 2.85s
|
20
|
+
```
|
21
|
+
|
22
|
+
Find running services:
|
23
|
+
|
24
|
+
```
|
25
|
+
hoo list web
|
26
|
+
```
|
27
|
+
```
|
28
|
+
nofxx desktop | 192.168.1.77 | 64:a3:XX:XX:XX:XX
|
29
|
+
```
|
30
|
+
|
31
|
+
Or simply by ports:
|
32
|
+
|
33
|
+
|
34
|
+
```
|
35
|
+
hoo list 80
|
36
|
+
hoo list 6777 udp
|
37
|
+
```
|
38
|
+
|
39
|
+
Monitor:
|
40
|
+
|
41
|
+
```
|
42
|
+
hoo watch
|
43
|
+
```
|
44
|
+
```
|
45
|
+
New nodes @ 2014-12-11 13:53:06 -0200
|
46
|
+
NAME | IP | MAC
|
47
|
+
-------------|--------------|------------------
|
48
|
+
iPhone nofxx | 192.168.1.76 | 64:a3:XX:XX:XX:XX
|
49
|
+
```
|
50
|
+
|
51
|
+
Use as a lib:
|
52
|
+
```
|
53
|
+
require 'hooray'
|
54
|
+
Hooray::Seek.lan(port, protocol).devices
|
55
|
+
Hooray::Seek.new(network, port, protocol).devices
|
56
|
+
```
|
57
|
+
|
58
|
+
## Why?
|
59
|
+
|
60
|
+
Bind macs or fix IP's is also boring and a per device work.
|
61
|
+
As more we use wifi/ethernet devices in our company, nmap gets boring.
|
62
|
+
Not to mention slow, there's no easy way to assign names to devices I know,
|
63
|
+
which makes OS scan/port scan a needed option most times (thus making it slower).
|
64
|
+
Also (please open a issue if you know how) even in fast mode nmap won't run in under 2s.
|
65
|
+
And, on recent updates, you need sudo for all of that.
|
66
|
+
|
67
|
+
## How?
|
68
|
+
|
69
|
+
```
|
70
|
+
hoo init
|
71
|
+
```
|
72
|
+
|
73
|
+
A ~/.horray folder is prepopulated:
|
74
|
+
devices.yml regex list you mac addresses with names you recognize.
|
75
|
+
services.yml regex list ports to names you recognize.
|
76
|
+
|
77
|
+
|
78
|
+
## Credits
|
79
|
+
|
80
|
+
* Ruby net-ping gem.
|
81
|
+
* Nmap`s mac precompiled prefixes.
|
data/Rakefile
ADDED
data/bin/hoo
ADDED
data/lib/hooray.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# require 'pry'
|
2
|
+
require 'yaml'
|
3
|
+
require 'open3'
|
4
|
+
require 'socket'
|
5
|
+
require 'ipaddr'
|
6
|
+
require 'macaddr'
|
7
|
+
require 'net/ping'
|
8
|
+
require 'paint/pa'
|
9
|
+
require 'table_print'
|
10
|
+
|
11
|
+
require 'hooray/settings'
|
12
|
+
require 'hooray/node'
|
13
|
+
require 'hooray/port'
|
14
|
+
require 'hooray/seek'
|
15
|
+
|
16
|
+
require 'hooray/cli'
|
17
|
+
|
18
|
+
# Hooray!
|
19
|
+
module Hooray
|
20
|
+
end
|
data/lib/hooray/cli.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Hooray
|
4
|
+
# Nice cli
|
5
|
+
class CLI < Thor
|
6
|
+
class_option :verbose, type: :boolean, aliases: :v
|
7
|
+
class_option :network, type: :string, aliases: :n
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
super
|
11
|
+
return if ARGV.first =~ /init/
|
12
|
+
Settings.load!
|
13
|
+
@start = Time.now
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'init', 'creates settings on ~'
|
17
|
+
long_desc <<-LONG
|
18
|
+
|
19
|
+
Creates local settings on your home folder.
|
20
|
+
|
21
|
+
LONG
|
22
|
+
def init
|
23
|
+
return if Dir.exist?(Settings::CONFIG_DIR)
|
24
|
+
pa 'Creating ~/.hooray directory', :red
|
25
|
+
Dir.mkdir(Settings::CONFIG_DIR)
|
26
|
+
settings_dir = File.join(File.dirname(__FILE__), 'settings')
|
27
|
+
%w(settings devices services).each do |file|
|
28
|
+
pa "Creating ~/.hooray/#{file}.yml", :red
|
29
|
+
FileUtils.cp "#{settings_dir}/#{file}.yml", Settings::CONFIG_DIR
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'list FILTER', 'list and apply FILTER'
|
34
|
+
long_desc <<-LONG
|
35
|
+
|
36
|
+
Lists out all devices connected to the current network.
|
37
|
+
|
38
|
+
LONG
|
39
|
+
def list(*filter)
|
40
|
+
pa "Listing devices * #{filter}", :red
|
41
|
+
print_table Seek.new(options[:network], *filter).nodes
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'watch FILTER', 'watch in realtime FILTER'
|
45
|
+
long_desc <<-LONG
|
46
|
+
|
47
|
+
Keeps listing out all devices connected to the current network.
|
48
|
+
|
49
|
+
LONG
|
50
|
+
def watch
|
51
|
+
print_table old_seek = Seek.new(options[:network]).nodes
|
52
|
+
pa "Starting watch...", :red
|
53
|
+
loop do
|
54
|
+
sleep 5
|
55
|
+
new_seek = Seek.new(options[:network]).nodes
|
56
|
+
print_change :new, (new_seek - old_seek)
|
57
|
+
print_change :old, (old_seek - new_seek), :red
|
58
|
+
old_seek = new_seek
|
59
|
+
# binding.pry
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
desc 'update', 'updates ssh config files'
|
64
|
+
long_desc <<-LONG
|
65
|
+
|
66
|
+
Updates your config files based on devices.
|
67
|
+
|
68
|
+
LONG
|
69
|
+
def update
|
70
|
+
end
|
71
|
+
|
72
|
+
desc 'local', 'local port status'
|
73
|
+
long_desc <<-LONG
|
74
|
+
|
75
|
+
This is a helper for those who can never remember netstat options.
|
76
|
+
|
77
|
+
LONG
|
78
|
+
def local
|
79
|
+
Kernel.system 'netstat -na | grep "tcp.*LISTEN"'
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def debug(message)
|
85
|
+
return unless options[:verbose]
|
86
|
+
say message
|
87
|
+
end
|
88
|
+
|
89
|
+
def print_change(txt, changes, color = :blue)
|
90
|
+
return if changes.empty?
|
91
|
+
pa "#{txt.to_s.capitalize} nodes @ #{Time.now}", color
|
92
|
+
tp changes, :name, :ip, :mac
|
93
|
+
end
|
94
|
+
|
95
|
+
def print_table(nodes)
|
96
|
+
tp nodes, :name, :ip, :mac
|
97
|
+
puts "---"
|
98
|
+
took = (Time.now - @start).round(2)
|
99
|
+
pa "#{nodes.count} devices @ #{Time.now} #{took}s", '#777', :bold
|
100
|
+
end
|
101
|
+
|
102
|
+
def method_missing(*params)
|
103
|
+
case params.size
|
104
|
+
when 1 then
|
105
|
+
puts "Do you want to `#{params.first}` to a device?"
|
106
|
+
puts "Use #{ARGV.first} #{params.first} <device name>"
|
107
|
+
when 2 then
|
108
|
+
command, device = *params
|
109
|
+
system "#{command} "
|
110
|
+
else super
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/hooray/node.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Hooray
|
2
|
+
# Node representing a device
|
3
|
+
class Node
|
4
|
+
attr_accessor :host, :name, :nick, :mac, :ip, :ports
|
5
|
+
|
6
|
+
def initialize(params = {})
|
7
|
+
@ip = params[:ip]
|
8
|
+
@mac = params[:mac]
|
9
|
+
@mac ||= Mac.addr if @ip == Seek.my_lan_ip
|
10
|
+
set_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_name
|
14
|
+
return unless mac
|
15
|
+
if [Mac.addr].flatten.include?(mac)
|
16
|
+
@name = Socket.gethostname
|
17
|
+
else
|
18
|
+
@name = Settings.device(mac) || Settings.family(mac)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_ip
|
23
|
+
Addrinfo.ip(ip)
|
24
|
+
end
|
25
|
+
|
26
|
+
def <=>(other)
|
27
|
+
ip <=> other.ip
|
28
|
+
end
|
29
|
+
|
30
|
+
def eql?(other)
|
31
|
+
ip == other.ip || mac == other.mac
|
32
|
+
end
|
33
|
+
|
34
|
+
def hash
|
35
|
+
[ip, mac].hash
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/hooray/port.rb
ADDED
data/lib/hooray/scan.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Hooray
|
2
|
+
#
|
3
|
+
# Main runner
|
4
|
+
#
|
5
|
+
class Scan < Struct.new(:target, :ports, :opts)
|
6
|
+
def run
|
7
|
+
scan = []
|
8
|
+
bots = []
|
9
|
+
pa "Starting #{Settings.list} on '#{target}' #{ports}"
|
10
|
+
[ports].flatten.each do |port|
|
11
|
+
bots << Thread.new do
|
12
|
+
scan << ip if Net::Ping::TCP.new(ip.to_s, port, 1).ping?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
bots.each(&:join)
|
16
|
+
scan
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/hooray/seek.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
module Hooray
|
2
|
+
#
|
3
|
+
# Main runner
|
4
|
+
#
|
5
|
+
class Seek
|
6
|
+
attr_accessor :network, :opts, :nodes
|
7
|
+
|
8
|
+
NET_MASK = 24
|
9
|
+
TIMEOUT = 1
|
10
|
+
|
11
|
+
def initialize(network = nil, *params)
|
12
|
+
@network = network || Seek.local_mask
|
13
|
+
unless params.empty?
|
14
|
+
ports, words = params.flatten.partition { |s| s =~ /\d+/ }
|
15
|
+
@ports = ports.map(&:to_i).first # TODO
|
16
|
+
@protocol = words.select { |w| w =~ /udp|tcp/ }.join
|
17
|
+
end
|
18
|
+
|
19
|
+
@nodes = ping.sort.map do |n|
|
20
|
+
Node.new(ip: n, mac: arp_table[n.to_s]) # , name: find_name(n))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
alias_method :nodes, :devices
|
24
|
+
|
25
|
+
def ping_class
|
26
|
+
return Net::Ping::External unless @protocol
|
27
|
+
@protocol =~ /udp/ ? Net::Ping::UDP : Net::Ping::TCP
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# fast -> -sn -PA
|
32
|
+
#
|
33
|
+
def ping
|
34
|
+
scan = []
|
35
|
+
bots = []
|
36
|
+
# pa "Starting #{Settings.list} on '#{network}' #{@ports} #{@protocol}"
|
37
|
+
@network.to_range.each do |ip|
|
38
|
+
# next if ip == my_lan_ip
|
39
|
+
bots << Thread.new do
|
40
|
+
if ping_class.new(ip.to_s, @ports, TIMEOUT).ping?
|
41
|
+
scan << ip
|
42
|
+
print '.'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
bots.each(&:join)
|
47
|
+
puts
|
48
|
+
scan
|
49
|
+
end
|
50
|
+
|
51
|
+
def arp_table
|
52
|
+
return @arp_table if @arp_table
|
53
|
+
@arp_table ||= {}
|
54
|
+
`arp -n`.split(/\n/).each do |l|
|
55
|
+
ip, _hw, mac, _flag, iface = l.split(/\s+/)
|
56
|
+
# p "#{ip} #{mac} #{iface}"
|
57
|
+
@arp_table.merge!(ip => mac) if iface
|
58
|
+
end
|
59
|
+
@arp_table
|
60
|
+
end
|
61
|
+
|
62
|
+
class << self
|
63
|
+
|
64
|
+
def my_ips
|
65
|
+
Socket.ip_address_list.select do |ip|
|
66
|
+
ip.ipv4_private? && !ip.ipv4_loopback?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def my_lan_ip
|
71
|
+
IPAddr.new(my_ips.first.ip_address)
|
72
|
+
end
|
73
|
+
|
74
|
+
def local_mask
|
75
|
+
my_lan_ip.mask(NET_MASK)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Hooray
|
2
|
+
# App settings
|
3
|
+
class Settings
|
4
|
+
CONFIG_DIR = ENV['HOME'] + '/.hooray/'
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :all, :services, :devices, :macs
|
8
|
+
|
9
|
+
def no_config_folder
|
10
|
+
puts "No config folder, run `#{$PROGRAM_NAME} init`"
|
11
|
+
puts
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
|
15
|
+
def load!
|
16
|
+
no_config_folder unless Dir.exist?(CONFIG_DIR)
|
17
|
+
@all = YAML.load_file(CONFIG_DIR + 'settings.yml')
|
18
|
+
@services = YAML.load_file(CONFIG_DIR + 'services.yml')
|
19
|
+
@devices = YAML.load_file(CONFIG_DIR + 'devices.yml')
|
20
|
+
@macs = {}
|
21
|
+
File.read(CONFIG_DIR + 'nmap-mac-prefixes').each_line do |line|
|
22
|
+
next if line =~ /^\s*#/
|
23
|
+
prefix, *name = *line.split(/\s/)
|
24
|
+
@macs.store(prefix, name.join(' '))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def list
|
29
|
+
out = 'SCAN: '
|
30
|
+
out += ' SYN' if syn_scan
|
31
|
+
out += ' SERVICE' if service_scan
|
32
|
+
out += ' OS' if os_fingerprint
|
33
|
+
out
|
34
|
+
end
|
35
|
+
|
36
|
+
def device(mac)
|
37
|
+
devices[mac.to_sym] || devices[mac.to_s]
|
38
|
+
end
|
39
|
+
|
40
|
+
def family(mac)
|
41
|
+
prefix = mac.to_s.gsub(':', '')[0,6].upcase
|
42
|
+
macs[prefix]
|
43
|
+
end
|
44
|
+
|
45
|
+
def all
|
46
|
+
@all ||= {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def method_missing(meth, *params)
|
50
|
+
if meth =~ /=/
|
51
|
+
all[meth.to_s.gsub('=', '')] = params.first
|
52
|
+
else
|
53
|
+
arg = meth.to_s.gsub('?', '')
|
54
|
+
all[arg] || all[arg.to_sym]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end # class << self
|
58
|
+
end # Settings
|
59
|
+
end # Hooray
|