nmunch 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.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +33 -0
- data/LICENSE +22 -0
- data/README.md +103 -0
- data/Rakefile +44 -0
- data/bin/nmunch +75 -0
- data/lib/nmunch.rb +60 -0
- data/lib/nmunch/dissectors/arp.rb +26 -0
- data/lib/nmunch/dissectors/base.rb +46 -0
- data/lib/nmunch/dissectors/ip.rb +26 -0
- data/lib/nmunch/mac_oui_lookup.rb +40 -0
- data/lib/nmunch/node.rb +53 -0
- data/lib/nmunch/report.rb +53 -0
- data/lib/nmunch/subscribers/base.rb +18 -0
- data/lib/nmunch/subscribers/stdout.rb +23 -0
- data/lib/nmunch/version.rb +4 -0
- data/mac-prefixes.txt +16085 -0
- data/nmunch.gemspec +22 -0
- data/spec/lib/nmunch/dissectors/arp_spec.rb +24 -0
- data/spec/lib/nmunch/dissectors/base_spec.rb +45 -0
- data/spec/lib/nmunch/dissectors/ip_spec.rb +25 -0
- data/spec/lib/nmunch/mac_oui_lookup_spec.rb +21 -0
- data/spec/lib/nmunch/node_spec.rb +54 -0
- data/spec/lib/nmunch/report_spec.rb +39 -0
- data/spec/lib/nmunch/subscribers/base_spec.rb +12 -0
- data/spec/lib/nmunch/subscribers/stdout_spec.rb +26 -0
- data/spec/lib/nmunch_spec.rb +9 -0
- data/spec/spec_helper.rb +43 -0
- metadata +166 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
nmunch (1.0.0)
|
5
|
+
methadone (~> 1.2.3)
|
6
|
+
paint
|
7
|
+
pcap
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
diff-lcs (1.1.3)
|
13
|
+
methadone (1.2.3)
|
14
|
+
bundler
|
15
|
+
paint (0.8.5)
|
16
|
+
pcap (0.7.7)
|
17
|
+
rake (0.9.5)
|
18
|
+
rspec (2.12.0)
|
19
|
+
rspec-core (~> 2.12.0)
|
20
|
+
rspec-expectations (~> 2.12.0)
|
21
|
+
rspec-mocks (~> 2.12.0)
|
22
|
+
rspec-core (2.12.1)
|
23
|
+
rspec-expectations (2.12.0)
|
24
|
+
diff-lcs (~> 1.1.3)
|
25
|
+
rspec-mocks (2.12.0)
|
26
|
+
|
27
|
+
PLATFORMS
|
28
|
+
ruby
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
nmunch!
|
32
|
+
rake (~> 0.9.2)
|
33
|
+
rspec
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Michael Henriksen
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# Nmunch
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
Nmunch is a simple passive network discovery tool to find live nodes just by
|
6
|
+
listening to [ARP][arp] and broadcast packets on a network interface.
|
7
|
+
|
8
|
+
The main reason why I built this tool was mostly to have an excuse to play around
|
9
|
+
with the [ruby-pcap gem][ruby-pcap], but it turned out to be quite useful and the
|
10
|
+
architecture is flexible enough to extend the tool to do pretty much anything
|
11
|
+
with a discovered node.
|
12
|
+
|
13
|
+
## Requirements
|
14
|
+
|
15
|
+
- Ruby 1.9
|
16
|
+
- libpcap (<http://www.tcpdump.org/>)
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
gem install nmunch
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+

|
25
|
+
|
26
|
+
Nmunch is pretty straight-forward and supports two different methods:
|
27
|
+
|
28
|
+
### Listen on a network device
|
29
|
+
|
30
|
+
nmunch -i <device name>
|
31
|
+
|
32
|
+
To find the names of your network devices, use `ifconfig` in a terminal. Usually,
|
33
|
+
they are called `eth0`, `en1`, etc.
|
34
|
+
|
35
|
+
To stop listening, simply press `Ctrl`+`C` to interrupt. It might take a couple of
|
36
|
+
seconds before it reacts.
|
37
|
+
|
38
|
+
|
39
|
+
### Analyze a PCAP capture file
|
40
|
+
|
41
|
+
Nmunch can also find nodes from a `pcap` capture file:
|
42
|
+
|
43
|
+
nmunch -f <path/to/pcap/file.pcap>
|
44
|
+
|
45
|
+
### See all options
|
46
|
+
|
47
|
+
To see all options for Nmunch, type:
|
48
|
+
|
49
|
+
nmunch --help
|
50
|
+
|
51
|
+
## Extending Nmunch
|
52
|
+
|
53
|
+
I have tried to keep the architecture pretty flexible with a simple *publish/subscribe* pattern for handling the discovered nodes. So if you want all nodes to be pwned by your secret 0day spl0itz, you can write a new node subscriber to do that for you. Have a look at `Nmunch::Subscriber::Stdout` for an example of how to write a node subscriber
|
54
|
+
|
55
|
+
### *Y U NO capture [insert protocol name] to find more nodes!?!1*
|
56
|
+
|
57
|
+
The current version of Nmunch only looks for [ARP][arp] and broadcast IP packets which captures quite a lot of nodes, but if you have more ideas, you can easily make a new packet dissector to handle it. see `Nmunch::Dissectors::Arp` for an example of how to write a packet dissector.
|
58
|
+
|
59
|
+
## Roadmap
|
60
|
+
|
61
|
+
This is what I have on the roadmap for Nmunch:
|
62
|
+
|
63
|
+
### ▢ More tests
|
64
|
+
### ▢ Improve documentation
|
65
|
+
### ▢ Export results in formats compatible with other tools
|
66
|
+
### ▢ Easier way to register custom node subscribers
|
67
|
+
### ▢ Extract more interesting information apart from live nodes
|
68
|
+
### ▢ Make a [Metasploit] Auxiliary module version of Nmunch
|
69
|
+
|
70
|
+
## Contributing
|
71
|
+
|
72
|
+
Feel free to file an issue, even if you don't have time to submit a patch.
|
73
|
+
|
74
|
+
Please try to include tests for any patch you submit. If you don't include tests, I'll have to write one, and it'll take longer to get your code in.
|
75
|
+
|
76
|
+
## License
|
77
|
+
|
78
|
+
Copyright (c) 2012 Michael Henriksen
|
79
|
+
|
80
|
+
MIT License
|
81
|
+
|
82
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
83
|
+
a copy of this software and associated documentation files (the
|
84
|
+
"Software"), to deal in the Software without restriction, including
|
85
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
86
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
87
|
+
permit persons to whom the Software is furnished to do so, subject to
|
88
|
+
the following conditions:
|
89
|
+
|
90
|
+
The above copyright notice and this permission notice shall be
|
91
|
+
included in all copies or substantial portions of the Software.
|
92
|
+
|
93
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
94
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
95
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
96
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
97
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
98
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
99
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
100
|
+
|
101
|
+
[arp]: http://en.wikipedia.org/wiki/Address_Resolution_Protocol "Address Resolution Protocol"
|
102
|
+
[ruby-pcap]: https://github.com/ahobson/ruby-pcap
|
103
|
+
[metasploit]: http://www.metasploit.com/ "Metasploit penetration testing framework"
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
def dump_load_path
|
2
|
+
puts $LOAD_PATH.join("\n")
|
3
|
+
found = nil
|
4
|
+
$LOAD_PATH.each do |path|
|
5
|
+
if File.exists?(File.join(path,"rspec"))
|
6
|
+
puts "Found rspec in #{path}"
|
7
|
+
if File.exists?(File.join(path,"rspec","core"))
|
8
|
+
puts "Found core"
|
9
|
+
if File.exists?(File.join(path,"rspec","core","rake_task"))
|
10
|
+
puts "Found rake_task"
|
11
|
+
found = path
|
12
|
+
else
|
13
|
+
puts "!! no rake_task"
|
14
|
+
end
|
15
|
+
else
|
16
|
+
puts "!!! no core"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
if found.nil?
|
21
|
+
puts "Didn't find rspec/core/rake_task anywhere"
|
22
|
+
else
|
23
|
+
puts "Found in #{path}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
require 'bundler'
|
27
|
+
require 'rake/clean'
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rspec/core/rake_task'
|
31
|
+
rescue LoadError
|
32
|
+
dump_load_path
|
33
|
+
raise
|
34
|
+
end
|
35
|
+
|
36
|
+
include Rake::DSL
|
37
|
+
|
38
|
+
Bundler::GemHelper.install_tasks
|
39
|
+
|
40
|
+
RSpec::Core::RakeTask.new do |t|
|
41
|
+
# Put spec opts in a file named .rspec in root
|
42
|
+
end
|
43
|
+
|
44
|
+
task :default => [:spec]
|
data/bin/nmunch
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'methadone'
|
5
|
+
require 'pcap'
|
6
|
+
require 'paint'
|
7
|
+
require 'ipaddr'
|
8
|
+
require 'nmunch'
|
9
|
+
|
10
|
+
class App
|
11
|
+
include Methadone::Main
|
12
|
+
include Methadone::CLILogging
|
13
|
+
|
14
|
+
main do
|
15
|
+
begin
|
16
|
+
if !options[:interface] && !options[:file]
|
17
|
+
help_now!("You must specify an interface or a PCAP file.")
|
18
|
+
end
|
19
|
+
|
20
|
+
logger.debug("Command-line options: #{options.inspect}")
|
21
|
+
|
22
|
+
logger.debug("Setting up report")
|
23
|
+
report = Nmunch::Report.new(options)
|
24
|
+
|
25
|
+
logger.debug("Registering STDOUT node subscriber")
|
26
|
+
report.register_subscriber(Nmunch::Subscribers::Stdout.new(options))
|
27
|
+
|
28
|
+
# Disable output coloring if specified in options
|
29
|
+
if options.include?('no-color')
|
30
|
+
logger.debug("Disabling output coloring")
|
31
|
+
Paint.mode = 0
|
32
|
+
end
|
33
|
+
|
34
|
+
if !options.include?('no-banner')
|
35
|
+
puts Paint[Nmunch.banner, :blue]
|
36
|
+
puts Paint[" Munching on packets om nom nom nom...\n", :blue]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Start munching packets and find live nodes...
|
40
|
+
logger.debug("Munching packets now...")
|
41
|
+
Nmunch.munch!(options) do |node|
|
42
|
+
logger.debug("Found node: #{node.ip_address} (#{node.mac_address})")
|
43
|
+
report.publish_node(node)
|
44
|
+
end
|
45
|
+
|
46
|
+
logger.debug("Done, printing summary")
|
47
|
+
puts "\n#{report.summary}"
|
48
|
+
|
49
|
+
rescue Exception => e
|
50
|
+
if !e.is_a?(Interrupt)
|
51
|
+
logger.debug("Caught exception!")
|
52
|
+
logger.debug(e.inspect)
|
53
|
+
puts Paint[" Warp Core Breach!", :red]
|
54
|
+
puts Paint[" [#{e.class}] #{e.message}", :red]
|
55
|
+
else
|
56
|
+
logger.debug("Caught interrupt")
|
57
|
+
puts "\n#{report.summary}"
|
58
|
+
exit!
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
on("-i INTERFACE", "--interface", "Interface to munch packets on")
|
64
|
+
on("-f FILE", "--file", "Path to PCAP file to analyze")
|
65
|
+
on("--no-prefix-lookup", "Disable MAC prefix lookups")
|
66
|
+
on("--no-banner", "Don't display awesome Nmunch banner")
|
67
|
+
on("--no-color", "Don't colorize output")
|
68
|
+
|
69
|
+
description "A passive network discovery tool."
|
70
|
+
version Nmunch::VERSION
|
71
|
+
|
72
|
+
use_log_level_option
|
73
|
+
|
74
|
+
go!
|
75
|
+
end
|
data/lib/nmunch.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require "nmunch/version"
|
2
|
+
require "nmunch/dissectors/base"
|
3
|
+
require "nmunch/dissectors/ip"
|
4
|
+
require "nmunch/dissectors/arp"
|
5
|
+
require "nmunch/subscribers/base"
|
6
|
+
require "nmunch/subscribers/stdout"
|
7
|
+
require "nmunch/node"
|
8
|
+
require "nmunch/report"
|
9
|
+
require "nmunch/mac_oui_lookup"
|
10
|
+
|
11
|
+
# Public: Main Nmunch module
|
12
|
+
#
|
13
|
+
# Acts as a facade in front of the ruby-pcap gem and abstracts away the packet analysis.
|
14
|
+
module Nmunch
|
15
|
+
|
16
|
+
# Internal: PCAP filter expression to only get ARP and broadcast packets
|
17
|
+
CAPTURE_FILTER = 'arp or broadcast'
|
18
|
+
|
19
|
+
# Public: Start munching on packets on interface or from PCAP file
|
20
|
+
#
|
21
|
+
# options - Hash of command line options
|
22
|
+
#
|
23
|
+
# Yields an Nmunch::Node with IP and MAC address information
|
24
|
+
def self.munch!(options)
|
25
|
+
create_capture_object(options).each_packet do |packet|
|
26
|
+
dissector = Nmunch::Dissectors::Base.factory(packet)
|
27
|
+
yield Nmunch::Node.create_from_dissector(dissector)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Internal: Get the awesome Nmunch ASCII banner
|
32
|
+
#
|
33
|
+
# Returns ASCII banner
|
34
|
+
def self.banner
|
35
|
+
banner = <<EOB
|
36
|
+
|
37
|
+
88b 88 8b d8 88 88 88b 88 dP""b8 88 88
|
38
|
+
88Yb88 88b d88 88 88 88Yb88 dP `" 88 88
|
39
|
+
88 Y88 88YbdP88 Y8 8P 88 Y88 Yb 888888
|
40
|
+
88 Y8 88 YY 88 `YbodP' 88 Y8 YboodP 88 88
|
41
|
+
EOB
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Internal: Create a capture object from options
|
47
|
+
#
|
48
|
+
# options - Hash of command line options
|
49
|
+
#
|
50
|
+
# Returns a Pcap::Capture object on interface or a PCAP file
|
51
|
+
def self.create_capture_object(options)
|
52
|
+
if options[:interface]
|
53
|
+
capture = Pcap::Capture.open_live(options[:interface])
|
54
|
+
else
|
55
|
+
capture = Pcap::Capture.open_offline(options[:file])
|
56
|
+
end
|
57
|
+
capture.setfilter(CAPTURE_FILTER)
|
58
|
+
capture
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Nmunch
|
2
|
+
module Dissectors
|
3
|
+
|
4
|
+
# Public: Address Resolution Protocol dissector
|
5
|
+
#
|
6
|
+
# Extracts IP and MAC address from an ARP packet
|
7
|
+
#
|
8
|
+
# See http://en.wikipedia.org/wiki/Address_Resolution_Protocol for protocol details
|
9
|
+
class Arp < Nmunch::Dissectors::Base
|
10
|
+
|
11
|
+
# Public: Get sender IP address from ARP packet
|
12
|
+
#
|
13
|
+
# Returns IP address
|
14
|
+
def ip_address
|
15
|
+
@ip_address ||= IPAddr.new(raw_data[28..31].join.hex, Socket::AF_INET).to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public: Get sender MAC address from ARP packet
|
19
|
+
#
|
20
|
+
# Returns MAC address
|
21
|
+
def mac_address
|
22
|
+
@mac_address ||= raw_data[22..27].join(':')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Nmunch
|
2
|
+
module Dissectors
|
3
|
+
|
4
|
+
# Public: Base dissector class
|
5
|
+
class Base
|
6
|
+
attr_reader :pcap_packet, :raw_data
|
7
|
+
|
8
|
+
# Public: Initializer
|
9
|
+
#
|
10
|
+
# pcap_packet - An instance of Pcap::Packet
|
11
|
+
#
|
12
|
+
# Stores the packet and extracts to raw packet data in a way that makes
|
13
|
+
# it easy to dissect
|
14
|
+
#
|
15
|
+
# Returns nothing
|
16
|
+
def initialize(pcap_packet)
|
17
|
+
@pcap_packet = pcap_packet
|
18
|
+
extract_raw_packet_data
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Dissector factory
|
22
|
+
#
|
23
|
+
# pcap_packet - An instance of Pcap::Packet
|
24
|
+
#
|
25
|
+
# Returns an instance of a dissector
|
26
|
+
def self.factory(pcap_packet)
|
27
|
+
if pcap_packet.ip?
|
28
|
+
Nmunch::Dissectors::Ip.new(pcap_packet)
|
29
|
+
else
|
30
|
+
Nmunch::Dissectors::Arp.new(pcap_packet)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
# Internal: Extract raw packet data and store it in @raw_data
|
37
|
+
#
|
38
|
+
# Unpacks data to HEX and splits it into an array of bytes
|
39
|
+
#
|
40
|
+
# Returns nothing
|
41
|
+
def extract_raw_packet_data
|
42
|
+
@raw_data = pcap_packet.raw_data.unpack('H*').first.scan(/.{2}/)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Nmunch
|
2
|
+
module Dissectors
|
3
|
+
|
4
|
+
# Public: Internet Protocol dissector
|
5
|
+
#
|
6
|
+
# Extracts IP and MAC address from IP packet
|
7
|
+
#
|
8
|
+
# See http://en.wikipedia.org/wiki/Internet_Protocol for protocol details
|
9
|
+
class Ip < Nmunch::Dissectors::Base
|
10
|
+
|
11
|
+
# Public: Get sender IP address from IP packet
|
12
|
+
#
|
13
|
+
# Returns IP address
|
14
|
+
def ip_address
|
15
|
+
@ip_address ||= pcap_packet.ip_src.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public: Get sender MAC address from IP packet
|
19
|
+
#
|
20
|
+
# Returns MAC address
|
21
|
+
def mac_address
|
22
|
+
@mac_address ||= raw_data[6..11].join(':')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|