unified2 0.4.0 → 0.5.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.
Files changed (44) hide show
  1. data/ChangeLog.rdoc +6 -0
  2. data/LICENSE.txt +1 -1
  3. data/README.md +72 -0
  4. data/example/{basic-example.rb → example.rb} +3 -2
  5. data/example/seeds/{unified2 → unified2.log} +0 -0
  6. data/gemspec.yml +2 -0
  7. data/lib/unified2/classification.rb +17 -3
  8. data/lib/unified2/config_file.rb +34 -10
  9. data/lib/unified2/constructor/construct.rb +83 -0
  10. data/lib/unified2/constructor/event_ip4.rb +47 -0
  11. data/lib/unified2/constructor/event_ip6.rb +44 -0
  12. data/lib/unified2/constructor/packet.rb +30 -0
  13. data/lib/unified2/constructor/primitive/ipv4.rb +31 -0
  14. data/lib/unified2/{primitive.rb → constructor/primitive.rb} +0 -0
  15. data/lib/unified2/constructor/record_header.rb +17 -0
  16. data/lib/unified2/constructor.rb +1 -0
  17. data/lib/unified2/core_ext/string.rb +10 -2
  18. data/lib/unified2/event.rb +250 -100
  19. data/lib/unified2/exceptions/file_not_found.rb +6 -3
  20. data/lib/unified2/exceptions/file_not_readable.rb +6 -3
  21. data/lib/unified2/exceptions/unknown_load_type.rb +6 -3
  22. data/lib/unified2/payload.rb +82 -13
  23. data/lib/unified2/protocol.rb +141 -0
  24. data/lib/unified2/sensor.rb +22 -0
  25. data/lib/unified2/signature.rb +28 -4
  26. data/lib/unified2/version.rb +2 -2
  27. data/lib/unified2.rb +84 -13
  28. data/spec/event_spec.rb +112 -0
  29. data/spec/spec_helper.rb +45 -1
  30. data/spec/unified2_spec.rb +87 -1
  31. metadata +45 -25
  32. data/README.rdoc +0 -60
  33. data/Rakefile.compiled.rbc +0 -775
  34. data/example/connect.rb +0 -20
  35. data/example/models.rb +0 -194
  36. data/example/mysql-example.rb +0 -73
  37. data/example/search.rb +0 -14
  38. data/example/untitled.rb +0 -31
  39. data/lib/unified2/construct.rb +0 -54
  40. data/lib/unified2/event_ip4.rb +0 -26
  41. data/lib/unified2/event_ip6.rb +0 -23
  42. data/lib/unified2/packet.rb +0 -16
  43. data/lib/unified2/primitive/ipv4.rb +0 -19
  44. data/lib/unified2/record_header.rb +0 -10
@@ -1,196 +1,345 @@
1
1
  require 'unified2/classification'
2
2
  require 'unified2/payload'
3
+ require 'unified2/protocol'
3
4
  require 'unified2/sensor'
4
5
  require 'unified2/signature'
5
6
 
7
+ require 'packetfu'
6
8
  require 'ipaddr'
7
9
  require 'json'
8
10
 
9
11
  module Unified2
10
-
12
+ #
13
+ # Event
14
+ #
11
15
  class Event
12
-
13
- attr_accessor :id, :metadata, :packet
14
16
 
17
+ attr_accessor :id, :event_data, :packet_data
18
+ #
19
+ # Initialize event
20
+ #
21
+ # @param [Integer] id Event id
22
+ #
15
23
  def initialize(id)
16
- @id = id
24
+ @id = id.to_i
17
25
  end
18
26
 
27
+ #
28
+ # Packet Time
29
+ #
30
+ # Time of creation for the unified2 packet.
31
+ #
32
+ # @return [Time, nil] Packet time object
33
+ #
19
34
  def packet_time
20
- if @packet.has_key?(:packet_second)
21
- @packet[:packet_second]
22
- @timestamp = Time.at(@packet[:packet_second].to_i)
35
+ if @packet_data.has_key?(:packet_second)
36
+ @packet_data[:packet_second]
37
+ @timestamp = Time.at(@packet_data[:packet_second].to_i)
23
38
  end
24
39
  end
25
-
40
+
41
+ #
42
+ # Checksum
43
+ #
44
+ # Create a unique checksum for each event
45
+ # using the ip source, destination, signature id,
46
+ # generator id, sensor id, severity id, and the
47
+ # classification id.
48
+ #
49
+ # @return [String] Event checksum
50
+ #
26
51
  def checksum
