thm 0.1.8

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/lib/thm.rb ADDED
@@ -0,0 +1,49 @@
1
+ ########################################################################
2
+ #
3
+ # Author: Brian Hood
4
+ #
5
+ # Description: Threatmonitor Producer
6
+ #
7
+ # Producer / Consumer controller module
8
+ #
9
+ ########################################################################
10
+
11
+ require 'rubygems'
12
+ require 'amqp'
13
+ require 'bunny'
14
+ require 'eventmachine'
15
+ require 'guid'
16
+ require 'yaml'
17
+ require 'pcaplet'
18
+ require 'pcaprub' # For Live capture / write
19
+ #require '../datalayerlight.rb'
20
+ require File.expand_path(File.join(
21
+ File.dirname(__FILE__),
22
+ "../datalayerlight.rb"))
23
+ include Pcap
24
+
25
+ # TODO
26
+ #
27
+ # Create def's for that packet SQL / Refactor to provent code duplication
28
+ # Create def's for Hash table YAML same idea as above.
29
+
30
+ module Tools
31
+
32
+ class << self
33
+
34
+ def guid
35
+ guid = Guid.new # Generate GUID
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ # Load Datasources
43
+ require "#{File.dirname(__FILE__)}/thm/dataservices.rb"
44
+ require "#{File.dirname(__FILE__)}/thm/producer.rb"
45
+ require "#{File.dirname(__FILE__)}/thm/consumer.rb"
46
+ require "#{File.dirname(__FILE__)}/thm/localmachine.rb"
47
+ require "#{File.dirname(__FILE__)}/thm/version.rb"
48
+
49
+
@@ -0,0 +1,228 @@
1
+
2
+ trap("INT") {
3
+
4
+ if EM.reactor_running? == true
5
+ puts "Exiting Reactor thread ..."
6
+ EventMachine.stop
7
+ end
8
+ exit
9
+ }
10
+
11
+ module Thm
12
+
13
+ # Bulk load from queue YAML into Database
14
+
15
+ class Consumer < DataServices
16
+
17
+ def from_mq_to_db
18
+ # TODO: Test this.
19
+ # Process ippacket queue first.
20
+ n = 0
21
+ # Using AMQP Gem here as Bunny never exits the thread so i can't move on to TCP / UDP probably migrate all to this gem.
22
+ banner = "\e[1;34mStage 1: Load IP Packet data \e[0m\ \n"
23
+ banner << "\e[1;34m=================================\e[0m\ \n"
24
+ puts banner
25
+ EM.run do
26
+ connection = AMQP.connect(:host => "#{@mqhost}", :user => "#{@mquser}", :pass => "#{@mqpass}", :vhost => "#{@mqvhost}")
27
+ puts "Connected to AMQP broker. Running #{AMQP::VERSION}"
28
+ channel = AMQP::Channel.new(connection)
29
+ puts "Queue: #{@queueprefix}_ippacket"
30
+ queue = channel.queue("#{@queueprefix}_ippacket")
31
+ exchange = channel.direct("")
32
+ t = 0
33
+ queue.bind("#{@queueprefix}_ippacket").subscribe do |metadata, body|
34
+ #puts "MSGID: [#{n}] Received #{body}"
35
+ ipdata = YAML.load(body).to_a
36
+ ipdatadim = ipdata[0][1]
37
+ ip_packet = "INSERT INTO #{@tblname_ippacket} "
38
+ ip_packet << "(guid, recv_date, ip_df, ip_dst, ip_hlen, ip_id, ip_len, ip_mf, ip_off, ip_proto, ip_src, ip_sum, ip_tos, ip_ttl, ip_ver) "
39
+ ip_packet << "VALUES ("
40
+ ip_packet << "'#{ipdatadim["guid"]}',"
41
+ ip_packet << "'#{ipdatadim["recv_date"]}',"
42
+ ip_df = ipdatadim["ip_df"].to_s # Due to TrueClass issues will have a look later
43
+ if ip_df == "true"
44
+ ip_packet << "'Y',"
45
+ else
46
+ ip_packet << "'N',"
47
+ end
48
+ ip_packet << "'#{ipdatadim["ip_dst"]}',"
49
+ ip_packet << "'#{ipdatadim["ip_hlen"]}',"
50
+ ip_packet << "'#{ipdatadim["ip_id"]}',"
51
+ ip_packet << "'#{ipdatadim["ip_len"]}',"
52
+ ip_mf = ipdatadim["ip_mf"].to_s
53
+ if ip_mf == "true"
54
+ ip_packet << "'Y',"
55
+ else
56
+ ip_packet << "'N',"
57
+ end
58
+ ip_packet << "'#{ipdatadim["ip_off"]}',"
59
+ ip_packet << "'#{ipdatadim["ip_proto"]}',"
60
+ ip_packet << "'#{ipdatadim["ip_src"]}',"
61
+ ip_packet << "'#{ipdatadim["ip_sum"]}',"
62
+ ip_packet << "'#{ipdatadim["ip_tos"]}',"
63
+ ip_packet << "'#{ipdatadim["ip_ttl"]}',"
64
+ ip_packet << "'#{ipdatadim["ip_ver"]}');"
65
+ if t == 50
66
+ puts "\e[1;32m\ MSGID:\e[0m\ [#{n}] \e[1;32m\Generated SQL:\e[0m\ #{ip_packet}"
67
+ t = 0
68
+ end
69
+ t = t + 1 unless t == 50
70
+ res = @conn.query("#{ip_packet}")
71
+ @conn.save
72
+ n = n + 1
73
+ connection.close { EventMachine.stop }
74
+ end
75
+ end
76
+ ipcount = n
77
+ @conn.release
78
+ @conn.commit
79
+
80
+ # TCP Packet
81
+ n = 0
82
+ banner = "\e[1;34mStage 2: Load TCP Packet data \e[0m\ \n"
83
+ banner << "\e[1;34m=================================\e[0m\ \n"
84
+ puts banner
85
+ EM.run do
86
+ connection = AMQP.connect(:host => "#{@mqhost}", :user => "#{@mquser}", :pass => "#{@mqpass}", :vhost => "#{@mqvhost}")
87
+ puts "Connected to AMQP broker. Running #{AMQP::VERSION}"
88
+ channel = AMQP::Channel.new(connection)
89
+ puts "Queue: #{@queueprefix}_tcppacket"
90
+ queue = channel.queue("#{@queueprefix}_tcppacket")
91
+ exchange = channel.direct("")
92
+ t = 0
93
+ queue.bind("#{@queueprefix}_tcppacket").subscribe do |metadata, body|
94
+ #puts "MSGID: [#{n}] Received #{body}"
95
+ tcpdata = YAML.load(body).to_a
96
+ tcpdatadim = tcpdata[0][1]
97
+ tcp_packet = "INSERT INTO #{@tblname_tcppacket} "
98
+ tcp_packet << "(guid, recv_date, tcp_data_len, tcp_dport, tcp_ack, tcp_fin, tcp_syn, tcp_rst, tcp_psh, tcp_urg, tcp_off, tcp_hlen, tcp_seq, tcp_sum, tcp_sport, tcp_urp, "
99
+ tcp_packet << "tcp_win) "
100
+ tcp_packet << "VALUES ("
101
+ tcp_packet << "'#{tcpdatadim["guid"]}',"
102
+ tcp_packet << "'#{tcpdatadim["recv_date"]}', "
103
+ tcp_packet << "#{tcpdatadim["tcp_data_len"]},"
104
+ tcp_packet << "#{tcpdatadim["tcp_dport"]},"
105
+ tcp_ack = tcpdatadim["tcp_ack"].to_s
106
+ if tcp_ack == "true"
107
+ tcp_packet << "'Y',"
108
+ else
109
+ tcp_packet << "'N',"
110
+ end
111
+ tcp_fin = tcpdatadim["tcp_fin"].to_s
112
+ if tcp_fin == "true"
113
+ tcp_packet << "'Y',"
114
+ else
115
+ tcp_packet << "'N',"
116
+ end
117
+ tcp_syn = tcpdatadim["tcp_syn"].to_s
118
+ if tcp_syn == "true"
119
+ tcp_packet << "'Y',"
120
+ else
121
+ tcp_packet << "'N',"
122
+ end
123
+ tcp_rst = tcpdatadim["tcp_rst"].to_s
124
+ if tcp_rst == "true"
125
+ tcp_packet << "'Y',"
126
+ else
127
+ tcp_packet << "'N',"
128
+ end
129
+ tcp_psh = tcpdatadim["tcp_psh"].to_s
130
+ if tcp_psh == "true"
131
+ tcp_packet << "'Y',"
132
+ else
133
+ tcp_packet << "'N',"
134
+ end
135
+ tcp_urg = tcpdatadim["tcp_urg"].to_s
136
+ if tcp_urg == "true"
137
+ tcp_packet << "'Y',"
138
+ else
139
+ tcp_packet << "'N',"
140
+ end
141
+ tcp_packet << "#{tcpdatadim["tcp_off"]}, #{tcpdatadim["tcp_hlen"]}, #{tcpdatadim["tcp_seq"]}, #{tcpdatadim["tcp_sum"]}, #{tcpdatadim["tcp_sport"]}, #{tcpdatadim["tcp_urp"]}, #{tcpdatadim["tcp_win"]});"
142
+ if t == 50
143
+ puts "\e[1;32m\ MSGID:\e[0m\ [#{n}] \e[1;32m\Generated SQL:\e[0m\ #{tcp_packet}"
144
+ t = 0
145
+ end
146
+ t = t + 1 unless t == 50
147
+ res = @conn.query("#{tcp_packet}")
148
+ @conn.save
149
+ n = n + 1
150
+ connection.close { EventMachine.stop }
151
+ end
152
+ end
153
+ tcpcount = n
154
+ @conn.release
155
+ @conn.commit
156
+
157
+ # UDP Packet
158
+ n = 0
159
+ banner = "\e[1;34mStage 2: Load UDP Packet data \e[0m\ \n"
160
+ banner << "\e[1;34m=================================\e[0m\ \n"
161
+ puts banner
162
+ EM.run do
163
+ connection = AMQP.connect(:host => "#{@mqhost}", :user => "#{@mquser}", :pass => "#{@mqpass}", :vhost => "#{@mqvhost}")
164
+ puts "Connected to AMQP broker. Running #{AMQP::VERSION}"
165
+ channel = AMQP::Channel.new(connection)
166
+ puts "Queue: #{@queueprefix}_udppacket"
167
+ queue = channel.queue("#{@queueprefix}_udppacket")
168
+ exchange = channel.direct("")
169
+ t = 0
170
+ queue.bind("#{@queueprefix}_udppacket").subscribe do |metadata, body|
171
+ #puts "MSGID: [#{n}] Received #{body}"
172
+ udpdata = YAML.load(body).to_a
173
+ udpdatadim = udpdata[0][1]
174
+ udp_packet = "INSERT INTO #{@tblname_udppacket} "
175
+ udp_packet << "(guid,"
176
+ udp_packet << "recv_date,"
177
+ udp_packet << "udp_dport,"
178
+ udp_packet << "udp_len,"
179
+ udp_packet << "udp_sum,"
180
+ udp_packet << "udp_sport) "
181
+ udp_packet << "VALUES ("
182
+ udp_packet << "'#{udpdatadim["guid"]}',"
183
+ udp_packet << "'#{udpdatadim["recv_date"]}',"
184
+ udp_packet << "'#{udpdatadim["udp_dport"]}',"
185
+ udp_packet << "'#{udpdatadim["udp_len"]}',"
186
+ udp_packet << "'#{udpdatadim["udp_sum"]}',"
187
+ udp_packet << "'#{udpdatadim["udp_sport"]}');"
188
+ if t == 50
189
+ puts "\e[1;32m\ MSGID:\e[0m\ [#{n}] \e[1;32m\Generated SQL:\e[0m\ #{udp_packet}"
190
+ t = 0
191
+ end
192
+ t = t + 1 unless t == 50
193
+ res = @conn.query("#{udp_packet}")
194
+ @conn.save
195
+ n = n + 1
196
+ connection.close { EventMachine.stop }
197
+ end
198
+ end
199
+ udpcount = n
200
+ @conn.release
201
+ @conn.commit
202
+ totals = "\e[1;31m=======================================================================\e[0m\ \n"
203
+ totals << "\e[1;31mPackets Total | IP: #{ipcount} | TCP: #{tcpcount} | UDP: #{udpcount}\e[0m\ \n"
204
+ totals << "\e[1;31m======================================================================\e[0m\ \n"
205
+ puts totals
206
+
207
+ end
208
+
209
+ def infinite
210
+ puts "\e[1;31mStarting Consumer in infinite mode"
211
+ puts "\e[1;31m==================================\n"
212
+ puts "NOTE: Only should be used for live captures\n"
213
+ loop {
214
+ from_mq_to_db
215
+ }
216
+ end
217
+
218
+ def passes(passes)
219
+ puts "\e[1;31mStarting Consumer for #{passes} passes"
220
+ puts "\e[1;31m======================================="
221
+ passes.times {
222
+ from_mq_to_db
223
+ }
224
+ end
225
+
226
+ end
227
+
228
+ end
@@ -0,0 +1,73 @@
1
+ module Thm
2
+
3
+ class DataServices
4
+
5
+ # This class provides all the core functionality to the lower level DatalayerLight
6
+ #
7
+ # Example variables
8
+ #
9
+ # obj = Thm::Producer.new
10
+ # obj.mqhost = "127.0.0.1"
11
+ # obj.mquser = "test"
12
+ # obj.mqpass = "setone"
13
+ # obj.mqconnect
14
+ # obj.dbconnect
15
+
16
+ attr_accessor :datastore, :mqhost, :mquser, :mqpass, :mqvhost, :dbhost, :dbuser, :dbpass, :dbname, :queueprefix, :tblname_ippacket, :tblname_tcppacket, :tblname_udppacket
17
+
18
+ def initialize
19
+ @datastore = "monetdb"
20
+ @mqhost = "127.0.0.1"
21
+ @mquser = "traffic"
22
+ @mqpass = "dk3rbi9l"
23
+ @mqvhost = "/"
24
+ @dbhost = "127.0.0.1"
25
+ @dbuser = "threatmonitor"
26
+ @dbpass = "dk3rbi9l"
27
+ @dbname = "threatmonitor"
28
+ @queueprefix = "cactus" # Queue names will be come prefixed with cactus_ippacket etc ..
29
+ # Implement tblname for table freedom
30
+ @tblname_ippacket = "ippacket"
31
+ @tblname_tcppacket = "tcppacket"
32
+ @tblname_udppacket = "udppacket"
33
+ @mqconn = Bunny.new(:hostname => "#{@mqhost}", :user => "#{@mquser}", :pass => "#{@mqpass}", :vhost => "#{@mqvhost}")
34
+ end
35
+
36
+ def mqconnect
37
+ @mqconn.start
38
+ @ch = @mqconn.create_channel
39
+ end
40
+
41
+ def mqclose
42
+ @conn.close
43
+ end
44
+
45
+ def dbconnect
46
+ if @datastore == "mysql"
47
+ @conn = DatalayerLight::MySQLDrv.new
48
+ puts "Using MySQL Datasource"
49
+ elsif @datastore == "monetdb"
50
+ @conn = DatalayerLight::MonetDBDrv.new
51
+ puts "Using MonetDB Datasource"
52
+ end
53
+ @conn.hostname = @dbhost
54
+ @conn.username = @dbuser
55
+ @conn.password = @dbpass
56
+ @conn.dbname = @dbname
57
+ @conn.autocommit = false
58
+ begin
59
+ @conn.connect
60
+ rescue Errno::ECONNREFUSED
61
+ puts "Database not running!"
62
+ puts "Bye!"
63
+ exit
64
+ end
65
+ end
66
+
67
+ def query(sql)
68
+ res = @conn.query("#{sql}")
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,170 @@
1
+
2
+ module Thm
3
+
4
+ # Process data from / to local files.
5
+
6
+ class Localmachine < DataServices
7
+
8
+ # We have to use a different Gem here called pcaprub that supports live interface / dumping mode.
9
+
10
+ def from_pcap_to_disk(interface, dumpfile)
11
+ puts "Capturing Live data... "
12
+ begin
13
+ capture = PCAPRUB::Pcap.open_live("#{interface}", 65535, true, 0)
14
+ puts "Writing to file ..."
15
+ puts "Press CTRL+C to exit ..."
16
+ dumper = capture.dump_open("#{dumpfile}")
17
+ capture_packets = 100
18
+ capture.each {|pkt|
19
+ capture.dump(pkt.length, pkt.length, pkt)
20
+ }
21
+ capture.dump_close
22
+ rescue
23
+ puts "Make sure the interface name is correct and you have enough disk space"
24
+ exit
25
+ end
26
+ end
27
+
28
+ # We can inject packets into an interface DANGEROUS !!!
29
+
30
+ def from_pcap_to_interface_injection(interface, inputdumpfile)
31
+ # NOTES
32
+ # Src / Dst rewriting ip_src, ip_dst
33
+ end
34
+
35
+ # From Pcap file to Datastore
36
+
37
+ def from_pcap_db(pcapfile)
38
+ t, n, s, v, x, z = 0, 0, 0, 0, 0, 0
39
+ ipcount, tcpcount, udpcount = 0, 0, 0
40
+ inp = Pcap::Capture.open_offline(pcapfile)
41
+ inp.each_packet do |pkt|
42
+ guid = Tools::guid # IP / TCP / UDP relationship
43
+ dtime = Time.now
44
+ # IP Packet
45
+ if pkt.ip?
46
+ ip_packet = "INSERT INTO #{@tblname_ippacket} "
47
+ ip_packet << "(guid, recv_date, ip_df, ip_dst, ip_hlen, ip_id, ip_len, ip_mf, ip_off, ip_proto, ip_src, ip_sum, ip_tos, ip_ttl, ip_ver) "
48
+ ip_packet << "VALUES ("
49
+ ip_packet << "'#{guid}',"
50
+ ip_packet << "'#{dtime}',"
51
+ if pkt.ip_df? == true
52
+ ip_packet << "'Y',"
53
+ else
54
+ ip_packet << "'N',"
55
+ end
56
+ ip_packet << "'#{pkt.ip_dst}',"
57
+ ip_packet << "'#{pkt.ip_hlen}',"
58
+ ip_packet << "'#{pkt.ip_id}',"
59
+ ip_packet << "'#{pkt.ip_len}',"
60
+ if pkt.ip_mf? == true
61
+ ip_packet << "'Y',"
62
+ else
63
+ ip_packet << "'N',"
64
+ end
65
+ ip_packet << "'#{pkt.ip_off}',"
66
+ ip_packet << "'#{pkt.ip_proto}',"
67
+ ip_packet << "'#{pkt.ip_src}',"
68
+ ip_packet << "'#{pkt.ip_sum}',"
69
+ ip_packet << "'#{pkt.ip_tos}',"
70
+ ip_packet << "'#{pkt.ip_ttl}',"
71
+ ip_packet << "'#{pkt.ip_ver}');"
72
+ if t == 50
73
+ puts "\e[1;32m\ MSGID:\e[0m\ [#{n}] \e[1;32m\Generated SQL:\e[0m\ #{ip_packet}"
74
+ t = 0
75
+ end
76
+ t = t + 1 unless t == 50
77
+ res = @conn.query("#{ip_packet}")
78
+ @conn.save
79
+ n = n + 1
80
+ end
81
+ # TCP Packet
82
+ if pkt.tcp?
83
+ tcp_packet = "INSERT INTO #{@tblname_tcppacket} "
84
+ tcp_packet << "(guid, recv_date, tcp_data_len, tcp_dport, tcp_ack, tcp_fin, tcp_syn, tcp_rst, tcp_psh, tcp_urg, tcp_off, tcp_hlen, tcp_seq, tcp_sum, tcp_sport, tcp_urp, "
85
+ tcp_packet << "tcp_win) "
86
+ tcp_packet << "VALUES ("
87
+ tcp_packet << "'#{guid}',"
88
+ tcp_packet << "'#{dtime}', "
89
+ tcp_packet << "#{pkt.tcp_data_len},"
90
+ tcp_packet << "#{pkt.tcp_dport},"
91
+ if pkt.tcp_ack? == true
92
+ tcp_packet << "'Y',"
93
+ else
94
+ tcp_packet << "'N',"
95
+ end
96
+ if pkt.tcp_fin? == true
97
+ tcp_packet << "'Y',"
98
+ else
99
+ tcp_packet << "'N',"
100
+ end
101
+ if pkt.tcp_syn? == true
102
+ tcp_packet << "'Y',"
103
+ else
104
+ tcp_packet << "'N',"
105
+ end
106
+ if pkt.tcp_rst? == true
107
+ tcp_packet << "'Y',"
108
+ else
109
+ tcp_packet << "'N',"
110
+ end
111
+ if pkt.tcp_psh? == true
112
+ tcp_packet << "'Y',"
113
+ else
114
+ tcp_packet << "'N',"
115
+ end
116
+ if pkt.tcp_urg? == true
117
+ tcp_packet << "'Y',"
118
+ else
119
+ tcp_packet << "'N',"
120
+ end
121
+ tcp_packet << "#{pkt.tcp_off}, #{pkt.tcp_hlen}, #{pkt.tcp_seq}, #{pkt.tcp_sum}, #{pkt.tcp_sport}, #{pkt.tcp_urp}, #{pkt.tcp_win});"
122
+ if s == 50
123
+ puts "\e[1;32m\ MSGID:\e[0m\ [#{v}] \e[1;32m\Generated SQL:\e[0m\ #{tcp_packet}"
124
+ s = 0
125
+ end
126
+ s = s + 1 unless s == 50
127
+ res = @conn.query("#{tcp_packet}")
128
+ @conn.save
129
+ v = v + 1
130
+ end
131
+ # UDP Packet
132
+ if pkt.udp?
133
+ udp_packet = "INSERT INTO #{@tblname_udppacket} "
134
+ udp_packet << "(guid,"
135
+ udp_packet << "recv_date,"
136
+ udp_packet << "udp_dport,"
137
+ udp_packet << "udp_len,"
138
+ udp_packet << "udp_sum,"
139
+ udp_packet << "udp_sport) "
140
+ udp_packet << "VALUES ("
141
+ udp_packet << "'#{guid}',"
142
+ udp_packet << "'#{dtime}',"
143
+ udp_packet << "'#{pkt.udp_dport}',"
144
+ udp_packet << "'#{pkt.udp_len}',"
145
+ udp_packet << "'#{pkt.udp_sum}',"
146
+ udp_packet << "'#{pkt.udp_sport}');"
147
+ if x == 50
148
+ puts "\e[1;32m\ MSGID:\e[0m\ [#{z}] \e[1;32m\Generated SQL:\e[0m\ #{udp_packet}"
149
+ x = 0
150
+ end
151
+ x = x + 1 unless x == 50
152
+ res = @conn.query("#{udp_packet}")
153
+ @conn.save
154
+ z = z + 1
155
+ end
156
+ ipcount = n
157
+ tcpcount = v
158
+ udpcount = z
159
+ end
160
+ @conn.release
161
+ @conn.commit
162
+ totals = "\e[1;31m=======================================================================\e[0m\ \n"
163
+ totals << "\e[1;31mPackets Total | IP: #{ipcount} | TCP: #{tcpcount} | UDP: #{udpcount}\e[0m\ \n"
164
+ totals << "\e[1;31m======================================================================\e[0m\ \n"
165
+ puts totals
166
+ end
167
+
168
+ end
169
+
170
+ end