nmunch 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![OM NOM NOM NOM](https://dl.dropbox.com/u/70881769/omnomnomnom.jpg)
|
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
|
+
![Nmunch munching](https://dl.dropbox.com/u/70881769/nmunch.png "Nmunch munching packets on a network")
|
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
|