dorothy2 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/CHANGELOG +39 -14
- data/README.md +80 -62
- data/UPDATE +6 -14
- data/bin/dorothy2 +472 -0
- data/dorothy2.gemspec +22 -16
- data/etc/ddl/dorothive.ddl +619 -373
- data/etc/sources.yml.example +27 -2
- data/lib/doroGUI.rb +232 -0
- data/lib/doroParser.rb +34 -78
- data/lib/dorothy2.rb +288 -248
- data/lib/dorothy2/BFM.rb +114 -61
- data/lib/dorothy2/DEM.rb +3 -1
- data/lib/dorothy2/NAM.rb +2 -2
- data/lib/dorothy2/Settings.rb +2 -1
- data/lib/dorothy2/VSM.rb +2 -1
- data/lib/dorothy2/deep_symbolize.rb +2 -7
- data/lib/dorothy2/do-init.rb +286 -19
- data/lib/dorothy2/do-logger.rb +1 -1
- data/lib/dorothy2/do-utils.rb +382 -33
- data/lib/dorothy2/version.rb +1 -1
- data/lib/dorothy2/vtotal.rb +30 -20
- data/lib/mu/xtractr.rb +11 -11
- data/lib/mu/xtractr/stream.rb +1 -1
- data/lib/www/public/reset.css +153 -0
- data/lib/www/public/style.css +65 -0
- data/lib/www/views/analyses.erb +28 -0
- data/lib/www/views/email.erb +63 -0
- data/lib/www/views/flows.erb +30 -0
- data/lib/www/views/layout.erb +27 -0
- data/lib/www/views/profile.erb +49 -0
- data/lib/www/views/queue.erb +28 -0
- data/lib/www/views/resume.erb +135 -0
- data/lib/www/views/resume.erb~ +88 -0
- data/lib/www/views/samples.erb +20 -0
- data/lib/www/views/upload.erb +154 -0
- data/share/img/The_big_picture.pdf +0 -0
- data/test/tc_dorothy_full.rb +3 -0
- metadata +169 -70
- data/TODO +0 -27
- data/bin/dorothy_start +0 -225
- data/bin/dorothy_stop +0 -28
- data/bin/dparser_start +0 -94
- data/bin/dparser_stop +0 -31
- data/etc/dorothy copy.yml.example +0 -39
- data/etc/extensions.yml +0 -41
- data/share/update-dorothive.sql +0 -19
data/lib/dorothy2/BFM.rb
CHANGED
@@ -3,35 +3,43 @@
|
|
3
3
|
# See the file 'LICENSE' for copying permission.
|
4
4
|
|
5
5
|
|
6
|
-
|
7
|
-
###BINARY FETCHER MODULE###
|
8
|
-
|
9
|
-
|
6
|
+
#############################
|
7
|
+
### BINARY FETCHER MODULE ###
|
8
|
+
#############################
|
9
|
+
|
10
10
|
#The BFM module is in charge of retreiving the binary from the sources configured in the sources.yml file.
|
11
11
|
#It receive the source hash, and return the downloaded binaries objects.
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
|
12
17
|
module Dorothy
|
13
18
|
|
14
19
|
class DorothyFetcher
|
15
|
-
attr_reader :
|
20
|
+
attr_reader :added
|
16
21
|
|
17
|
-
#
|
18
|
-
def initialize(
|
22
|
+
#Source_arr is an array e.g.: ["webgui", {"type"=>"system", "localdir"=>"/Users/akira/Downloads/doroth2_1.9.3_mail/opt/bins/webgui", "typeid"=>1}]
|
23
|
+
def initialize(source_arr)
|
19
24
|
ndownloaded = 0
|
20
25
|
|
21
|
-
@
|
22
|
-
|
26
|
+
@added = Hash.new
|
27
|
+
source = source_arr[1] #source_arr[1] is a hash
|
28
|
+
|
29
|
+
source["priority"] ||= 0
|
30
|
+
source["profile"] ||= "default"
|
31
|
+
|
23
32
|
|
24
33
|
case source["type"]
|
25
34
|
|
26
35
|
when "ssh" then
|
27
|
-
LOGGER.info "BFM", " Fetching trojan from > Honeypot"
|
28
36
|
#file = "/opt/dionaea/var/dionaea/binaries/"
|
29
37
|
#puts "Start to download malware"
|
30
38
|
|
31
39
|
files = []
|
32
40
|
|
33
41
|
begin
|
34
|
-
Net::SSH.start(source["ip"], source["
|
42
|
+
Net::SSH.start(source["ip"], source["username"], :password => source["pass"], :port => source["port"]) do |ssh|
|
35
43
|
ssh.scp.download!(source["remotedir"],source["localdir"], :recursive => true) do |ch, name, sent, total|
|
36
44
|
unless files.include? "#{source["localdir"]}/" + File.basename(name)
|
37
45
|
ndownloaded += 1
|
@@ -55,7 +63,7 @@ module Dorothy
|
|
55
63
|
|
56
64
|
begin
|
57
65
|
unless DoroSettings.env[:testmode]
|
58
|
-
Net::SSH.start(source["
|
66
|
+
Net::SSH.start(source["host"], source["user"], :password => source["pass"], :port => source["port"]) do |ssh|
|
59
67
|
ssh.exec "mv #{source["remotedir"]}/* #{source["remotedir"]}/../analyzed "
|
60
68
|
end
|
61
69
|
end
|
@@ -64,78 +72,123 @@ module Dorothy
|
|
64
72
|
end
|
65
73
|
|
66
74
|
files.each do |f|
|
67
|
-
|
75
|
+
begin
|
76
|
+
@added = QueueManager.add(f, source_arr[0], source["profile"], source["priority"])
|
77
|
+
rescue
|
78
|
+
LOGGER.error "BFM", "Error while adding the bin to the queue, skipping."
|
79
|
+
LOGGER.debug "BFM", $!
|
80
|
+
next
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
#Thanks to Salvatore Gervino who made the first PoC of this source-module: http://www.honeynet.it/wp-content/uploads/Mentored_Projects/salvatore_gervino-dorothy2_email.pdf
|
86
|
+
when "mail" then
|
87
|
+
|
88
|
+
@db = Insertdb.new
|
89
|
+
|
90
|
+
account = {:address=>source["host"], :username=>source["username"],
|
91
|
+
:password=>source["password"], :port=>source["port"], :ssl=>source["enable_ssl"], :n_emails => source["n_emails"] , :delete_once_downloaded => source["delete_once_downloaded"]}
|
92
|
+
|
93
|
+
|
94
|
+
mailer = Dorothy::Mailer.new(account)
|
95
|
+
begin
|
96
|
+
emails = mailer.get_emails
|
97
|
+
|
98
|
+
emails.each do |email|
|
99
|
+
|
100
|
+
LOGGER.debug "BFM", "Analyzing email: #{email.date} - #{email.from_addrs[0]} - #{email.subject} - #{email.to_addrs[0]}"
|
101
|
+
|
102
|
+
|
103
|
+
unless email.attachments.empty?
|
104
|
+
mail_id = @db.push_email_data(email)
|
105
|
+
|
106
|
+
attachment_content_type = Mail::ContentTypeElement.new(email.attachments.first.content_type)
|
107
|
+
#if the attachment is a forwarded email, treat the attachment as the original email
|
108
|
+
if attachment_content_type.main_type == 'message'
|
109
|
+
|
110
|
+
LOGGER.info "BFM", "Forwarded email from #{email.from.first} found"
|
111
|
+
email = mailer.read_from_string email.attachments.first.body.decoded
|
112
|
+
mail_id = @db.push_email_data(email, mail_id)
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
email.attachments.each do | attachment |
|
118
|
+
LOGGER.info "BFM", "Attachment found: #{attachment.filename} "
|
119
|
+
bin = source["localdir"] + "/" + Digest::MD5.hexdigest(attachment.body.decoded) + "_" + attachment.filename
|
120
|
+
Util.write( bin, attachment.body.decoded)
|
121
|
+
id = QueueManager.add(bin, source_arr[0],source["profile"], source["priority"], mail_id)
|
122
|
+
@added.store(id,[bin, source["priority"], source["profile"], source_arr[0]])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end #end for
|
127
|
+
@db.close
|
128
|
+
LOGGER.debug "BFM", "Analyzing email: End "
|
129
|
+
rescue => e
|
130
|
+
LOGGER.error "BFM", "Error while adding the bin to the queue, skipping. #{$!}"
|
131
|
+
LOGGER.debug "DB", e.backtrace
|
68
132
|
end
|
69
133
|
|
70
134
|
when "system" then
|
71
|
-
LOGGER.info "BFM", "Fetching trojan from > filesystem: " + source["localdir"]
|
72
135
|
empty = true
|
73
136
|
Dir.foreach(source["localdir"]) do |file|
|
74
137
|
bin = source["localdir"] + "/" + file
|
75
|
-
next if File.directory?(bin)
|
76
|
-
|
138
|
+
next if File.directory?(bin)
|
139
|
+
|
140
|
+
begin
|
141
|
+
id = QueueManager.add(bin, source_arr[0], source["profile"], source["priority"])
|
142
|
+
empty = false
|
143
|
+
@added.store(id,[bin, source["priority"], source["profile"], source_arr[0]])
|
144
|
+
|
145
|
+
rescue
|
146
|
+
LOGGER.error "BFM", "Error while adding the bin to the queue, skipping."
|
147
|
+
LOGGER.debug "BFM", $!
|
148
|
+
next
|
149
|
+
end
|
150
|
+
|
77
151
|
end
|
78
|
-
LOGGER.
|
152
|
+
LOGGER.debug "BFM", "No binaries were found in the selected source" if empty
|
79
153
|
else
|
80
|
-
LOGGER.fatal "BFM", "Source #{
|
154
|
+
LOGGER.fatal "BFM", "Source type #{source["type"]} is not yet configured"
|
81
155
|
end
|
82
156
|
end
|
83
157
|
|
84
|
-
|
85
|
-
def
|
158
|
+
#Expects an Hash as input
|
159
|
+
def self.loader(sources, daemon=false)
|
86
160
|
|
87
|
-
|
88
|
-
bin = Loadmalw.new(f)
|
89
|
-
if bin.size == 0 || bin.sha.empty?
|
90
|
-
LOGGER.warn "BFM", "Warning - Empty file #{filename}, deleting and skipping.."
|
91
|
-
FileUtils.rm bin.binpath
|
92
|
-
return false
|
93
|
-
end
|
161
|
+
infinite = true
|
94
162
|
|
95
|
-
samplevalues = [bin.sha, bin.size, bin.dir_bin, filename, bin.md5, bin.type ]
|
96
|
-
sighvalues = [bin.sha, typeid, bin.ctime, "null"]
|
97
163
|
|
98
164
|
begin
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
return false
|
104
|
-
end
|
105
|
-
|
106
|
-
#FileUtils.rm(bin.binpath)
|
107
|
-
@bins.push bin
|
108
|
-
end
|
109
|
-
|
165
|
+
while infinite #infinite loop
|
166
|
+
sources.each do |sname|
|
167
|
+
#skip if it is webgui
|
168
|
+
next if sname.first == 'webgui'
|
110
169
|
|
170
|
+
LOGGER.debug "BFM", "Start to fetch binaries from #{sname.first.yellow} @ #{sname[1]["localdir"]}"
|
111
171
|
|
112
|
-
|
172
|
+
added = self.new(sname).added
|
173
|
+
LOGGER.info "BFM", "#{added.size.to_s.yellow} binaries retreived from #{sname.first.yellow}"
|
113
174
|
|
114
|
-
|
115
|
-
|
175
|
+
added.each do |b|
|
176
|
+
LOGGER.debug "BFM", "#{b[0]}\t#{File.basename(b[1][0])}\t#{b[1][3]}"
|
177
|
+
end
|
116
178
|
|
117
|
-
|
118
|
-
|
179
|
+
end
|
180
|
+
if daemon
|
181
|
+
LOGGER.info "BFM", "SLEEPING 10"
|
182
|
+
sleep DoroSettings.bfm[:sleeptime].to_i
|
183
|
+
end
|
184
|
+
infinite = daemon
|
185
|
+
end
|
119
186
|
|
120
|
-
|
121
|
-
|
122
|
-
LOGGER.warn "BFM", " The binary #{samplevalues[0]} has been already added on #{date}"
|
187
|
+
rescue SignalException #, RuntimeError
|
188
|
+
LOGGER.warn "BFM", "SIGINT".red + " Catched [1], exiting gracefully."
|
123
189
|
end
|
124
|
-
|
125
|
-
raise "A DB error occurred" unless db.insert("sightings", sighvalues)
|
126
|
-
|
127
|
-
# explanation: I don't want to insert/analyze the same malware but I do want to
|
128
|
-
# insert the sighting value anyway ("the malware X has been downloaded 1 time but
|
129
|
-
# has been spoted 32 times")
|
130
|
-
|
131
|
-
db.commit
|
132
|
-
db.close
|
133
|
-
true
|
134
|
-
|
135
190
|
end
|
136
191
|
|
137
|
-
|
138
|
-
|
139
192
|
end
|
140
193
|
|
141
194
|
end
|
data/lib/dorothy2/DEM.rb
CHANGED
@@ -54,12 +54,14 @@ module DoroParser
|
|
54
54
|
|
55
55
|
|
56
56
|
|
57
|
-
class SMTP #todo to redo
|
57
|
+
class SMTP #todo to redo ##NEW PARSER CODED BY SALVATORE
|
58
58
|
attr_reader :hcmd
|
59
59
|
attr_reader :hcont
|
60
60
|
attr_accessor :body
|
61
61
|
attr_accessor :rdata
|
62
62
|
|
63
|
+
|
64
|
+
|
63
65
|
def self.body(data)
|
64
66
|
email = TMail::Mail.parse(data)
|
65
67
|
return email
|
data/lib/dorothy2/NAM.rb
CHANGED
@@ -43,14 +43,14 @@ module Dorothy
|
|
43
43
|
|
44
44
|
def init_sniffer
|
45
45
|
Net::SSH.start(@server, @user, :password => @pass, :port =>@port) do |ssh|
|
46
|
-
ssh.exec "sudo killall tcpdump"
|
46
|
+
ssh.exec "nohup sudo killall tcpdump 2>/dev/null"
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
50
|
def stop_sniffer(pid)
|
51
51
|
Net::SSH.start(@server, @user, :password => @pass, :port =>@port) do |ssh|
|
52
52
|
ssh.exec "sudo kill -2 #{pid}"
|
53
|
-
|
53
|
+
LOGGER.debug "NAM", "Tcpdump instance #{pid} stopped"
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
data/lib/dorothy2/Settings.rb
CHANGED
@@ -10,7 +10,8 @@ module Dorothy
|
|
10
10
|
attr_reader :_settings
|
11
11
|
|
12
12
|
def load!(filename, options = {})
|
13
|
-
|
13
|
+
t = YAML::load_file(filename).extend DeepSymbolizable
|
14
|
+
newsets = t.deep_symbolize
|
14
15
|
newsets = newsets[options[:env].to_sym] if \
|
15
16
|
options[:env] && \
|
16
17
|
newsets[options[:env].to_sym]
|
data/lib/dorothy2/VSM.rb
CHANGED
@@ -106,7 +106,8 @@ module Dorothy
|
|
106
106
|
@pp2
|
107
107
|
end
|
108
108
|
|
109
|
-
def get_new_procs(current_procs,
|
109
|
+
def get_new_procs(current_procs, original_procs_file)
|
110
|
+
original_procs = YAML.load_file(original_procs_file)
|
110
111
|
@new_procs = Hash.new
|
111
112
|
current_procs.each_key {|pid|
|
112
113
|
@new_procs.merge!(Hash[pid, current_procs[pid]]) unless original_procs.has_key?(pid)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# From https://gist.github.com/morhekil/998709
|
1
2
|
# Symbolizes all of hash's keys and subkeys.
|
2
3
|
# Also allows for custom pre-processing of keys (e.g. downcasing, etc)
|
3
4
|
# if the block is given:
|
@@ -13,10 +14,6 @@
|
|
13
14
|
|
14
15
|
module DeepSymbolizable
|
15
16
|
|
16
|
-
class Hash
|
17
|
-
include DeepSymbolizable
|
18
|
-
end
|
19
|
-
|
20
17
|
def deep_symbolize(&block)
|
21
18
|
method = self.class.to_s.downcase.to_sym
|
22
19
|
syms = DeepSymbolizable::Symbolizers
|
@@ -62,6 +59,4 @@ module DeepSymbolizable
|
|
62
59
|
end
|
63
60
|
end
|
64
61
|
|
65
|
-
end
|
66
|
-
|
67
|
-
class Hash; include DeepSymbolizable; end
|
62
|
+
end
|
data/lib/dorothy2/do-init.rb
CHANGED
@@ -19,6 +19,7 @@ module Dorothy
|
|
19
19
|
Dir.mkdir("#{home}/opt")
|
20
20
|
Dir.mkdir("#{home}/opt/bins")
|
21
21
|
Dir.mkdir("#{home}/opt/analyzed")
|
22
|
+
Dir.mkdir("#{home}/opt/analyzed/bins")
|
22
23
|
end
|
23
24
|
unless Util.exists?("#{home}/etc")
|
24
25
|
Dir.mkdir("#{home}/etc")
|
@@ -50,6 +51,8 @@ module Dorothy
|
|
50
51
|
conf["virustotal"] = Hash.new
|
51
52
|
conf["esx"] = Hash.new
|
52
53
|
conf["pcapr"] = Hash.new
|
54
|
+
conf["wgui"] = Hash.new
|
55
|
+
conf["bfm"] = Hash.new
|
53
56
|
|
54
57
|
|
55
58
|
################################################
|
@@ -64,14 +67,15 @@ module Dorothy
|
|
64
67
|
|
65
68
|
puts "The Dorothy home directory is #{home}"
|
66
69
|
|
67
|
-
conf["env"]["
|
70
|
+
conf["env"]["pidfiles"] = "#{home}/var"
|
68
71
|
conf["env"]["pidfile_parser"] = "#{home}/var/doroParser.pid"
|
69
72
|
conf["env"]["analysis_dir"] = "#{home}/opt/analyzed"
|
73
|
+
conf["env"]["bins_repository"] = "#{home}/opt/analyzed/bins"
|
70
74
|
conf["env"]["geoip"] = "#{home}/etc/geo/GeoLiteCity.dat"
|
71
75
|
conf["env"]["geoasn"] = "#{home}/etc/geo/GeoIPASNum.dat"
|
72
76
|
conf["env"]["geoisp"] = "#{home}/etc/geo/GeoIPISP.dat"
|
73
77
|
|
74
|
-
conf["env"]["
|
78
|
+
conf["env"]["sleeptime"] = 5
|
75
79
|
|
76
80
|
conf["env"]["logfile"] = "#{home}/var/log/dorothy.log"
|
77
81
|
conf["env"]["logfile_parser"] = "#{home}/var/log/parser.log"
|
@@ -120,19 +124,7 @@ module Dorothy
|
|
120
124
|
|
121
125
|
puts "\n######### [" + " Sandbox configuration settings ".red + "] #########"
|
122
126
|
|
123
|
-
puts "Insert the time (seconds) that the Sandbox should be run before it's reverted [60]"
|
124
|
-
conf["sandbox"]["sleeptime"] = (t = gets.chop).empty? ? 60 : t
|
125
127
|
|
126
|
-
puts "Insert how many screenshots do you want to take [1]"
|
127
|
-
conf["sandbox"]["num_screenshots"] = (t = gets.chop).empty? ? 1 : t.to_i
|
128
|
-
|
129
|
-
if conf["sandbox"]["num_screenshots"] > 1
|
130
|
-
puts "Insert the time interval (seconds) between each screenshot [5] "
|
131
|
-
conf["sandbox"]["screen2time"] = (t = gets.chop).empty? ? 5 : t
|
132
|
-
end
|
133
|
-
|
134
|
-
puts "After how many seconds do you want to take the first screenshot? [1]"
|
135
|
-
conf["sandbox"]["screen1time"] = (t = gets.chop).empty? ? 1 : t
|
136
128
|
|
137
129
|
puts "Which is the sandox's network? [10.10.10.0/0]"
|
138
130
|
conf["sandbox"]["network"] = (t = gets.chop).empty? ? "10.10.10.0/0" : t
|
@@ -158,7 +150,7 @@ module Dorothy
|
|
158
150
|
conf["nam"]["pass"] = gets.chop
|
159
151
|
|
160
152
|
puts "SSH Port [22] :"
|
161
|
-
conf["nam"]["port"] = (t = gets.chop).empty? ? 22 : t
|
153
|
+
conf["nam"]["port"] = (t = gets.chop).empty? ? 22 : t.to_i
|
162
154
|
|
163
155
|
puts "Folder where to store PCAP files [/home/#{conf["nam"]["user"]}/pcaps]"
|
164
156
|
conf["nam"]["pcaphome"] = (t = gets.chop).empty? ? "/home/#{conf["nam"]["user"]}/pcaps" : t
|
@@ -183,7 +175,30 @@ module Dorothy
|
|
183
175
|
end
|
184
176
|
|
185
177
|
puts "Pcapr HTTP Port [8080]:"
|
186
|
-
conf["pcapr"]["port"] = (t = gets.chop).empty? ? 8080 : t
|
178
|
+
conf["pcapr"]["port"] = (t = gets.chop).empty? ? 8080 : t.to_i
|
179
|
+
|
180
|
+
|
181
|
+
######################################################
|
182
|
+
###WebGUI
|
183
|
+
######################################################
|
184
|
+
puts "\n######### [" + " Web GUI configuration ".red + "] #########"
|
185
|
+
|
186
|
+
puts "IP Address used for listening. Use 0.0.0.0 to allow remote connections [localhost]:"
|
187
|
+
conf["wgui"]["host"] = (t = gets.chop).empty? ? 'localhost' : t.to_s
|
188
|
+
|
189
|
+
puts "TCP port [3435]:"
|
190
|
+
conf["wgui"]["port"] = (t = gets.chop).empty? ? 3435 : t.to_i
|
191
|
+
|
192
|
+
conf["wgui"]["environment"] = "production"
|
193
|
+
conf["wgui"]["logfile"] = "#{home}/var/log/webgui.log"
|
194
|
+
|
195
|
+
######################################################
|
196
|
+
###Binaries Fetcher Module
|
197
|
+
######################################################
|
198
|
+
puts "\n######### [" + " Binaries Fetcher Module ".red + "] #########"
|
199
|
+
|
200
|
+
puts "How often the BFM should pool all the resources (sec)? [60]"
|
201
|
+
conf["bfm"]["sleeptime"] = (t = gets.chop).empty? ? 60 : t.to_i
|
187
202
|
|
188
203
|
|
189
204
|
######################################################
|
@@ -212,8 +227,6 @@ module Dorothy
|
|
212
227
|
File.open("#{File.expand_path("~")}/.dorothy.yml", 'w+') {|f| f.write(conf.to_yaml) }
|
213
228
|
FileUtils.ln_s("#{File.expand_path("~")}/.dorothy.yml", "#{home}/etc/dorothy.yml") unless Util.exists?("#{home}/etc/dorothy.yml")
|
214
229
|
|
215
|
-
#copy the default extension file to the user-defined home
|
216
|
-
FileUtils.cp("#{HOME}/etc/extensions.yml", "#{home}/etc/extensions.yml")
|
217
230
|
correct = true
|
218
231
|
puts "Configuration file has been saved in ~/.dorothy.conf and a symlink has been created in\n#{home}/etc/dorothy.yml for an easier edit."
|
219
232
|
puts "\n######### [" + " Now you can restart dorothy, enjoy! ".yellow + "] #########"
|
@@ -231,6 +244,131 @@ module Dorothy
|
|
231
244
|
|
232
245
|
end
|
233
246
|
|
247
|
+
|
248
|
+
def create_profiles(filename, sandbox=false, vtotal=nil)
|
249
|
+
|
250
|
+
correct = false
|
251
|
+
conf = Hash.new
|
252
|
+
if sandbox
|
253
|
+
|
254
|
+
conf['default'] = {}
|
255
|
+
|
256
|
+
conf['default']['sleeptime'] = 60
|
257
|
+
vtotal ? conf['default']['vtotal_query'] = true : conf['default']['vtotal_query'] = false
|
258
|
+
|
259
|
+
|
260
|
+
conf['default']['screenshots'] = {}
|
261
|
+
conf['default']['screenshots']['number'] = 2
|
262
|
+
conf['default']['screenshots']['delay_first'] = 1
|
263
|
+
conf['default']['screenshots']['delay_inbetween'] = 30
|
264
|
+
|
265
|
+
|
266
|
+
|
267
|
+
conf['default']['OS'] = {}
|
268
|
+
conf['default']['OS']['type'] = sandbox['os']
|
269
|
+
conf['default']['OS']['version'] = sandbox['version']
|
270
|
+
conf['default']['OS']['lang'] = sandbox['os_lang']
|
271
|
+
|
272
|
+
|
273
|
+
|
274
|
+
conf['default']['extensions'] = {}
|
275
|
+
|
276
|
+
%w(exe bat html rtf).each do |ext|
|
277
|
+
conf['default']['extensions'][ext] = Hash.new
|
278
|
+
conf['default']['extensions'][ext]['prog_name'] = 'Windows CMD.exe'
|
279
|
+
conf['default']['extensions'][ext]['prog_path'] = 'C:\windows\system32\cmd.exe'
|
280
|
+
conf['default']['extensions'][ext]['prog_args'] = '/C'
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
File.open(filename, 'w+') {|f| f.write(conf.to_yaml) }
|
285
|
+
puts "Profiles have been saved in #{filename}\nYou can either modify such file directly. Enjoy!"
|
286
|
+
|
287
|
+
else
|
288
|
+
until correct
|
289
|
+
|
290
|
+
finished = false
|
291
|
+
|
292
|
+
until finished
|
293
|
+
|
294
|
+
puts "\n######### [" + " Profiles configuration ".red + "] #########"
|
295
|
+
|
296
|
+
puts "Please insert the unique name for this profile"
|
297
|
+
pname = gets.chop
|
298
|
+
|
299
|
+
conf[pname] = {}
|
300
|
+
conf[pname]['OS'] = {}
|
301
|
+
conf[pname]['screenshots'] = {}
|
302
|
+
conf[pname]['extensions'] = {}
|
303
|
+
|
304
|
+
puts "Please insert the information on the OS you want to associate with this profile. This info must reflect the one inserted into the sandboxes.yml file"
|
305
|
+
puts "OS Type (Windows|Linux) [Windows] "
|
306
|
+
conf[pname]["OS"]['type'] = (t = gets.chop).empty? ? 'Windows' : t
|
307
|
+
puts "OS Version: (e.g. XP SP3) [XP SP3]"
|
308
|
+
conf[pname]["OS"]['version'] = (t = gets.chop).empty? ? 'XP SP3' : t
|
309
|
+
puts "OS Language: [eng]"
|
310
|
+
conf[pname]["OS"]['lang'] = (t = gets.chop).empty? ? 'eng' : t
|
311
|
+
|
312
|
+
puts "Sandbox parameters"
|
313
|
+
puts "Insert the time (seconds) that the Sandbox should be run before it's reverted [60]"
|
314
|
+
conf[pname]["sleeptime"] = (t = gets.chop).empty? ? 60 : t
|
315
|
+
|
316
|
+
puts "Insert how many screenshots do you want to take [1]"
|
317
|
+
conf[pname]['screenshots']["number"] = (t = gets.chop).empty? ? 1 : t.to_i
|
318
|
+
|
319
|
+
if conf[pname]["num_screenshots"] > 1
|
320
|
+
puts "Insert the time interval (seconds) between each screenshot [5] "
|
321
|
+
conf[pname]["screenshots"]['delay_inbetween'] = (t = gets.chop).empty? ? 5 : t.to_i
|
322
|
+
end
|
323
|
+
|
324
|
+
puts "After how many seconds do you want to take the first screenshot? [1]"
|
325
|
+
conf[pname]["screenshots"]['delay_first'] = (t = gets.chop).empty? ? 1 : t.to_i
|
326
|
+
|
327
|
+
|
328
|
+
puts "Enable Virus Total queries? VT API key must be in .dorothy.yml [y]"
|
329
|
+
t = gets.chop
|
330
|
+
(t.empty? || t == "y" || t == "yes") ? conf[pname]["vtotal_query"] = true : conf[pname]["vtotal_query"] = false
|
331
|
+
|
332
|
+
puts "Adding basic extensions (exe, bat, html, rtf)"
|
333
|
+
|
334
|
+
%w(exe bat html rtf).each do |ext|
|
335
|
+
conf[pname]['extensions'][ext] = Hash.new
|
336
|
+
conf[pname]['extensions'][ext]['prog_name'] = 'Windows CMD.exe'
|
337
|
+
conf[pname]['extensions'][ext]['prog_name'] = 'C:\windows\system32\cmd.exe'
|
338
|
+
conf[pname]['extensions'][ext]['prog_name'] = '/C'
|
339
|
+
end
|
340
|
+
|
341
|
+
puts "Profiles configured. Want you to configure another one? [n]"
|
342
|
+
t = gets.chop
|
343
|
+
|
344
|
+
if t == "y" || t == "yes"
|
345
|
+
finished = false
|
346
|
+
else
|
347
|
+
finished = true
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
351
|
+
|
352
|
+
puts "Configuration finished"
|
353
|
+
puts "Confirm? [y]"
|
354
|
+
t = gets.chop
|
355
|
+
puts t
|
356
|
+
|
357
|
+
if t.empty? || t == "y" || t == "yes"
|
358
|
+
File.open(filename, 'w+') {|f| f.write(conf.to_yaml) }
|
359
|
+
correct = true
|
360
|
+
puts "Profiles have been saved in #{filename}\nYou can either modify such file directly. Enjoy!"
|
361
|
+
else
|
362
|
+
puts "Please reinsert the info"
|
363
|
+
correct = false
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
|
370
|
+
|
371
|
+
|
234
372
|
#Creates the sandbox configuration file
|
235
373
|
def create_sandbox(sboxfile)
|
236
374
|
|
@@ -305,6 +443,135 @@ module Dorothy
|
|
305
443
|
end
|
306
444
|
end
|
307
445
|
|
446
|
+
|
447
|
+
|
448
|
+
#Creates the Source configuration file
|
449
|
+
def create_sources(sourcesfile = DoroSettings.env[:home] + '/etc/sources.yml')
|
450
|
+
|
451
|
+
correct = false
|
452
|
+
|
453
|
+
until correct
|
454
|
+
|
455
|
+
conf = Hash.new
|
456
|
+
|
457
|
+
#Add WGUI as default source
|
458
|
+
|
459
|
+
conf['webgui'] = Hash.new
|
460
|
+
conf['webgui']["type"] = 'web'
|
461
|
+
conf['webgui']["typeid"] = 1
|
462
|
+
conf['webgui']["localdir"] = DoroSettings.env[:home] + '/opt/bins/webgui'
|
463
|
+
conf['webgui']["priority"] = 3
|
464
|
+
conf['webgui']["profile"] = 'default'
|
465
|
+
|
466
|
+
finished = false
|
467
|
+
|
468
|
+
until finished
|
469
|
+
puts "Please insert a unique name for the binary source you want to add"
|
470
|
+
sname = gets.chop
|
471
|
+
|
472
|
+
conf[sname] = Hash.new
|
473
|
+
|
474
|
+
puts "Please specify the binary source type (system|ssh|mail) [system]"
|
475
|
+
conf[sname]["type"] = (t = gets.chop).empty? ? "system" : t
|
476
|
+
puts ">" + conf[sname]["type"]
|
477
|
+
|
478
|
+
case conf[sname]["type"]
|
479
|
+
when "system" then
|
480
|
+
puts "Please specify the system folder where are located the binaries [#{DoroSettings.env[:home]}/opt/bins/#{sname}]"
|
481
|
+
conf[sname]["localdir"] = (t = gets.chop).empty? ? "#{DoroSettings.env[:home]}/opt/bins/#{sname}" : t
|
482
|
+
when "mail" then
|
483
|
+
puts "Please specify the IP address/hostname of the mail server (e.g. pop-mail.outlook.com)"
|
484
|
+
conf[sname]["address"] = gets.chop
|
485
|
+
|
486
|
+
puts "Please specify the username used for the authentication"
|
487
|
+
conf[sname]["username"] = gets.chop
|
488
|
+
|
489
|
+
puts "Please specify the password used for the authentication"
|
490
|
+
conf[sname]["password"] = gets.chop
|
491
|
+
|
492
|
+
puts "Please specify the TCP port used by the mailserver [993]"
|
493
|
+
conf[sname]["port"] = (t = gets.chop).empty? ? 993 : t.to_i
|
494
|
+
puts ">" + conf[sname]["port"].to_s
|
495
|
+
|
496
|
+
puts "Is SSL required for this mailbox (true|false)? [true]"
|
497
|
+
t = (gets.chop == "false" ? false : true)
|
498
|
+
conf[sname]["enable_ssl"] = t
|
499
|
+
puts ">" + conf[sname]["enable_ssl"].to_s
|
500
|
+
|
501
|
+
puts "How many emails do you want to retreive during every request? [3]"
|
502
|
+
conf[sname]["n_emails"] = (t = gets.chop).empty? ? 3 : t.to_i
|
503
|
+
puts ">" + conf[sname]["n_emails"].to_s
|
504
|
+
|
505
|
+
puts "Do you want to delete the emails from the server once downloaded? [true] (Warning, if false, Dorothy wont understand which email is new. Put false only for development/testing)"
|
506
|
+
t = (gets.chop == "false" ? false : true)
|
507
|
+
conf[sname]["delete_once_downloaded"] = t
|
508
|
+
puts ">" + conf[sname]["delete_once_downloaded"].to_s
|
509
|
+
|
510
|
+
puts "Please specify the system folder where the attachments will be temporaly copied into [#{DoroSettings.env[:home]}/opt/bins/#{sname}]"
|
511
|
+
conf[sname]["localdir"] = (t = gets.chop).empty? ? "#{DoroSettings.env[:home]}/opt/bins/#{sname}" : t
|
512
|
+
|
513
|
+
when "ssh" then
|
514
|
+
puts "Please specify the IP address/hostname of the remote server"
|
515
|
+
conf[sname]["host"] = gets.chop
|
516
|
+
|
517
|
+
puts "Please specify the ssh TCP port of the remote server [22]"
|
518
|
+
conf[sname]["port"] = (t = gets.chop).empty? ? 22 : t.to_i
|
519
|
+
puts ">" + conf[sname]["port"].to_s
|
520
|
+
|
521
|
+
puts "Please specify the username used for the authentication"
|
522
|
+
conf[sname]["username"] = gets.chop
|
523
|
+
|
524
|
+
puts "Please specify the password used for the authentication"
|
525
|
+
conf[sname]["password"] = gets.chop
|
526
|
+
|
527
|
+
puts "Please specify the remote path where the binaries are"
|
528
|
+
conf[sname]["remotedir"] = gets.chop
|
529
|
+
|
530
|
+
puts "Please specify the system folder where the binaries will be temporaly copied into [#{DoroSettings.env[:home]}/opt/bins/#{sname}]"
|
531
|
+
conf[sname]["localdir"] = (t = gets.chop).empty? ? "#{DoroSettings.env[:home]}/opt/bins/#{sname}" : t
|
532
|
+
end
|
533
|
+
|
534
|
+
|
535
|
+
puts "Please specify the priority of this source. 1 is the lowest [1]"
|
536
|
+
conf[sname]["priority"] = (t = gets.chop).empty? ? 1 : t.to_i
|
537
|
+
puts ">" + conf[sname]["priority"].to_s
|
538
|
+
|
539
|
+
puts "Please specify which analysis profile you want to associate with this source. [default]"
|
540
|
+
conf[sname]["profile"] = (t = gets.chop).empty? ? "default" : t
|
541
|
+
puts ">" + conf[sname]["profile"]
|
542
|
+
|
543
|
+
|
544
|
+
puts "Binary source added. Do you want to add another one? [n]"
|
545
|
+
t = gets.chop
|
546
|
+
|
547
|
+
if t == "y" || t == "yes"
|
548
|
+
finished = false
|
549
|
+
else
|
550
|
+
finished = true
|
551
|
+
end
|
552
|
+
|
553
|
+
|
554
|
+
end
|
555
|
+
|
556
|
+
puts "Configuration finished"
|
557
|
+
puts "Confirm? [y]"
|
558
|
+
t = gets.chop
|
559
|
+
puts t
|
560
|
+
|
561
|
+
if t.empty? || t == "y" || t == "yes"
|
562
|
+
File.open(sourcesfile, 'w+') {|f| f.write(conf.to_yaml) }
|
563
|
+
correct = true
|
564
|
+
puts "Configuration file has been saved in #{sourcesfile}\nYou can either modify such file directly. Enjoy!"
|
565
|
+
else
|
566
|
+
puts "Please reinsert the info"
|
567
|
+
correct = false
|
568
|
+
end
|
569
|
+
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
|
574
|
+
|
308
575
|
#This method will populate the dorothive table sandboxes
|
309
576
|
def init_sandbox(file="../etc/sandboxes.yml")
|
310
577
|
conf = YAML.load_file(file)
|
@@ -312,7 +579,7 @@ module Dorothy
|
|
312
579
|
db = Insertdb.new
|
313
580
|
db.begin_t
|
314
581
|
|
315
|
-
LOGGER.warn "INIT", "
|
582
|
+
LOGGER.warn "INIT", "Warning, the SandBox table is gonna be flushed, and updated with the new file"
|
316
583
|
db.flush_table("sandboxes")
|
317
584
|
|
318
585
|
conf.each_key do |sbox|
|