dorothy2 0.0.1
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/.gitignore +21 -0
- data/Gemfile +4 -0
- data/LICENSE +644 -0
- data/README.md +231 -0
- data/Rakefile +1 -0
- data/bin/dorothy_start +176 -0
- data/bin/dorothy_stop +28 -0
- data/bin/dparser_start +66 -0
- data/bin/dparser_stop +23 -0
- data/dorothy2.gemspec +30 -0
- data/etc/ddl/dorothive.ddl +1803 -0
- data/etc/dorothy copy.yml.example +39 -0
- data/etc/sandboxes.yml.example +20 -0
- data/etc/sources.yml.example +32 -0
- data/lib/doroParser.rb +518 -0
- data/lib/dorothy2/BFM.rb +156 -0
- data/lib/dorothy2/MAM.rb +239 -0
- data/lib/dorothy2/Settings.rb +35 -0
- data/lib/dorothy2/deep_symbolize.rb +67 -0
- data/lib/dorothy2/do-init.rb +296 -0
- data/lib/dorothy2/do-logger.rb +43 -0
- data/lib/dorothy2/do-parsers.rb +468 -0
- data/lib/dorothy2/do-utils.rb +223 -0
- data/lib/dorothy2/environment.rb +29 -0
- data/lib/dorothy2/version.rb +3 -0
- data/lib/dorothy2/vtotal.rb +84 -0
- data/lib/dorothy2.rb +470 -0
- data/share/img/Dorothy-Basic.pdf +0 -0
- data/share/img/Setup-Advanced.pdf +0 -0
- data/share/img/The_big_picture.pdf +0 -0
- data/test/tc_dorothy_full.rb +95 -0
- data/var/log/parser.log +0 -0
- metadata +260 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
---
|
2
|
+
dorothive:
|
3
|
+
dbuser: postgres
|
4
|
+
dbpass: password
|
5
|
+
dbhost: localhost
|
6
|
+
dbname: dorothive
|
7
|
+
ddl: /Users/akira/Codes/dorothy-gem-try/dorothy2/etc/ddl/dorothive.ddl
|
8
|
+
env:
|
9
|
+
geoip: /Users/akira/Codes/dorothy-gem-try/dorothy2/etc/geo/GeoLiteCity.dat
|
10
|
+
geoasn: /Users/akira/Codes/dorothy-gem-try/dorothy2/etc/geo/GeoIPASNum.dat
|
11
|
+
loglevel: 0
|
12
|
+
testmode: true
|
13
|
+
dtimeout: 3600
|
14
|
+
logfile_parser: /Users/akira/Codes/dorothy-gem-try/dorothy2/var/log/parser.log
|
15
|
+
analysis_dir: /Users/akira/Codes/dorothy-gem-try/dorothy2/opt/analyzed
|
16
|
+
pidfile_parser: /Users/akira/Codes/dorothy-gem-try/dorothy2/var/doroParser.pid
|
17
|
+
pidfile: /Users/akira/Codes/dorothy-gem-try/dorothy2/var/dorothy.pid
|
18
|
+
logage: weekly
|
19
|
+
logfile: /Users/akira/Codes/dorothy-gem-try/dorothy2/var/log/dorothy.log
|
20
|
+
home: /Users/akira/Codes/dorothy-gem-try/dorothy2
|
21
|
+
esx:
|
22
|
+
user: "root"
|
23
|
+
pass: "Dorothy!?!"
|
24
|
+
host: "192.168.187.128"
|
25
|
+
sandbox:
|
26
|
+
screen1time: 1
|
27
|
+
sleeptime: 60
|
28
|
+
screen2time: 15
|
29
|
+
nam:
|
30
|
+
namuser: dorothy
|
31
|
+
pcaphome: ~/pcaps
|
32
|
+
nampass: ""
|
33
|
+
interface: eth0
|
34
|
+
namserver: ""
|
35
|
+
virustotal:
|
36
|
+
vtapikey: "c37baad50a42d7df3f91e957255a2c6a9deabe339c2ff44d4a637fff912def48"
|
37
|
+
|
38
|
+
|
39
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#############################################
|
2
|
+
### DOROTHY SANDBOX LIST #
|
3
|
+
#############################################
|
4
|
+
###
|
5
|
+
###
|
6
|
+
### List of the available virtual machines
|
7
|
+
### (loaded into ESX)
|
8
|
+
### WARNING - after editing this file, run
|
9
|
+
### $dorothy_start -sandbox reload
|
10
|
+
#############################################
|
11
|
+
---
|
12
|
+
WinXP1:
|
13
|
+
password: password
|
14
|
+
ipaddress: 192.168.187.130
|
15
|
+
username: billy
|
16
|
+
os_lang: eng
|
17
|
+
os: Windows
|
18
|
+
version: XP SP2
|
19
|
+
type: virtual
|
20
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#############################################
|
2
|
+
### DOROTHY SOURCE LIST #
|
3
|
+
#############################################
|
4
|
+
###
|
5
|
+
### type means the communication channel used
|
6
|
+
### to download the binaries, possible values
|
7
|
+
### are: system | ssh
|
8
|
+
###
|
9
|
+
### typeid defines the type of the source, it
|
10
|
+
### depends on a userdefined-type in
|
11
|
+
### dorothive(DB) (table Sensors) please use
|
12
|
+
### ONLY the following ones (or add new ones
|
13
|
+
### in the DB accordingly)
|
14
|
+
###
|
15
|
+
### 0 - lowinteraction
|
16
|
+
### 1 - external source
|
17
|
+
### 2 - unknown
|
18
|
+
#############################################
|
19
|
+
---
|
20
|
+
malwarefolder:
|
21
|
+
type: system
|
22
|
+
localdir: /Users/m4rco/dorothy2/opt/bins/manual
|
23
|
+
typeid: 2
|
24
|
+
honeypot1:
|
25
|
+
type: ssh
|
26
|
+
ip: 1.2.3.4
|
27
|
+
port: 22
|
28
|
+
user: asd
|
29
|
+
pass: asdasdasd
|
30
|
+
remotedir: /asda/bins
|
31
|
+
localdir: /opt/bins/honeypot
|
32
|
+
typeid: 0
|
data/lib/doroParser.rb
ADDED
@@ -0,0 +1,518 @@
|
|
1
|
+
# Copyright (C) 2010-2013 marco riccardi.
|
2
|
+
# This file is part of Dorothy - http://www.honeynet.it/dorothy
|
3
|
+
# See the file 'LICENSE' for copying permission.
|
4
|
+
|
5
|
+
#!/usr/local/bin/ruby
|
6
|
+
$LOAD_PATH.unshift '/opt/local/lib/ruby/gems/1.8/gems/ruby-filemagic-0.4.2/lib' #for MACOSX
|
7
|
+
#$LOAD_PATH.unshift '/usr/lib/ruby/gems/1.8/gems/ruby-filemagic-0.4.2/lib' #for linux Debian
|
8
|
+
require 'rubygems'
|
9
|
+
require 'mu/xtractr'
|
10
|
+
require 'md5'
|
11
|
+
require 'rbvmomi'
|
12
|
+
require 'rest_client'
|
13
|
+
require 'net/dns/packet'
|
14
|
+
require 'ipaddr'
|
15
|
+
require 'colored'
|
16
|
+
require 'trollop'
|
17
|
+
require 'ftools'
|
18
|
+
require 'filemagic' #require 'pcaplet'
|
19
|
+
require 'geoip' #1)gem install geoip
|
20
|
+
require 'pg'
|
21
|
+
require 'iconv'
|
22
|
+
require 'tmail'
|
23
|
+
require 'ipaddr'
|
24
|
+
|
25
|
+
require File.dirname(__FILE__) + '/dorothy/environment'
|
26
|
+
require File.dirname(__FILE__) + '/dorothy/do-parsers'
|
27
|
+
require File.dirname(__FILE__) + '/dorothy/do-utils'
|
28
|
+
require File.dirname(__FILE__) + '/dorothy/do-logger'
|
29
|
+
|
30
|
+
|
31
|
+
module DoroParser
|
32
|
+
#Host roles
|
33
|
+
|
34
|
+
CCIRC = 1
|
35
|
+
CCDROP = 3
|
36
|
+
CCSUPPORT = 5
|
37
|
+
|
38
|
+
def search_irc(streamdata)
|
39
|
+
|
40
|
+
util = Util.new
|
41
|
+
|
42
|
+
|
43
|
+
ircvalues = []
|
44
|
+
streamdata.each do |m|
|
45
|
+
# if m[1] == 0 #we fetch only outgoing traffic
|
46
|
+
direction_bool = (m[1] == 0 ? false : true)
|
47
|
+
LOGGER_PARSER.info "PARSER", "FOUND IRC DATA".white
|
48
|
+
LOGGER_PARSER.info "IRC", "#{m[0]}".yellow
|
49
|
+
#puts "..::: #{parsed.command}".white + " #{parsed.content}".yellow
|
50
|
+
|
51
|
+
ircvalues.push "default, currval('dorothy.connections_id_seq'), E'#{Insertdb.escape_bytea(m[0])}', #{direction_bool}"
|
52
|
+
# end
|
53
|
+
end
|
54
|
+
return ircvalues
|
55
|
+
end
|
56
|
+
|
57
|
+
def analyze_bintraffic(pcaps)
|
58
|
+
|
59
|
+
dns_list = Hash.new
|
60
|
+
hosts = []
|
61
|
+
@insertdb.begin_t
|
62
|
+
|
63
|
+
pcaps.each do |dump|
|
64
|
+
#RETRIVE MALWARE FILE INFO
|
65
|
+
|
66
|
+
!dump['sample'].nil? && !dump['hash'].nil? && !dump['pcapr_id'].nil? or next
|
67
|
+
|
68
|
+
LOGGER_PARSER.info "PARSER", "Analyzing file: ".yellow + dump['sample']
|
69
|
+
LOGGER_PARSER.info "PARSER", "Analyzing pcaprid: ".yellow + dump['pcapr_id'].gsub(/\s+/, "")
|
70
|
+
|
71
|
+
|
72
|
+
LOGGER_PARSER.debug "PARSER", "Analyzing dump: ".yellow + dump['hash'].gsub(/\s+/, "") if VERBOSE
|
73
|
+
|
74
|
+
|
75
|
+
#gets
|
76
|
+
|
77
|
+
downloadir = "#{ANALYSIS_DIR}/#{dump['sample'].gsub(/\s+/, "")}/downloads"
|
78
|
+
|
79
|
+
#puts "Sighting of #{malw.sha} imported"
|
80
|
+
|
81
|
+
##NETWORK DUMP PARSING#
|
82
|
+
#######################
|
83
|
+
#LOAD XTRACTR INSTANCE#
|
84
|
+
|
85
|
+
# begin
|
86
|
+
# t = RestClient.get "http://172.20.250.13:8080/pcaps/1/about/#{dump['pcapr_id'].gsub(/\s+/, "")"
|
87
|
+
# jt = JSON.parse(t)
|
88
|
+
# rescue RestClient::InternalServerError
|
89
|
+
# puts ".:: File not found: http://172.20.250.13:8080/pcaps/1/about/#{dump['pcapr_id'].gsub(/\s+/, "")".red
|
90
|
+
# puts ".:: #{$!}".red
|
91
|
+
## puts ".:: Skipping malware #{dump['hash']} and doing DB ROLLBACK".red
|
92
|
+
# next
|
93
|
+
# end
|
94
|
+
|
95
|
+
# puts ".:: File PCAP found on PCAPR DB - #{jt['filename']} - ID #{dump['pcapr_id'].gsub(/\s+/, "")"
|
96
|
+
#xtractr = Mu::Xtractr.create "http://172.20.250.13:8080/home/index.html#/browse/pcap/dd737a00ff0495083cf6edd772fe2a18"
|
97
|
+
# 843272e9a0b6a5f4aa5985d151cb6721
|
98
|
+
|
99
|
+
begin
|
100
|
+
#TEST!
|
101
|
+
xtractr = Doroxtractr.create "http://172.20.250.13:8080/pcaps/1/pcap/#{dump['pcapr_id'].gsub(/\s+/, "")}"
|
102
|
+
#xtractr = Doroxtractr.create "http://172.20.250.13:8080/pcaps/1/pcap/071dc8540a88d72c15d8542f6c7610f8"
|
103
|
+
#puts "TEST MODE ON!"
|
104
|
+
#gets
|
105
|
+
|
106
|
+
rescue
|
107
|
+
LOGGER_PARSER.fatal "PARSER", "Can't create a XTRACTR instance, try with nextone".red
|
108
|
+
# LOGGER_PARSER.debug "PARSER", "#{$!}"
|
109
|
+
next
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
LOGGER_PARSER.info "PARSER", "Scanning network flows and searching for unknown host IPs".yellow
|
114
|
+
|
115
|
+
#xtractr.flows('flow.service:HTTP').each { |flow|
|
116
|
+
|
117
|
+
xtractr.flows.each { |flow|
|
118
|
+
#TODO: begin , make exception hangling for every flow
|
119
|
+
|
120
|
+
#DEBUG
|
121
|
+
#puts flow.id
|
122
|
+
|
123
|
+
flowdeep = xtractr.flows("flow.id:#{flow.id}")
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
#Skipping if NETBIOS spreading activity:
|
128
|
+
if flow.dport == 135 or flow.dport == 445
|
129
|
+
LOGGER_PARSER.info "PARSER", "Netbios connections, skipping flow" unless NONETBIOS
|
130
|
+
next
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
title = flow.title[0..200].gsub(/'/,"") #xtool bug ->')
|
135
|
+
|
136
|
+
|
137
|
+
#insert hosts (geo) info into db
|
138
|
+
#check if is a localaddress
|
139
|
+
localip = "10.0.0.0"
|
140
|
+
localnet = IPAddr.new("#{localip}/24")
|
141
|
+
multicast = IPAddr.new("224.0.0.0/4")
|
142
|
+
|
143
|
+
#check if already present in DB
|
144
|
+
unless(@insertdb.select("host_ips", "ip", flow.dst.address).one? || hosts.include?(flow.dst.address))
|
145
|
+
LOGGER_PARSER.info "PARSER", "Analyzing #{flow.dst.address}".yellow
|
146
|
+
hosts << flow.dst.address
|
147
|
+
dest = flow.dst.address
|
148
|
+
|
149
|
+
|
150
|
+
#insert Geoinfo
|
151
|
+
unless(localnet.include?(flow.dst.address) || multicast.include?(flow.dst.address))
|
152
|
+
|
153
|
+
geo = Geoinfo.new(flow.dst.address.to_s)
|
154
|
+
geoval = ["default", geo.coord, geo.country, geo.city, geo.updated, geo.asn]
|
155
|
+
LOGGER_PARSER.debug "GEO", "Geo-values for #{flow.dst.address.to_s}: " + geo.country + " " + geo.city + " " + geo.coord if VERBOSE
|
156
|
+
|
157
|
+
if geo.coord != "null"
|
158
|
+
LOGGER_PARSER.debug "DB", " Inserting geo values for #{flow.dst.address.to_s} : #{geo.country}".blue if VERBOSE
|
159
|
+
@insertdb.insert("geoinfo",geoval)
|
160
|
+
geoval = "currval('dorothy.geoinfo_id_seq')"
|
161
|
+
else
|
162
|
+
LOGGER_PARSER.warn "DB", " No Geovalues found for #{flow.dst.address.to_s}".red if VERBOSE
|
163
|
+
geoval = "null"
|
164
|
+
end
|
165
|
+
|
166
|
+
else
|
167
|
+
LOGGER_PARSER.warn "PARSER", "#{flow.dst.address} skipped while searching for GeoInfo (it's a local network))".yellow
|
168
|
+
geoval = 'null'
|
169
|
+
dest = localip
|
170
|
+
end
|
171
|
+
|
172
|
+
#Insert host info
|
173
|
+
#ip - geoinfo - sbl - uptime - is_online - whois - zone - last-update - id - dns_name
|
174
|
+
hostname = (dns_list[dest].nil? ? "null" : dns_list[dest])
|
175
|
+
hostval = [dest, geoval, "null", "null", true, "null", "null", get_time, "default", hostname]
|
176
|
+
|
177
|
+
if !@insertdb.insert("host_ips",hostval)
|
178
|
+
LOGGER_PARSER.debug "DB", " Skipping flow #{flow.id}: #{flow.src.address} > #{flow.dst.address}" if VERBOSE
|
179
|
+
next
|
180
|
+
end
|
181
|
+
|
182
|
+
else
|
183
|
+
LOGGER_PARSER.debug "PARSER", "Host already #{flow.dst.address} known, skipping..." if VERBOSE
|
184
|
+
#puts ".:: Geo info host #{flow.dst.address} already present in geodatabase, skipping.." if @insertdb.select("host_ips", "ip", flow.dst.address)
|
185
|
+
end
|
186
|
+
|
187
|
+
#case TCP xtractr.flows('flow.service:SMTP').first.proto = 6
|
188
|
+
|
189
|
+
flowvals = [flow.src.address, flow.dst.address, flow.sport, flow.dport, flow.bytes, dump['hash'], flow.packets, "default", flow.proto, flow.service.name, title, "null", flow.duration, flow.time, flow.id ]
|
190
|
+
|
191
|
+
if !@insertdb.insert("flows",flowvals)
|
192
|
+
LOGGER_PARSER.info "PARSER", "Skipping flow #{flow.id}: #{flow.src.address} > #{flow.dst.address}"
|
193
|
+
next
|
194
|
+
end
|
195
|
+
|
196
|
+
LOGGER_PARSER.debug("DB", "Inserting flow #{flow.id} - #{flow.title}".blue) if VERBOSE
|
197
|
+
|
198
|
+
flowid = "currval('dorothy.connections_id_seq')"
|
199
|
+
|
200
|
+
#Layer 3 analysis
|
201
|
+
service = flow.service.name
|
202
|
+
|
203
|
+
#DEBUG
|
204
|
+
#puts "PROTO = " + flow.proto.to_s
|
205
|
+
|
206
|
+
case flow.proto
|
207
|
+
when 6 then
|
208
|
+
#check if HTTP,IRC, MAIL
|
209
|
+
#xtractr.flows('flow.service:SMTP').first.service.name == "TCP" when unknow
|
210
|
+
|
211
|
+
#Layer 4 analysis
|
212
|
+
streamdata = xtractr.streamdata(flow.id)
|
213
|
+
|
214
|
+
#DEBUG
|
215
|
+
#puts "SERVICE = " + service.to_s
|
216
|
+
|
217
|
+
case service #TODO: don't trust service field: it's based on default-port definition, do a packet inspection instead.
|
218
|
+
|
219
|
+
#case HTTP
|
220
|
+
when "HTTP" then
|
221
|
+
http = DoroHttp.new(flowdeep)
|
222
|
+
|
223
|
+
if http.method =~ /GET|POST/
|
224
|
+
LOGGER_PARSER.info "HTTP", "FOUND an HTTP request".white
|
225
|
+
LOGGER_PARSER.info "HTTP", "HTTP #{http.method}".white + " #{http.uri}".yellow
|
226
|
+
|
227
|
+
t = http.uri.split('/')
|
228
|
+
filename = (t[t.length - 1].nil? ? "noname-#{flow.id}" : t[t.length - 1])
|
229
|
+
|
230
|
+
if http.method =~ /POST/
|
231
|
+
role_values = [CCDROP, flow.dst.address]
|
232
|
+
@insertdb.insert("host_roles", role_values ) unless @insertdb.select("host_roles", "role", role_values[0], "host_ip", role_values[1]).one?
|
233
|
+
http.data = xtractr.flowcontent(flow.id)
|
234
|
+
LOGGER_PARSER.debug "DB", "POST DATA SAVED IN THE DB"
|
235
|
+
end
|
236
|
+
|
237
|
+
if http.contype =~ /application/ # STORING ONLY application* type GET DATA (avoid html pages, etc)
|
238
|
+
LOGGER_PARSER.info "HTTP", "FOUND an Application Type".white
|
239
|
+
LOGGER_PARSER.debug "DB", " Inserting #{filename} downloaded file info" if VERBOSE
|
240
|
+
|
241
|
+
#download
|
242
|
+
flowdeep.each do |flow|
|
243
|
+
flow.contents.each do |c|
|
244
|
+
|
245
|
+
LOGGER_PARSER.debug("DB", "Inserting downloaded http file info from #{flow.dst.address.to_s}".blue) if VERBOSE
|
246
|
+
|
247
|
+
downvalues = [ DoroFile.sha2(c.body), flowid, downloadir, filename ]
|
248
|
+
@insertdb.insert("downloads", downvalues )
|
249
|
+
|
250
|
+
role_values = [CCSUPPORT, flow.dst.address]
|
251
|
+
@insertdb.insert("host_roles", role_values ) unless @insertdb.select("host_roles", "role", role_values[0], "host_ip", role_values[1]).one?
|
252
|
+
|
253
|
+
LOGGER_PARSER.debug "HTTP", "Saving downloaded file into #{downloadir}".white if VERBOSE
|
254
|
+
c.save("#{downloadir}/#{filename}")
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
httpvalues = "default, '#{http.method.downcase}', '#{http.uri}', #{http.size}, #{http.ssl}, #{flowid}, E'#{Insertdb.escape_bytea(http.data)}' "
|
263
|
+
|
264
|
+
LOGGER_PARSER.debug "DB", " Inserting http data info from #{flow.dst.address.to_s}".blue if VERBOSE
|
265
|
+
@insertdb.raw_insert("http_data", httpvalues)
|
266
|
+
|
267
|
+
else
|
268
|
+
LOGGER_PARSER.warn "HTTP", "Not a regular HTTP traffic on flow #{flow.id}".yellow
|
269
|
+
LOGGER_PARSER.info "PARSER", "Trying to guess if it is IRC".white
|
270
|
+
|
271
|
+
if Parser.guess(streamdata.inspect).class.inspect =~ /IRC/
|
272
|
+
ircvalues = search_irc(streamdata)
|
273
|
+
ircvalues.each do |ircvalue|
|
274
|
+
LOGGER_PARSER.debug "DB", " Inserting IRC DATA info from #{flow.dst.address.to_s}".blue if VERBOSE
|
275
|
+
@insertdb.raw_insert("irc_data", ircvalue )
|
276
|
+
role_values = [CCIRC, flow.dst.address]
|
277
|
+
@insertdb.insert("host_roles", role_values ) unless @insertdb.select("host_roles", "role", role_values[0], "host_ip", role_values[1]).one?
|
278
|
+
end
|
279
|
+
|
280
|
+
else
|
281
|
+
LOGGER_PARSER.info "PARSER", "NO-IRC".red
|
282
|
+
#TODO, store UNKNOWN communication data
|
283
|
+
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
|
288
|
+
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
#case MAIL
|
293
|
+
when "SMTP" then
|
294
|
+
LOGGER_PARSER.info "SMTP", "FOUND an SMTP request..".white
|
295
|
+
#insert mail
|
296
|
+
#by from to subject data id time connection
|
297
|
+
|
298
|
+
|
299
|
+
streamdata.each do |m|
|
300
|
+
mailfrom = 'null'
|
301
|
+
mailto = 'null'
|
302
|
+
mailcontent = 'null'
|
303
|
+
mailsubject = 'null'
|
304
|
+
mailhcmd = 'null'
|
305
|
+
mailhcont = 'null'
|
306
|
+
rdata = ['null', 'null']
|
307
|
+
|
308
|
+
case m[1]
|
309
|
+
when 0
|
310
|
+
if Parser::SMTP.header?(m[0])
|
311
|
+
@email = Parser::SMTP.new(m[0])
|
312
|
+
LOGGER_PARSER.info "SMTP", "[A]".white + @email.hcmd + " " + @email.hcont
|
313
|
+
if Parser::SMTP.hasbody?(m[0])
|
314
|
+
@email.body = Parser::SMTP.body(m[0])
|
315
|
+
mailto = @email.body.to
|
316
|
+
mailfrom = @email.body.from
|
317
|
+
mailsubject = @email.body.subject.gsub(/'/,"") #xtool bug ->')
|
318
|
+
end
|
319
|
+
end
|
320
|
+
when 1
|
321
|
+
rdata = Parser::SMTP.response(m[0]) if Parser::SMTP.response(m[0])
|
322
|
+
LOGGER_PARSER.info "SMTP", "[R]".white + rdata[0] + " " + rdata[1]
|
323
|
+
rdata[0] = 'null' if rdata[0].empty?
|
324
|
+
rdata[1] = 'null' if rdata[1].empty?
|
325
|
+
|
326
|
+
end
|
327
|
+
mailvalues = [mailfrom, mailto, mailsubject, mailcontent, "default", flowid, mailhcmd, mailhcont, rdata[0], rdata[1].gsub(/'/,"")] #xtool bug ->')
|
328
|
+
@insertdb.insert("emails", mailvalues )
|
329
|
+
end
|
330
|
+
|
331
|
+
|
332
|
+
|
333
|
+
#case FTP
|
334
|
+
when "FTP" then
|
335
|
+
LOGGER_PARSER.info "FTP", "FOUND an FTP request".white
|
336
|
+
#TODO
|
337
|
+
when "TCP" then
|
338
|
+
|
339
|
+
LOGGER_PARSER.info "TCP", "FOUND GENERIC TCP TRAFFIC - may be a netbios scan".white
|
340
|
+
LOGGER_PARSER.info "PARSER", "Trying see if it is IRC traffic".white
|
341
|
+
|
342
|
+
if Parser.guess(streamdata.inspect).class.inspect =~ /IRC/
|
343
|
+
ircvalues = search_irc(streamdata)
|
344
|
+
ircvalues.each do |ircvalue|
|
345
|
+
LOGGER_PARSER.debug "DB", " Inserting IRC DATA info from #{flow.dst.address.to_s}".blue if VERBOSE
|
346
|
+
@insertdb.raw_insert("irc_data", ircvalue )
|
347
|
+
role_values = [CCIRC, flow.dst.address]
|
348
|
+
@insertdb.insert("host_roles", role_values ) unless @insertdb.select("host_roles", "role", CCIRC, "host_ip", flow.dst.address).one?
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
|
353
|
+
else
|
354
|
+
|
355
|
+
LOGGER_PARSER.info "PARSER", "Unknown traffic, try see if it is IRC traffic"
|
356
|
+
|
357
|
+
if Parser.guess(streamdata.inspect).class.inspect =~ /IRC/
|
358
|
+
ircvalues = search_irc(streamdata)
|
359
|
+
ircvalues.each do |ircvalue|
|
360
|
+
LOGGER_PARSER.debug "DB", " Inserting IRC DATA info from #{flow.dst.address.to_s}".blue if VERBOSE
|
361
|
+
@insertdb.raw_insert("irc_data", ircvalue )
|
362
|
+
role_values = [CCIRC, flow.dst.address]
|
363
|
+
@insertdb.insert("host_roles", role_values ) unless @insertdb.select("host_roles", "role", CCIRC, "host_ip", flow.dst.address).one?
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
end
|
368
|
+
|
369
|
+
when 17 then
|
370
|
+
#check if DNS
|
371
|
+
|
372
|
+
#Layer 4 analysis
|
373
|
+
case service
|
374
|
+
when "DNS" then
|
375
|
+
#DEBUG
|
376
|
+
#puts "DNS"
|
377
|
+
|
378
|
+
@i = 0
|
379
|
+
@p = []
|
380
|
+
|
381
|
+
flowdeep.each do |flow|
|
382
|
+
flow.each do |pkt|
|
383
|
+
@p[@i] = pkt.payload
|
384
|
+
@i = @i + 1
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
|
389
|
+
@p.each do |d|
|
390
|
+
|
391
|
+
begin
|
392
|
+
|
393
|
+
dns = DoroDNS.new(d)
|
394
|
+
|
395
|
+
|
396
|
+
dnsvalues = ["default", dns.name, dns.cls_i.inspect, dns.qry?, dns.ttl, flowid, dns.address.to_s, dns.data, dns.type_i.inspect]
|
397
|
+
|
398
|
+
LOGGER_PARSER.debug "DB", " Inserting DNS data from #{flow.dst.address.to_s}".blue if VERBOSE
|
399
|
+
unless @insertdb.insert("dns_data", dnsvalues )
|
400
|
+
LOGGER_PARSER.error "DB", " Error while Inserting DNS data".blue
|
401
|
+
nex
|
402
|
+
end
|
403
|
+
dnsid = @insertdb.find_seq("dns_id_seq").first['currval']
|
404
|
+
|
405
|
+
if dns.qry?
|
406
|
+
LOGGER_PARSER.info "DNS", "DNS Query:".white + " #{dns.name}".yellow + " class #{dns.cls_i} type #{dns.type_i}"
|
407
|
+
else
|
408
|
+
dns_list.merge!( dns.address.to_s => dnsid)
|
409
|
+
LOGGER_PARSER.info "DNS", "DNS Answer:".white + " #{dns.name}".yellow + " class #{dns.cls} type #{dns.type} ttl #{dns.ttl} " + "#{dns.address}".yellow
|
410
|
+
end
|
411
|
+
|
412
|
+
rescue
|
413
|
+
|
414
|
+
LOGGER_PARSER.error "DB", "Something went wrong while adding a DNS entry into the DB (packet malformed?) - The packet will be skipped ::."
|
415
|
+
LOGGER_PARSER.debug "DB", "#{$!}"
|
416
|
+
end
|
417
|
+
|
418
|
+
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
when 1 then
|
423
|
+
#TODO: ICMP data
|
424
|
+
#case ICMP xtractr.flows('flow.service:SMTP').first.proto = 1
|
425
|
+
else
|
426
|
+
|
427
|
+
LOGGER_PARSER.warn "PARSER", "Unknown protocol: #{flow.id} -- Proto #{flow.proto}".yellow
|
428
|
+
|
429
|
+
end
|
430
|
+
|
431
|
+
|
432
|
+
}
|
433
|
+
|
434
|
+
#DEBUG
|
435
|
+
#puts "save?"
|
436
|
+
#gets
|
437
|
+
@insertdb.set_analyzed(dump['hash'])
|
438
|
+
@insertdb.commit
|
439
|
+
@insertdb.close
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
|
444
|
+
def self.start(daemon)
|
445
|
+
daemon ||= false
|
446
|
+
|
447
|
+
puts "[DoroParser]".yellow + " Started, tail log file to see some stuff.."
|
448
|
+
LOGGER_PARSER.info "Dorothy", "Started".yellow
|
449
|
+
|
450
|
+
if daemon
|
451
|
+
check_pid_file DoroSettings.env[:pidfile]
|
452
|
+
puts "[DoroParser]".yellow + " Going in backround with pid #{Process.pid}"
|
453
|
+
Process.daemon
|
454
|
+
create_pid_file DoroSettings.env[:pidfile]
|
455
|
+
LOGGER_PARSER.info "DoroParser", "Going in backround with pid #{Process.pid}"
|
456
|
+
end
|
457
|
+
|
458
|
+
|
459
|
+
@insertdb = Insertdb.new
|
460
|
+
infinite = true
|
461
|
+
|
462
|
+
while infinite
|
463
|
+
pcaps = @insertdb.find_pcap
|
464
|
+
analyze_bintraffic pcaps
|
465
|
+
infinite = daemon
|
466
|
+
sleep DTIMEOUT if daemon # Sleeping a while if -d wasn't set, then quit.
|
467
|
+
end
|
468
|
+
LOGGER_PARSER.info "DoroParser" , "There are no more files to analyze.".yellow
|
469
|
+
exit(0)
|
470
|
+
end
|
471
|
+
|
472
|
+
def check_pid_file file
|
473
|
+
if File.exist? file
|
474
|
+
# If we get Errno::ESRCH then process does not exist and
|
475
|
+
# we can safely cleanup the pid file.
|
476
|
+
pid = File.read(file).to_i
|
477
|
+
begin
|
478
|
+
Process.kill(0, pid)
|
479
|
+
rescue Errno::ESRCH
|
480
|
+
stale_pid = true
|
481
|
+
rescue
|
482
|
+
end
|
483
|
+
|
484
|
+
unless stale_pid
|
485
|
+
puts "[DoroParser]".yellow + " Dorothy is already running (pid=#{pid})"
|
486
|
+
exit
|
487
|
+
end
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
def create_pid_file file
|
492
|
+
File.open(file, "w") { |f| f.puts Process.pid }
|
493
|
+
|
494
|
+
# Remove pid file during shutdown
|
495
|
+
at_exit do
|
496
|
+
LOGGER_PARSER.info "DoroParser", "Shutting down." rescue nil
|
497
|
+
if File.exist? file
|
498
|
+
File.unlink file
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
# Sends SIGTERM to process in pidfile. Server should trap this
|
504
|
+
# and shutdown cleanly.
|
505
|
+
def self.stop
|
506
|
+
LOGGER_PARSER.info "DoroParser", "Shutting down.."
|
507
|
+
pid_file = DoroSettings.env[:pidfile]
|
508
|
+
if pid_file and File.exist? pid_file
|
509
|
+
pid = Integer(File.read(pid_file))
|
510
|
+
Process.kill -15, -pid
|
511
|
+
puts "[DoroParser]".yellow + " Process #{pid} terminated"
|
512
|
+
LOGGER_PARSER.info "DoroParser", "Process #{pid} terminated"
|
513
|
+
else
|
514
|
+
puts "[DoroParser]".yellow + "Can't find PID file, is Dorothy really running?"
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
end
|