unified2 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog.rdoc +6 -0
- data/LICENSE.txt +1 -1
- data/README.md +72 -0
- data/example/{basic-example.rb → example.rb} +3 -2
- data/example/seeds/{unified2 → unified2.log} +0 -0
- data/gemspec.yml +2 -0
- data/lib/unified2/classification.rb +17 -3
- data/lib/unified2/config_file.rb +34 -10
- data/lib/unified2/constructor/construct.rb +83 -0
- data/lib/unified2/constructor/event_ip4.rb +47 -0
- data/lib/unified2/constructor/event_ip6.rb +44 -0
- data/lib/unified2/constructor/packet.rb +30 -0
- data/lib/unified2/constructor/primitive/ipv4.rb +31 -0
- data/lib/unified2/{primitive.rb → constructor/primitive.rb} +0 -0
- data/lib/unified2/constructor/record_header.rb +17 -0
- data/lib/unified2/constructor.rb +1 -0
- data/lib/unified2/core_ext/string.rb +10 -2
- data/lib/unified2/event.rb +250 -100
- data/lib/unified2/exceptions/file_not_found.rb +6 -3
- data/lib/unified2/exceptions/file_not_readable.rb +6 -3
- data/lib/unified2/exceptions/unknown_load_type.rb +6 -3
- data/lib/unified2/payload.rb +82 -13
- data/lib/unified2/protocol.rb +141 -0
- data/lib/unified2/sensor.rb +22 -0
- data/lib/unified2/signature.rb +28 -4
- data/lib/unified2/version.rb +2 -2
- data/lib/unified2.rb +84 -13
- data/spec/event_spec.rb +112 -0
- data/spec/spec_helper.rb +45 -1
- data/spec/unified2_spec.rb +87 -1
- metadata +45 -25
- data/README.rdoc +0 -60
- data/Rakefile.compiled.rbc +0 -775
- data/example/connect.rb +0 -20
- data/example/models.rb +0 -194
- data/example/mysql-example.rb +0 -73
- data/example/search.rb +0 -14
- data/example/untitled.rb +0 -31
- data/lib/unified2/construct.rb +0 -54
- data/lib/unified2/event_ip4.rb +0 -26
- data/lib/unified2/event_ip6.rb +0 -23
- data/lib/unified2/packet.rb +0 -16
- data/lib/unified2/primitive/ipv4.rb +0 -19
- data/lib/unified2/record_header.rb +0 -10
data/ChangeLog.rdoc
CHANGED
data/LICENSE.txt
CHANGED
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# unified2
|
2
|
+
|
3
|
+
* [Homepage](http://github.com/mephux/unified2)
|
4
|
+
* [Issues](http://github.com/mephux/unified2/issues)
|
5
|
+
* [Documentation](http://rubydoc.info/gems/unified2/frames)
|
6
|
+
* [Email](mailto:dustin.webber at gmail.com)
|
7
|
+
|
8
|
+
## Description
|
9
|
+
|
10
|
+
A ruby interface for unified2 output. rUnified2 allows you to manipulate unified2 output for custom storage and/or analysis.
|
11
|
+
|
12
|
+
## Features
|
13
|
+
|
14
|
+
* Monitor/Read unified2 logs & manipulate the data.
|
15
|
+
* Numerous connivence methods
|
16
|
+
* Simple & Intuitive to Use
|
17
|
+
|
18
|
+
## Examples
|
19
|
+
|
20
|
+
require 'unified2'
|
21
|
+
|
22
|
+
#
|
23
|
+
# Load rules into memory
|
24
|
+
#
|
25
|
+
|
26
|
+
Unified2.configuration do
|
27
|
+
# Sensor Configurations
|
28
|
+
sensor :id => 1, :name => 'Test Sensor', :interface => 'en1'
|
29
|
+
|
30
|
+
# Load signatures, generators & classifications into memory
|
31
|
+
load :signatures, 'sid-msg.map'
|
32
|
+
load :generators, 'gen-msg.map'
|
33
|
+
load :classifications, 'classification.config'
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Unified2#watch
|
38
|
+
#
|
39
|
+
# Watch a unified2 file for changes and process the results.
|
40
|
+
#
|
41
|
+
|
42
|
+
Unified2.watch('/var/log/snort/merged.log', :last) do |event|
|
43
|
+
next if event.signature.name.blank?
|
44
|
+
puts event
|
45
|
+
end
|
46
|
+
|
47
|
+
# Unified2#read
|
48
|
+
# Parse a unified2 file and process the results.
|
49
|
+
|
50
|
+
Unified2.read('/var/log/snort/merged.log') do |event|
|
51
|
+
|
52
|
+
puts event.protocol #=> "TCP"
|
53
|
+
|
54
|
+
puts event.protocol.to_h #=> {:length=>379, :seq=>3934511163, :ack=>1584708129 ... }
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
## Requirements
|
59
|
+
|
60
|
+
* bindata ~> 1.3.1
|
61
|
+
* hexdump: ~> 0.1.0
|
62
|
+
* packetfu: ~> 1.0.0
|
63
|
+
|
64
|
+
## Install
|
65
|
+
|
66
|
+
`$ gem install unified2`
|
67
|
+
|
68
|
+
== Copyright
|
69
|
+
|
70
|
+
Copyright (c) 2011 Dustin Willis Webber
|
71
|
+
|
72
|
+
See LICENSE.txt for details.
|
@@ -1,6 +1,5 @@
|
|
1
1
|
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
2
|
require 'unified2'
|
3
|
-
require 'pp'
|
4
3
|
|
5
4
|
# Unified2 Configuration
|
6
5
|
Unified2.configuration do
|
@@ -10,7 +9,9 @@ Unified2.configuration do
|
|
10
9
|
|
11
10
|
# Load signatures, generators & classifications into memory
|
12
11
|
load :signatures, 'seeds/sid-msg.map'
|
12
|
+
|
13
13
|
load :generators, 'seeds/gen-msg.map'
|
14
|
+
|
14
15
|
load :classifications, 'seeds/classification.config'
|
15
16
|
|
16
17
|
end
|
@@ -19,7 +20,7 @@ end
|
|
19
20
|
# The second argument is the last event processed by
|
20
21
|
# the sensor. If the last_event_id column is blank in the
|
21
22
|
# sensor table it will begin at the first available event.
|
22
|
-
Unified2.watch('seeds/unified2', :first) do |event|
|
23
|
+
Unified2.watch('seeds/unified2.log', :first) do |event|
|
23
24
|
next if event.signature.blank?
|
24
25
|
|
25
26
|
puts event
|
File without changes
|
data/gemspec.yml
CHANGED
@@ -1,8 +1,21 @@
|
|
1
1
|
module Unified2
|
2
|
+
#
|
3
|
+
# Classification
|
4
|
+
#
|
2
5
|
class Classification
|
3
6
|
|
4
7
|
attr_accessor :id, :name, :short, :severity
|
5
|
-
|
8
|
+
|
9
|
+
#
|
10
|
+
# Initialize classification
|
11
|
+
#
|
12
|
+
# @param [Hash] classification Classification attributes
|
13
|
+
#
|
14
|
+
# @option classification [Integer] :classification_id Classification id
|
15
|
+
# @option classification [String] :name Classification name
|
16
|
+
# @option classification [String] :short Classification short name
|
17
|
+
# @option classification [String] :severity Classification severity id
|
18
|
+
#
|
6
19
|
def initialize(classification={})
|
7
20
|
@id = classification[:classification_id]
|
8
21
|
@name = classification[:name]
|
@@ -10,5 +23,6 @@ module Unified2
|
|
10
23
|
@severity = classification[:severity]
|
11
24
|
end
|
12
25
|
|
13
|
-
end
|
14
|
-
|
26
|
+
end # class Classification
|
27
|
+
|
28
|
+
end # module Unified2
|
data/lib/unified2/config_file.rb
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
module Unified2
|
2
|
+
#
|
3
|
+
# Configuration file
|
4
|
+
#
|
2
5
|
class ConfigFile
|
3
6
|
|
4
7
|
attr_accessor :type, :path, :md5, :data
|
5
8
|
|
9
|
+
#
|
10
|
+
# Initialize configuration file
|
11
|
+
#
|
12
|
+
# @param [String, Symbol] type Configuration file type
|
13
|
+
# @param [String] path Configuration file path
|
14
|
+
#
|
6
15
|
def initialize(type, path)
|
7
16
|
@type = type
|
8
17
|
@path = path
|
@@ -10,9 +19,24 @@ module Unified2
|
|
10
19
|
@md5 = Digest::MD5.hexdigest(@path)
|
11
20
|
import
|
12
21
|
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Size
|
25
|
+
#
|
26
|
+
# @return [Integer] Configuration size
|
27
|
+
#
|
28
|
+
def size
|
29
|
+
@data.size
|
30
|
+
end
|
13
31
|
|
14
32
|
private
|
15
|
-
|
33
|
+
|
34
|
+
#
|
35
|
+
# Configuration Import
|
36
|
+
#
|
37
|
+
# Parse the configuration files and store
|
38
|
+
# them in memory as a hash.
|
39
|
+
#
|
16
40
|
def import
|
17
41
|
file = File.open(@path)
|
18
42
|
|
@@ -25,9 +49,8 @@ module Unified2
|
|
25
49
|
next unless line[/^config\s/]
|
26
50
|
count += 1
|
27
51
|
|
28
|
-
# attempted-dos,Attempted Denial of Service,2
|
29
52
|
data = line.gsub!(/config classification: /, '')
|
30
|
-
short, name, severity = data.to_s.split(',')
|
53
|
+
short, name, severity = data.to_s.split(',').map(&:strip)
|
31
54
|
|
32
55
|
@data[count.to_s] = {
|
33
56
|
:short => short,
|
@@ -40,13 +63,13 @@ module Unified2
|
|
40
63
|
|
41
64
|
file.each_line do |line|
|
42
65
|
next if line[/^\#/]
|
43
|
-
generator_id, alert_id, name = line.split(' || ')
|
66
|
+
generator_id, alert_id, name = line.split(' || ').map(&:strip)
|
44
67
|
id = "#{generator_id}.#{alert_id}"
|
45
68
|
|
46
69
|
@data[id] = {
|
47
|
-
:generator_id => generator_id,
|
70
|
+
:generator_id => generator_id.to_i,
|
48
71
|
:name => name,
|
49
|
-
:signature_id => alert_id
|
72
|
+
:signature_id => alert_id.to_i
|
50
73
|
}
|
51
74
|
end
|
52
75
|
|
@@ -54,7 +77,7 @@ module Unified2
|
|
54
77
|
|
55
78
|
file.each_line do |line|
|
56
79
|
next if line[/^\#/]
|
57
|
-
id, body, *reference_data = line.split(' || ')
|
80
|
+
id, body, *reference_data = line.split(' || ').map(&:strip)
|
58
81
|
|
59
82
|
references = {}
|
60
83
|
reference_data.each do |line|
|
@@ -67,7 +90,7 @@ module Unified2
|
|
67
90
|
end
|
68
91
|
|
69
92
|
@data[id] = {
|
70
|
-
:signature_id => id,
|
93
|
+
:signature_id => id.to_i,
|
71
94
|
:name => body,
|
72
95
|
:generator_id => 1
|
73
96
|
}
|
@@ -76,5 +99,6 @@ module Unified2
|
|
76
99
|
end
|
77
100
|
end
|
78
101
|
|
79
|
-
end
|
80
|
-
|
102
|
+
end # class ConfigFile
|
103
|
+
|
104
|
+
end # module Unified2
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'unified2/constructor/event_ip4'
|
2
|
+
require 'unified2/constructor/event_ip6'
|
3
|
+
require 'unified2/constructor/record_header'
|
4
|
+
require 'unified2/constructor/packet'
|
5
|
+
|
6
|
+
module Unified2
|
7
|
+
#
|
8
|
+
# Unified2 Constructor Namespace
|
9
|
+
#
|
10
|
+
module Constructor
|
11
|
+
#
|
12
|
+
# Unified2 Construction
|
13
|
+
#
|
14
|
+
class Construct < ::BinData::Record
|
15
|
+
#
|
16
|
+
# Rename record_header to header
|
17
|
+
# to simplify and cut down on verbosity
|
18
|
+
#
|
19
|
+
record_header :header
|
20
|
+
|
21
|
+
#
|
22
|
+
# Unified2 data types
|
23
|
+
#
|
24
|
+
# Currently rUnified2 only supports packet,
|
25
|
+
# event_ip4 and event_ip6.
|
26
|
+
#
|
27
|
+
choice :data, :selection => :type_selection do
|
28
|
+
packet "packet"
|
29
|
+
event_ip4 "ev4"
|
30
|
+
event_ip6 "ev6"
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# String padding
|
35
|
+
#
|
36
|
+
string :read_length => :padding_length
|
37
|
+
|
38
|
+
#
|
39
|
+
# Type Selection
|
40
|
+
#
|
41
|
+
# Deterime and call data type based on
|
42
|
+
# the unified2 type attribute
|
43
|
+
#
|
44
|
+
def type_selection
|
45
|
+
case header.u2type.to_i
|
46
|
+
when 1
|
47
|
+
# define UNIFIED2_EVENT 1
|
48
|
+
when 2
|
49
|
+
# define UNIFIED2_PACKET 2
|
50
|
+
"packet"
|
51
|
+
when 7
|
52
|
+
# define UNIFIED2_IDS_EVENT 7
|
53
|
+
"ev4"
|
54
|
+
when 66
|
55
|
+
# define UNIFIED2_EVENT_EXTENDED 66
|
56
|
+
when 67
|
57
|
+
# define UNIFIED2_PERFORMANCE 67
|
58
|
+
when 68
|
59
|
+
# define UNIFIED2_PORTSCAN 68
|
60
|
+
when 72
|
61
|
+
# define UNIFIED2_IDS_EVENT_IPV6 72
|
62
|
+
"ev6"
|
63
|
+
else
|
64
|
+
"unknown type #{header.u2type}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Sometimes the data needs extra padding
|
70
|
+
#
|
71
|
+
def padding_length
|
72
|
+
if header.u2length > data.num_bytes
|
73
|
+
header.u2length - data.num_bytes
|
74
|
+
else
|
75
|
+
0
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end # class Construct
|
80
|
+
|
81
|
+
end # module Construct
|
82
|
+
|
83
|
+
end # module Unified2
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'unified2/primitive/ipv4'
|
2
|
+
|
3
|
+
module Unified2
|
4
|
+
|
5
|
+
module Constructor
|
6
|
+
#
|
7
|
+
# Event IP Version 4
|
8
|
+
#
|
9
|
+
class EventIP4 < ::BinData::Record
|
10
|
+
|
11
|
+
endian :big
|
12
|
+
|
13
|
+
uint32 :sensor_id
|
14
|
+
|
15
|
+
uint32 :event_id
|
16
|
+
|
17
|
+
uint32 :event_second
|
18
|
+
|
19
|
+
uint32 :event_microsecond
|
20
|
+
|
21
|
+
uint32 :signature_id
|
22
|
+
|
23
|
+
uint32 :generator_id
|
24
|
+
|
25
|
+
uint32 :signature_revision
|
26
|
+
|
27
|
+
uint32 :classification_id
|
28
|
+
|
29
|
+
uint32 :priority_id
|
30
|
+
|
31
|
+
ipv4 :ip_source
|
32
|
+
|
33
|
+
ipv4 :ip_destination
|
34
|
+
|
35
|
+
uint16 :sport_itype
|
36
|
+
|
37
|
+
uint16 :dport_icode
|
38
|
+
|
39
|
+
uint8 :protocol
|
40
|
+
|
41
|
+
uint8 :packet_action
|
42
|
+
|
43
|
+
end # class EventIP4
|
44
|
+
|
45
|
+
end # module Constructor
|
46
|
+
|
47
|
+
end # module Unified2
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Unified2
|
2
|
+
|
3
|
+
module Constructor
|
4
|
+
#
|
5
|
+
# Event IP Version 6
|
6
|
+
#
|
7
|
+
class EventIP6 < ::BinData::Record
|
8
|
+
endian :big
|
9
|
+
|
10
|
+
uint32 :sensor_id
|
11
|
+
|
12
|
+
uint32 :event_id
|
13
|
+
|
14
|
+
uint32 :event_second
|
15
|
+
|
16
|
+
uint32 :event_microsecond
|
17
|
+
|
18
|
+
uint32 :signature_id
|
19
|
+
|
20
|
+
uint32 :generator_id
|
21
|
+
|
22
|
+
uint32 :signature_revision
|
23
|
+
|
24
|
+
uint32 :classification_id
|
25
|
+
|
26
|
+
uint32 :priority_id
|
27
|
+
|
28
|
+
uint128 :ip_source
|
29
|
+
|
30
|
+
uint128 :ip_destination
|
31
|
+
|
32
|
+
uint16 :sport_itype
|
33
|
+
|
34
|
+
uint16 :dport_icode
|
35
|
+
|
36
|
+
uint8 :protocol
|
37
|
+
|
38
|
+
uint8 :packet_action
|
39
|
+
|
40
|
+
end # class EventIP6
|
41
|
+
|
42
|
+
end # module Constructor
|
43
|
+
|
44
|
+
end # module Unified2
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Unified2
|
2
|
+
|
3
|
+
module Constructor
|
4
|
+
#
|
5
|
+
# Event Packet
|
6
|
+
#
|
7
|
+
class Packet < ::BinData::Record
|
8
|
+
endian :big
|
9
|
+
|
10
|
+
uint32 :sensor_id
|
11
|
+
|
12
|
+
uint32 :event_id
|
13
|
+
|
14
|
+
uint32 :event_second
|
15
|
+
|
16
|
+
uint32 :packet_second
|
17
|
+
|
18
|
+
uint32 :packet_microsecond
|
19
|
+
|
20
|
+
uint32 :linktype
|
21
|
+
|
22
|
+
uint32 :packet_length
|
23
|
+
|
24
|
+
string :packet_data, :read_length => :packet_length
|
25
|
+
|
26
|
+
end # class Packet
|
27
|
+
|
28
|
+
end # module Constructor
|
29
|
+
|
30
|
+
end # module Unified2
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Unified2
|
2
|
+
|
3
|
+
module Constructor
|
4
|
+
#
|
5
|
+
# Unified2 Primitive Namespace
|
6
|
+
#
|
7
|
+
module Primitive
|
8
|
+
#
|
9
|
+
# BinData Primitive IP4 Constructor
|
10
|
+
#
|
11
|
+
class IPV4 < ::BinData::Primitive
|
12
|
+
array :octets, :type => :uint8, :initial_length => 4
|
13
|
+
|
14
|
+
# IPV4#set
|
15
|
+
def set(value)
|
16
|
+
ints = value.split(/\./).collect { |int| int.to_i }
|
17
|
+
self.octets = ints
|
18
|
+
end
|
19
|
+
|
20
|
+
# IPV4#get
|
21
|
+
def get
|
22
|
+
self.octets.collect { |octet| "%d" % octet }.join(".")
|
23
|
+
end
|
24
|
+
|
25
|
+
end # class IPV4
|
26
|
+
|
27
|
+
end # class Primitive
|
28
|
+
|
29
|
+
end # module Constructor
|
30
|
+
|
31
|
+
end # module Unified2
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Unified2
|
2
|
+
|
3
|
+
module Constructor
|
4
|
+
#
|
5
|
+
# Unified2 Header
|
6
|
+
#
|
7
|
+
class RecordHeader < ::BinData::Record
|
8
|
+
endian :big
|
9
|
+
|
10
|
+
uint32 :u2type
|
11
|
+
uint32 :u2length
|
12
|
+
|
13
|
+
end # class RecordHeader
|
14
|
+
|
15
|
+
end # module Constructor
|
16
|
+
|
17
|
+
end # module Unified2
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'unified2/constructor/construct'
|
@@ -1,8 +1,16 @@
|
|
1
|
+
#
|
2
|
+
# String monkeypatches
|
3
|
+
#
|
1
4
|
class String
|
2
|
-
|
5
|
+
#
|
6
|
+
# Blank?
|
7
|
+
#
|
8
|
+
# @return [true, false] If the string
|
9
|
+
# is blank or empty return true.
|
10
|
+
#
|
3
11
|
def blank?
|
4
12
|
return true if (self.nil? || self == '')
|
5
13
|
false
|
6
14
|
end
|
7
15
|
|
8
|
-
end
|
16
|
+
end # class String
|