ruby-nmap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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