27
52
  checkdum = [ip_source, ip_destination, signature.id, signature.generator, sensor.id, severity, classification.id]
28
53
  Digest::MD5.hexdigest(checkdum.join(''))
29
54
  end
30
55
 
31
- def uid
32
- "#{sensor.id}.#{@id}"
33
- end
34
-
56
+ #
57
+ # Event Time
58
+ #
59
+ # The event timestamp created by unified2.
60
+ #
61
+ # @return [Time, nil] Event time object
62
+ #
35
63
  def event_time
36
- if @packet.has_key?(:event_second)
37
- @timestamp = Time.at(@packet[:event_second].to_i)
64
+ if @packet_data.has_key?(:event_second)
65
+ @timestamp = Time.at(@packet_data[:event_second].to_i)
38
66
  end
39
67
  end
40
68
  alias :timestamp :event_time
41
69
 
70
+ #
71
+ # Microseconds
72
+ #
73
+ # The event time in microseconds.
74
+ #
75
+ # @return [String, nil] Event microseconds
76
+ #
42
77
  def microseconds
43
- if @metadata.has_key?(:event_microsecond)
44
- @microseconds = @metadata[:event_microsecond]
78
+ if @event_data.has_key?(:event_microsecond)
79
+ @microseconds = @event_data[:event_microsecond]
45
80
  end
46
81
  end
47
82
 
83
+ #
84
+ # Sensor
85
+ #
86
+ # @return [Sensor] Sensor object
87
+ #
48
88
  def sensor
49
89
  @sensor ||= Unified2.sensor
50
90
  end
51
91
 
92
+ #
93
+ # Packet Action
94
+ #
95
+ # @return [Integer, nil] Packet action
96
+ #
52
97
  def packet_action
53
- if @metadata.has_key?(:event_second)
54
- @packet_action = @metadata[:packet_action]
98
+ if @event_data.has_key?(:event_second)
99
+ @packet_data_action = @event_data[:packet_action]
55
100
  end
56
101
  end
57
102
 
103
+ #
104
+ # Protocol
105
+ #
106
+ # @return [Protocol] Event protocol object
107
+ #
58
108
  def protocol
59
- if @metadata.has_key?(:protocol)
60
- @protocol = determine_protocol(@metadata[:protocol])
61
- end
62
- end
63
-
64
- def icmp?
65
- return true if protocol == :ICMP
66
- false
67
- end
68
-
69
- def tcp?
70
- return true if protocol == :TCP
71
- false
109
+ @protocol = Protocol.new(determine_protocol(@event_data[:protocol]), packet)
72
110
  end
73
111
 
74
- def udp?
75
- return true if protocol == :UDP
76
- false
77
- end
78
112
 
113
+ #
114
+ # Classification
115
+ #
116
+ # @return [Classification] Event classification object
117
+ #
79
118
  def classification
80
- if @metadata.is_a?(Hash)
81
- @classification = Classification.new(@metadata[:classification]) if @metadata[:classification]
82
- end
119
+ @classification = Classification.new(@event_data[:classification]) if @event_data[:classification]
83
120
  end
84
121
 
122
+ #
123
+ # Signature
124
+ #
125
+ # @return [Signature, nil] Event signature object
126
+ #
85
127
  def signature
86
- if @metadata.is_a?(Hash)
87
- @signature = Signature.new(@metadata[:signature])
88
- end
89
- end
90
-
91
- def generator_id
92
- if @metadata.is_a?(Hash)
93
- @metadata[:generator_id] if @metadata.has_key?(:generator_id)
128
+ if @event_data.is_a?(Hash)
129
+ @signature = Signature.new(@event_data[:signature])
94
130
  end
95
131
  end
96
132
 
133
+ #
134
+ # Source IP Address
135
+ #
136
+ # @return [IPAddr] Event source ip address
137
+ #
97
138
  def ip_source
98
- if @metadata.is_a?(Hash)
99
- @metadata[:ip_source] if @metadata.has_key?(:ip_source)
139
+ if @event_data.is_a?(Hash)
140
+ @event_data[:ip_source] if @event_data.has_key?(:ip_source)
100
141
  end
101
142
  end
102
143
  alias :source_ip :ip_source
103
144
 
104
- # Add ICMP type
145
+ #
146
+ # Source Port
147
+ #
148
+ # @return [Integer] Event source port
149
+ #
150
+ # @note
151
+ # Event#source_port will return zero if the
152
+ # event protocol is icmp.
153
+ #
105
154
  def source_port
