ruby-nmap 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/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 0.1.0 / 2009-11-13
2
+
3
+ * Initial release.
4
+ * Provides a Ruby interface for running Nmap.
5
+ * Provides a Parser for enumerating Nmap XML scan files.
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,27 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/nmap.rb
6
+ lib/nmap/task.rb
7
+ lib/nmap/program.rb
8
+ lib/nmap/address.rb
9
+ lib/nmap/host.rb
10
+ lib/nmap/os_class.rb
11
+ lib/nmap/os_match.rb
12
+ lib/nmap/os.rb
13
+ lib/nmap/port.rb
14
+ lib/nmap/scanner.rb
15
+ lib/nmap/scan.rb
16
+ lib/nmap/status.rb
17
+ lib/nmap/xml.rb
18
+ lib/nmap/version.rb
19
+ tasks/spec.rb
20
+ tasks/yard.rb
21
+ spec/spec_helper.rb
22
+ spec/helpers/scan.xml
23
+ spec/helpers/xml.rb
24
+ spec/os_spec.rb
25
+ spec/host_spec.rb
26
+ spec/xml_spec.rb
27
+ spec/nmap_spec.rb
data/README.txt ADDED
@@ -0,0 +1,80 @@
1
+ = ruby-nmap
2
+
3
+ * http://github.com/sophsec/ruby-nmap
4
+ * http://github.com/sophsec/ruby-nmap/issues
5
+ * Postmodern (postmodern.mod3 at gmail.com)
6
+
7
+ == DESCRIPTION:
8
+
9
+ A Ruby interface to Nmap, the exploration tool and security / port scanner.
10
+
11
+ == FEATURES:
12
+
13
+ * Provides a Ruby interface for running Nmap.
14
+ * Provides a Parser for enumerating Nmap XML scan files.
15
+
16
+ == EXAMPLES:
17
+
18
+ * Run Nmap from Ruby:
19
+
20
+ require 'nmap/program'
21
+
22
+ Nmap::Program.scan do |nmap|
23
+ nmap.syn_scan = true
24
+ nmap.service_scan = true
25
+ nmap.os_fingerprint = true
26
+ nmap.xml = 'scan.xml'
27
+ nmap.verbose = true
28
+
29
+ nmap.ports = [20,21,22,23,25,80,110,443,512,522,8080,1080]
30
+ nmap.targets = '192.168.1.*'
31
+ end
32
+
33
+ * Parse Nmap XML scan files:
34
+
35
+ require 'nmap/xml'
36
+
37
+ Nmap::XML.new('scan.xml') do |xml|
38
+ xml.each_host do |host|
39
+ puts "[#{host.ip}]"
40
+
41
+ host.each_port do |port|
42
+ puts " #{port.number}/#{port.protocol}\t#{port.state}\t#{port.service}"
43
+ end
44
+ end
45
+ end
46
+
47
+ == REQUIREMENTS:
48
+
49
+ * {nmap}[http://www.insecure.org/]
50
+ * {nokogiri}[http://nokogiri.rubyforge.org/] >= 1.4.0
51
+ * {rprogram}[http://rprogram.rubyforge.org/] >= 0.1.7
52
+
53
+ == INSTALL:
54
+
55
+ $ sudo gem install ruby-nmap
56
+
57
+ == LICENSE:
58
+
59
+ The MIT License
60
+
61
+ Copyright (c) 2009 Hal Brodigan
62
+
63
+ Permission is hereby granted, free of charge, to any person obtaining
64
+ a copy of this software and associated documentation files (the
65
+ 'Software'), to deal in the Software without restriction, including
66
+ without limitation the rights to use, copy, modify, merge, publish,
67
+ distribute, sublicense, and/or sell copies of the Software, and to
68
+ permit persons to whom the Software is furnished to do so, subject to
69
+ the following conditions:
70
+
71
+ The above copyright notice and this permission notice shall be
72
+ included in all copies or substantial portions of the Software.
73
+
74
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
75
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
76
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
77
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
78
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
79
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
80
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './tasks/spec.rb'
6
+ require './tasks/yard.rb'
7
+
8
+ Hoe.spec 'ruby-nmap' do
9
+ self.developer('Postmodern', 'postmodern.mod3@gmail.com')
10
+ self.remote_rdoc_dir = '/'
11
+ self.extra_deps = [
12
+ ['nokogiri', '>=1.4.0'],
13
+ ['rprogram', '>=0.1.7']
14
+ ]
15
+
16
+ self.extra_dev_deps = [
17
+ ['rspec', '>=1.1.12'],
18
+ ['yard', '>=0.2.3.5']
19
+ ]
20
+
21
+ self.spec_extras = {:has_rdoc => 'yard'}
22
+ end
23
+
24
+ # vim: syntax=ruby
@@ -0,0 +1,35 @@
1
+ module Nmap
2
+ class Address
3
+
4
+ # Type of the address
5
+ attr_reader :type
6
+
7
+ # Address
8
+ attr_reader :addr
9
+
10
+ #
11
+ # Creates a new Address object.
12
+ #
13
+ # @param [Symbol] type
14
+ # The type of the address.
15
+ #
16
+ # @param [String] addr
17
+ # The address.
18
+ #
19
+ def initialize(type,addr)
20
+ @type = type
21
+ @addr = addr
22
+ end
23
+
24
+ #
25
+ # Converts the address to a String.
26
+ #
27
+ # @return [String]
28
+ # The address.
29
+ #
30
+ def to_s
31
+ @addr.to_s
32
+ end
33
+
34
+ end
35
+ end
data/lib/nmap/host.rb ADDED
@@ -0,0 +1,368 @@
1
+ require 'nmap/status'
2
+ require 'nmap/address'
3
+ require 'nmap/os'
4
+ require 'nmap/port'
5
+
6
+ require 'nokogiri'
7
+ require 'enumerator'
8
+
9
+ module Nmap
10
+ class Host
11
+
12
+ include Enumerable
13
+
14
+ #
15
+ # Creates a new Host object.
16
+ #
17
+ # @param [Nokogiri::XML::Node] node
18
+ # The XML node that contains the host information.
19
+ #
20
+ # @yield [host]
21
+ # If a block is given, it will be passed the newly created Host
22
+ # object.
23
+ #
24
+ # @yieldparam [Host] host
25
+ # The newly created Host object.
26
+ #
27
+ def initialize(node,&block)
28
+ @node = node
29
+
30
+ block.call(self) if block
31
+ end
32
+
33
+ #
34
+ # Parses the status of the host.
35
+ #
36
+ # @return [Status]
37
+ # The status of the host.
38
+ #
39
+ def status
40
+ status = @node.at('status')
41
+
42
+ return Status.new(
43
+ status['state'].to_sym,
44
+ status['reason']
45
+ )
46
+ end
47
+
48
+ #
49
+ # Parses each address of the host.
50
+ #
51
+ # @yield [addr]
52
+ # Each parsed address will be pass to a given block.
53
+ #
54
+ # @yieldparam [Address] addr
55
+ # A address of the host.
56
+ #
57
+ # @return [Host]
58
+ # The host.
59
+ #
60
+ def each_address(&block)
61
+ @node.xpath("address[@addr]").each do |addr|
62
+ address = Address.new(
63
+ addr['addrtype'].to_sym,
64
+ addr['addr']
65
+ )
66
+
67
+ block.call(address) if block
68
+ end
69
+
70
+ return self
71
+ end
72
+
73
+ #
74
+ # Parses the addresses of the host.
75
+ #
76
+ # @return [Array<Host>]
77
+ # The addresses of the host.
78
+ #
79
+ def addresses
80
+ Enumerator.new(self,:each_address).to_a
81
+ end
82
+
83
+ #
84
+ # Parses the MAC address of the host.
85
+ #
86
+ # @return [String]
87
+ # The MAC address of the host.
88
+ #
89
+ def mac
90
+ unless @mac
91
+ addr = @node.xpath("address[@addr][@addrtype='mac']").first
92
+
93
+ @mac = addr['addr'] if addr
94
+ end
95
+
96
+ return @mac
97
+ end
98
+
99
+ #
100
+ # Parses the IPv4 address of the host.
101
+ #
102
+ # @return [String]
103
+ # The IPv4 address of the host.
104
+ #
105
+ def ipv4
106
+ unless @ipv4
107
+ addr = @node.xpath("address[@addr][@addrtype='ipv4']").first
108
+
109
+ @ipv4 = addr['addr'] if addr
110
+ end
111
+
112
+ return @ipv4
113
+ end
114
+
115
+ #
116
+ # Parses the IPv6 address of the host.
117
+ #
118
+ # @return [String]
119
+ # The IPv6 address of the host.
120
+ #
121
+ def ipv6
122
+ unless @ipv6
123
+ addr = @node.xpath("address[@addr][@addrtype='ipv6']").first
124
+
125
+ @ipv6 = addr['addr'] if addr
126
+ end
127
+
128
+ return @ipv6
129
+ end
130
+
131
+ #
132
+ # The IP address of the host.
133
+ #
134
+ # @return [String]
135
+ # The IPv4 or IPv6 address of the host.
136
+ #
137
+ def ip
138
+ ipv6 || ipv4
139
+ end
140
+
141
+ #
142
+ # The address of the host.
143
+ #
144
+ # @return [String]
145
+ # The IP or MAC address of the host.
146
+ #
147
+ def address
148
+ ip || mac
149
+ end
150
+
151
+ #
152
+ # Parses the hostnames of the host.
153
+ #
154
+ # @yield [host]
155
+ # Each parsed hostname will be passed to the given block.
156
+ #
157
+ # @yieldparam [String] host
158
+ # A hostname of the host.
159
+ #
160
+ # @return [Host]
161
+ # The host.
162
+ #
163
+ def each_hostname(&block)
164
+ @node.xpath("hostnames/hostname[@name]").each do |host|
165
+ block.call(host['name']) if block
166
+ end
167
+
168
+ return self
169
+ end
170
+
171
+ #
172
+ # Parses the hostnames of the host.
173
+ #
174
+ # @return [Array<String>]
175
+ # The hostnames of the host.
176
+ #
177
+ def hostnames
178
+ Enumerator.new(self,:each_hostname).to_a
179
+ end
180
+
181
+ #
182
+ # Parses the OS guessing information of the host.
183
+ #
184
+ # @yield [os]
185
+ # If a block is given, it will be passed the OS guessing information.
186
+ #
187
+ # @yieldparam [OS] os
188
+ # The OS guessing information.
189
+ #
190
+ # @return [OS]
191
+ # The OS guessing information.
192
+ #
193
+ def os(&block)
194
+ os = @node.at('os')
195
+
196
+ return OS.new(os,&block) if os
197
+ end
198
+
199
+ #
200
+ # Parses the scanned ports of the host.
201
+ #
202
+ # @yield [port]
203
+ # Each scanned port of the host.
204
+ #
205
+ # @yieldparam [Port] port
206
+ # A scanned port of the host.
207
+ #
208
+ # @return [Host]
209
+ # The host.
210
+ #
211
+ def each_port(&block)
212
+ @node.xpath("ports/port").each do |port|
213
+ block.call(create_port(port)) if block
214
+ end
215
+
216
+ return self
217
+ end
218
+
219
+ #
220
+ # Parses the scanned ports of the host.
221
+ #
222
+ # @return [Array<Port>]
223
+ # The scanned ports of the host.
224
+ #
225
+ def ports
226
+ Enumerator.new(self,:each_port).to_a
227
+ end
228
+
229
+ #
230
+ # Parses the open ports of the host.
231
+ #
232
+ # @yield [port]
233
+ # Each open port of the host.
234
+ #
235
+ # @yieldparam [Port] port
236
+ # An open scanned port of the host.
237
+ #
238
+ # @return [Host]
239
+ # The host.
240
+ #
241
+ def each_open_port(&block)
242
+ @node.xpath("ports/port[state/@state='open']").each do |port|
243
+ block.call(create_port(port)) if block
244
+ end
245
+
246
+ return self
247
+ end
248
+
249
+ #
250
+ # Parses the open ports of the host.
251
+ #
252
+ # @return [Array<Port>]
253
+ # The open ports of the host.
254
+ #
255
+ def open_ports
256
+ Enumerator.new(self,:each_open_port).to_a
257
+ end
258
+
259
+ #
260
+ # Parses the TCP ports of the host.
261
+ #
262
+ # @yield [port]
263
+ # Each TCP port of the host.
264
+ #
265
+ # @yieldparam [Port] port
266
+ # An TCP scanned port of the host.
267
+ #
268
+ # @return [Host]
269
+ # The host.
270
+ #
271
+ def each_tcp_port(&block)
272
+ @node.xpath("ports/port[@protocol='tcp']").each do |port|
273
+ block.call(create_port(port)) if block
274
+ end
275
+
276
+ return self
277
+ end
278
+
279
+ #
280
+ # Parses the TCP ports of the host.
281
+ #
282
+ # @return [Array<Port>]
283
+ # The TCP ports of the host.
284
+ #
285
+ def tcp_ports
286
+ Enumerator.new(self,:each_tcp_port).to_a
287
+ end
288
+
289
+ #
290
+ # Parses the UDP ports of the host.
291
+ #
292
+ # @yield [port]
293
+ # Each UDP port of the host.
294
+ #
295
+ # @yieldparam [Port] port
296
+ # An UDP scanned port of the host.
297
+ #
298
+ # @return [Host]
299
+ # The host.
300
+ #
301
+ def each_udp_port(&block)
302
+ @node.xpath("ports/port[@protocol='udp']").each do |port|
303
+ block.call(create_port(port)) if block
304
+ end
305
+
306
+ return self
307
+ end
308
+
309
+ #
310
+ # Parses the UDP ports of the host.
311
+ #
312
+ # @return [Array<Port>]
313
+ # The UDP ports of the host.
314
+ #
315
+ def udp_ports
316
+ Enumerator.new(self,:each_udp_port).to_a
317
+ end
318
+
319
+ #
320
+ # Parses the open ports of the host.
321
+ #
322
+ # @see each_open_port
323
+ #
324
+ def each(&block)
325
+ each_open_port(&block)
326
+ end
327
+
328
+ #
329
+ # Converts the host to a String.
330
+ #
331
+ # @return [String]
332
+ # The address of the host.
333
+ #
334
+ # @see address
335
+ #
336
+ def to_s
337
+ address.to_s
338
+ end
339
+
340
+ protected
341
+
342
+ #
343
+ # Creates a new Port object around a node.
344
+ #
345
+ # @param [Nokogiri::XML::Node] node
346
+ # The node to create the Port object around.
347
+ #
348
+ # @return [Port]
349
+ # The port object.
350
+ #
351
+ def create_port(node)
352
+ state = node.at('state')
353
+
354
+ if (service = node.at('service/@name'))
355
+ service = service.inner_text
356
+ end
357
+
358
+ return Port.new(
359
+ node['protocol'].to_sym,
360
+ node['portid'].to_i,
361
+ state['state'].to_sym,
362
+ state['reason'],
363
+ service
364
+ )
365
+ end
366
+
367
+ end
368
+ end
data/lib/nmap/os.rb ADDED
@@ -0,0 +1,131 @@
1
+ require 'nmap/os_class'
2
+ require 'nmap/os_match'
3
+
4
+ require 'enumerator'
5
+
6
+ module Nmap
7
+ class OS
8
+
9
+ include Enumerable
10
+
11
+ #
12
+ # Creates a new OS object.
13
+ #
14
+ # @param [Nokogiri::XML::Node] node
15
+ # The node that contains the OS guessing information.
16
+ #
17
+ # @yield [os]
18
+ # If a block is given, it will passed the newly created OS object.
19
+ #
20
+ # @yieldparam [OS] os
21
+ # The newly created OS object.
22
+ #
23
+ def initialize(node,&block)
24
+ @node = node
25
+
26
+ block.call(self) if block
27
+ end
28
+
29
+ #
30
+ # Parses the OS class information.
31
+ #
32
+ # @yield [class]
33
+ # Passes each OS class to the given block.
34
+ #
35
+ # @yieldparam [OSClass] class
36
+ # The OS class information.
37
+ #
38
+ # @return [OS]
39
+ # The OS information.
40
+ #
41
+ def each_class(&block)
42
+ @node.xpath("osclass").map do |osclass|
43
+ os_class = OSClass.new(
44
+ osclass['type'].to_sym,
45
+ osclass['vendor'],
46
+ osclass['osfamily'].to_sym,
47
+ osclass['accuracy'].to_i
48
+ )
49
+
50
+ block.call(os_class) if block
51
+ end
52
+
53
+ return self
54
+ end
55
+
56
+ #
57
+ # Parses the OS class information.
58
+ #
59
+ # @return [Array<OSClass>]
60
+ # The OS class information.
61
+ #
62
+ def classes
63
+ Enumerator.new(self,:each_class).to_a
64
+ end
65
+
66
+ #
67
+ # Parses the OS match information.
68
+ #
69
+ # @yield [match]
70
+ # Passes each OS match to the given block.
71
+ #
72
+ # @yieldparam [OSMatch] class
73
+ # The OS match information.
74
+ #
75
+ # @return [OS]
76
+ # The OS information.
77
+ #
78
+ def each_match(&block)
79
+ @node.xpath("osmatch").map do |osclass|
80
+ os_match = OSMatch.new(
81
+ osclass['name'],
82
+ osclass['accuracy'].to_i
83
+ )
84
+
85
+ block.call(os_match) if block
86
+ end
87
+
88
+ return self
89
+ end
90
+
91
+ #
92
+ # Parses the OS match information.
93
+ #
94
+ # @return [Array<OSMatch>]
95
+ # The OS match information.
96
+ #
97
+ def matches
98
+ Enumerator.new(self,:each_match).to_a
99
+ end
100
+
101
+ #
102
+ # Parses the ports used for guessing the OS.
103
+ #
104
+ # @return [Array<Integer>]
105
+ # The ports used.
106
+ #
107
+ def ports_used
108
+ @node.xpath("portused/@portid").map { |port| port.inner_text.to_i }
109
+ end
110
+
111
+ #
112
+ # Parses the OS fingerprint used by Nmap.
113
+ #
114
+ # @return [String]
115
+ # The OS fingerprint.
116
+ #
117
+ def fingerprint
118
+ @node.at("osfingerprint/@fingerprint").inner_text
119
+ end
120
+
121
+ #
122
+ # Parses the OS match information.
123
+ #
124
+ # @see each_match
125
+ #
126
+ def each(&block)
127
+ each_match(&block)
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,49 @@
1
+ module Nmap
2
+ class OSClass
3
+
4
+ # The OS class
5
+ attr_reader :type
6
+
7
+ # The OS vendor
8
+ attr_reader :vendor
9
+
10
+ # The family of the OS
11
+ attr_reader :family
12
+
13
+ # The accuracy of the OS guess
14
+ attr_reader :accuracy
15
+
16
+ #
17
+ # Creates a new OSClass object.
18
+ #
19
+ # @param [Symbol] type
20
+ # The class of the OS.
21
+ #
22
+ # @param [String] vendor
23
+ # The vendor of the OS.
24
+ #
25
+ # @param [String] family
26
+ # The family of the OS.
27
+ #
28
+ # @param [Integer] accuracy
29
+ # The accuracy of the OS guess.
30
+ #
31
+ def initialize(type,vendor,family,accuracy)
32
+ @type = type
33
+ @vendor = vendor
34
+ @family = family
35
+ @accuracy = accuracy
36
+ end
37
+
38
+ #
39
+ # Converts the OS class to a String.
40
+ #
41
+ # @return [String]
42
+ # The String form of the OS class.
43
+ #
44
+ def to_s
45
+ "#{@type} #{@vendor} (#{@accuracy}%)"
46
+ end
47
+
48
+ end
49
+ end