106
- return 0 if icmp?
107
- @source_port = @metadata[:sport_itype] if @metadata.has_key?(:sport_itype)
155
+ return 0 if protocol.icmp?
156
+ @source_port = @event_data[:sport_itype] if @event_data.has_key?(:sport_itype)
108
157
  end
109
158
 
159
+ #
160
+ # Destination IP Address
161
+ #
162
+ # @return [IPAddr] Event destination ip address
163
+ #
110
164
  def ip_destination
111
- if @metadata.is_a?(Hash)
112
- @metadata[:ip_destination] if @metadata.has_key?(:ip_destination)
165
+ if @event_data.is_a?(Hash)
166
+ @event_data[:ip_destination] if @event_data.has_key?(:ip_destination)
113
167
  end
114
168
  end
115
169
  alias :destination_ip :ip_destination
116
170
 
117
- # Add ICMP code
171
+ #
172
+ # Destination Port
173
+ #
174
+ # @return [Integer] Event destination port
175
+ #
176
+ # @note
177
+ # Event#destination_port will return zero if the
178
+ # event protocol is icmp.
179
+ #
118
180
  def destination_port
119
- return 0 if icmp?
120
- @source_port = @metadata[:dport_icode] if @metadata.has_key?(:dport_icode)
181
+ return 0 if protocol.icmp?
182
+ @source_port = @event_data[:dport_icode] if @event_data.has_key?(:dport_icode)
121
183
  end
122
184
 
185
+ #
186
+ # Severity
187
+ #
188
+ # @return [Integer] Event severity id
189
+ #
123
190
  def severity
124
- @severity = @metadata[:priority_id] if @metadata.has_key?(:priority_id)
191
+ @severity = @event_data[:priority_id].to_i
125
192
  end
126
193
 
127
- def payload
128
- if @packet.is_a?(Hash)
129
- Payload.new(@packet)
130
- else
131
- Payload.new
132
- end
194
+ #
195
+ # Packet
196
+ #
197
+ # @return [Packet] Event packet object
198
+ #
199
+ # @note
200
+ # Please view the packetfu documentation for more
201
+ # information. (http://code.google.com/p/packetfu/)
202
+ #
203
+ def packet
204
+ @packet = PacketFu::Packet.parse(@packet_data[:packet])
133
205
  end
134
206
 
207
+ #
208
+ # Payload
209
+ #
210
+ # @return [Payload] Event payload object
211
+ #
212
+ def payload
213
+ Payload.new(packet.payload, @packet_data)
214
+ end
215
+
216
+ #
217
+ # Load
218
+ #
219
+ # Initializes the raw data returned by
220
+ # bindata into a more comfurtable format.
221
+ #
222
+ # @param [Hash] Name Description
223
+ #
224
+ # @return [nil]
225
+ #
135
226
  def load(event)
136
227
  if event.data.respond_to?(:signature_id)
137
- @metadata ||= build_event_metadata(event)
228
+ @event_data ||= build_event_data(event)
138
229
  end
139
230
 
140
231
  if event.data.respond_to?(:packet_data)
141
- @packet ||= build_packet_metadata(event)
232
+ @packet_data ||= build_packet_data(event)
142
233
  end
143
234
  end
144
235
 
236
+ #
237
+ # Convert To Hash
238
+ #
239
+ # @return [Hash] Event hash object
240
+ #
145
241
  def to_h
146
- if @metadata.is_a?(Hash)
147
- if @packet.is_a?(Hash)
148
- data = {}
149
- data.merge!(@metadata)
150
- data.merge!(@packet)
151
- return data
152
- end
153
- else
154
- if @packet.is_a?(Hash)
155
- return @packet
156
- end
242
+ @to_hash = {}
243
+
244
+ [@event_data, @packet_data].each do |hash|
245
+ @to_hash.merge!(hash) if hash.is_a?(Hash)
157
246
  end
247
+
248
+ @to_hash
158
249
  end
159
250
 
251
+ #
252
+ # Convert To Integer
253
+ #
254
+ # @return [Integer] Event id
255
+ #
160
256
  def to_i
161
257
  @id.to_i
162
258
  end
163
-
259
+
260
+ #
261
+ # Convert To Json
262
+ #
263
+ # @return [String] Event hash in json format
264
+ #
164
265
  def json
165
266
  to_h.to_json
166
267
  end
167
268
 
168
- def to_s
169
- data = %{
170
- #############################################################################
171
- # Sensor: #{sensor.id}
172
- # Event ID: #{id}
173
- # Timestamp: #{timestamp}
174
- # Severity: #{severity}
175
- # Protocol: #{protocol}
176
- # Source IP: #{source_ip}:#{source_port}
177
- # Destination IP: #{destination_ip}:#{destination_port}
178
- # Signature: #{signature.name}
179
- # Classification: #{classification.name}
180
- # Payload:
181
-
182
- }
183
- if payload.blank?
184
- data + '#############################################################################'
269
+ #
270
+ # Ethernet Header
271
+ #
272
+ # @return [Hash] Ethernet header
273
+ #
274
+ def eth_header
275
+ if ((packet.is_eth?) && packet.has_data?)
276
+ @ip_header = {
277
+ :v => payload.packet.ip_header.ip_v,
278
+ :hl => payload.packet.ip_header.ip_hl,
279
+ :tos => payload.packet.ip_header.ip_tos,
280
+ :len => payload.packet.ip_header.ip_len,
281
+ :id => payload.packet.ip_header.ip_id,
282
+ :frag => payload.packet.ip_header.ip_frag,
283
+ :ttl => payload.packet.ip_header.ip_ttl,
284
+ :proto => payload.packet.ip_header.ip_proto,
285
+ :sum => payload.packet.ip_header.ip_sum
286
+ }
185
287
  else
288
+ @ip_header = {}
289
+ end
290
+ end
291
+
292
+ #
293
+ # IP Header
294
+ #
295
+ # @return [Hash] IP header
296
+ #
297
+ def ip_header
298
+ if ((packet.is_ip?) && packet.has_data?)
299
+ @ip_header = {
300
+ :v => packet.ip_header.ip_v,
301
+ :hl => packet.ip_header.ip_hl,
302
+ :tos => packet.ip_header.ip_tos,
303
+ :len => packet.ip_header.ip_len,
304
+ :id => packet.ip_header.ip_id,
305
+ :frag => packet.ip_header.ip_frag,
306
+ :ttl => packet.ip_header.ip_ttl,
307
+ :proto => packet.ip_header.ip_proto,
308
+ :sum => packet.ip_header.ip_sum
309
+ }
310
+ else
311
+ @ip_header = {}
312
+ end
313
+ end
314
+
315
+ #
316
+ # Convert To String
317
+ #
318
+ # @return [String] Event string object
319
+ #
320
+ def to_s
321
+ data = %{
322
+ Sensor: #{sensor.id}
323
+ Event ID: #{id}
324
+ Timestamp: #{timestamp.strftime('%D %H:%M:%S')}
325
+ Severity: #{severity}
326
+ Protocol: #{protocol}
327
+ Source IP: #{source_ip}:#{source_port}
328
+ Destination IP: #{destination_ip}:#{destination_port}
329
+ Signature: #{signature.name}
330
+ Classification: #{classification.name}
331
+ }
332
+ unless payload.blank?
333
+ data += "Payload:\n"
186
334
  payload.dump(:width => 30, :output => data)
187
- data + "#############################################################################"
188
335
  end
336
+
337
+ data.gsub(/^\s+/, "")
189
338
  end
190
339
 
191
340
  private
192
341
 
193
- def build_event_metadata(event)
342
+ def build_event_data(event)
194
343
  @event_hash = {}
195
344
 
196
345
  @event_hash = {
@@ -220,13 +369,13 @@ data = %{
220
369
  @event_hash
221
370
  end
222
371
 
223
- def build_packet_metadata(event)
372
+ def build_packet_data(event)
224
373
  @packet_hash = {}
225
374
  @packet_hash = {
226
375
  :linktype => event.data.linktype,
227
376
  :packet_microsecond => event.data.packet_microsecond,
228
377
  :packet_second => event.data.packet_second,
229
- :payload => event.data.packet_data,
378
+ :packet => event.data.packet_data,
230
379
  :event_second => event.data.event_second,
231
380
  :packet_length => event.data.packet_length
232
381
  }
@@ -325,5 +474,6 @@ data = %{
325
474
  end
326
475
  end
327
476
 
328
- end
329
- end
477
+ end # class Event
478
+
479
+ end # module Unified2
@@ -1,4 +1,7 @@
1
1
  module Unified2
2
- class FileNotFound < StandardError
3
- end
4
- end
2
+ #
3
+ # File Not Found
4
+ #
5
+ class FileNotFound < StandardError; end # class FileNotFound
6
+
7
+ end # module Unified2
@@ -1,4 +1,7 @@
1
1
  module Unified2
2
- class FileNotReadable < StandardError
3
- end
4
- end
2
+ #
3
+ # File Not Readable
4
+ #
5
+ class FileNotReadable < StandardError; end # class FileNotReadable
6
+
7
+ end # module Unified2
@@ -1,4 +1,7 @@
1
1
  module Unified2
2
- class UnknownLoadType < StandardError
3
- end
4
- end
2
+ #
3
+ # Unknown Load Type
4
+ #
5
+ class UnknownLoadType < StandardError; end # class UnknownLoadType
6
+
7
+ end # module Unified2
@@ -1,34 +1,103 @@
1
1
  require 'hexdump'
2
2
 
3
3
  module Unified2
4
+ #
5
+ # Payload
6
+ #
4
7
  class Payload
5
-
6
- attr_accessor :linktype, :length
7
-
8
- def initialize(payload={})
9
- @payload = payload[:payload]
10
- @length = payload[:packet_length].to_i
11
- @linktype = payload[:linktype]
8
+
9
+ attr_accessor :linktype, :length, :packet
10
+
11
+ #
12
+ # Initialize payload object
13
+ #
14
+ # @param [String] raw Raw binary payload
15
+ # @param [Hash] packet Packet attributes
16
+ #
17
+ # @option packet [String] :packet Packet
18
+ # @option packet [Integer] :packet_length Packet length
19
+ # @option packet [Integer] :linktype Packet linktype
20
+ #
21
+ def initialize(raw, packet={})
22
+ @packet = raw
23
+ @length = packet[:packet_length].to_i
24
+ @linktype = packet[:linktype]
12
25
  end
13
26
 
27
+ #
28
+ # Blank?
29
+ #
30
+ # @return [true, false] Check is payload is blank
31
+ #
14
32
  def blank?
15
- return true unless @payload
33
+ return true unless @packet
16
34
  false
17
35
  end
18
36
 
37
+ #
38
+ # Raw
39
+ #
40
+ # @return [String] Raw binary payload
41
+ #
19
42
  def raw
20
- @payload.to_s
43
+ @packet
21
44
  end
22
-
45
+
46
+ #
47
+ # Hex
48
+ #
49
+ # @return [String] Convert payload to hex
50
+ #
23
51
  def hex
24
- @hex = @payload.to_s.unpack('H*')
52
+ @hex = @packet.to_s.unpack('H*')
25
53
  return @hex.first if @hex
26
54
  nil
27
55
  end
28
56
 
57
+ #
58
+ # Dump
59
+ #
60
+ # @param [options] options Hash of options for Hexdump#dump
61
+ #
62
+ # @option options [Integer] :width (16)
63
+ # The number of bytes to dump for each line.
64
+ #
65
+ # @option options [Symbol, Integer] :base (:hexadecimal)
66
+ # The base to print bytes in. Supported bases include, `:hexadecimal`,
67
+ # `:hex`, `16, `:decimal`, `:dec`, `10, `:octal`, `:oct`, `8`,
68
+ # `:binary`, `:bin` and `2`.
69
+ #
70
+ # @option options [Boolean] :ascii (false)
71
+ # Print ascii characters when possible.
72
+ #
73
+ # @option options [#<<] :output (STDOUT)
74
+ # The output to print the hexdump to.
75
+ #
76
+ # @yield [index,hex_segment,print_segment]
77
+ # The given block will be passed the hexdump break-down of each segment.
78
+ #
79
+ # @yieldparam [Integer] index
80
+ # The index of the hexdumped segment.
81
+ #
82
+ # @yieldparam [Array<String>] hex_segment
83
+ # The hexadecimal-byte representation of the segment.
84
+ #
85
+ # @yieldparam [Array<String>] print_segment
86
+ # The print-character representation of the segment.
87
+ #
88
+ # @return [nil]
89
+ #
90
+ # @raise [ArgumentError]
91
+ # The given data does not define the `#each_byte` method, or
92
+ #
93
+ # @note
94
+ # Please view the hexdump documentation for more
95
+ # information. Hexdump is a great lib by @postmodern.
96
+ # (http://github.com/postmodern/hexdump)
97
+ #
29
98
  def dump(options={})
30
- Hexdump.dump(@payload, options)
99
+ Hexdump.dump(@packet, options)
31
100
  end
32
101
 
33
102
  end
34
- end
103
+ end