dorothy2 1.0.9 → 1.1.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.
- data/README.md +3 -2
- data/UPDATE +2 -2
- data/bin/dorothy_start +18 -16
- data/etc/extensions.yml +1 -1
- data/lib/doroParser.rb +0 -2
- data/lib/dorothy2.rb +176 -152
- data/lib/dorothy2/NAM.rb +21 -2
- data/lib/dorothy2/VSM.rb +2 -3
- data/lib/dorothy2/do-utils.rb +10 -3
- data/lib/dorothy2/version.rb +1 -1
- metadata +4 -4
data/README.md
CHANGED
@@ -4,6 +4,7 @@ A malware/botnet analysis framework written in Ruby.
|
|
4
4
|
|
5
5
|
For a perfect view of this document (images and links), open it through the project's [code repository](https://github.com/m4rco-/dorothy2/blob/master/README.md).
|
6
6
|
|
7
|
+
For any issue, use our [Redmine](https://redmine.honeynet.it/projects/dorothy2)
|
7
8
|
##Introduction
|
8
9
|
|
9
10
|
Dorothy2 is a framework created for mass malware analysis. Currently, it is mainly based on analyzing the network behavior of a virtual machine where a suspicious executable was executed.
|
@@ -108,7 +109,7 @@ It is recommended to follow this step2step process:
|
|
108
109
|
|
109
110
|
#visudo
|
110
111
|
add the following line:
|
111
|
-
dorothy ALL = NOPASSWD: /usr/sbin/tcpdump, /bin/kill
|
112
|
+
dorothy ALL = NOPASSWD: /usr/sbin/tcpdump, /bin/kill, /usr/bin/killall
|
112
113
|
|
113
114
|
* If you want to install pcapr on this machine (if you want to use dorohy from a MacOSX machine, you have to do it) install also these packages (refer to this blog [post](https://github.com/pcapr-local/pcapr-local) for a detailed howto). However, if you are installing Dorothy into a Linux machine, I recommended you to install pcapr on the same machine where the Dorothy gem was installed.
|
114
115
|
|
@@ -283,7 +284,7 @@ Below there are some tips about how understand the root-cause of your crash.
|
|
283
284
|
|
284
285
|
> $dorothy_start -v -s malwarefolder
|
285
286
|
|
286
|
-
3.
|
287
|
+
3. If any error occours, go to our Redmine and raise a [bug-ticket](https://redmine.honeynet.it/projects/dorothy2/)!
|
287
288
|
|
288
289
|
------------------------------------------
|
289
290
|
|
data/UPDATE
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#######################################
|
2
|
-
#Updating from Dorothy 1.0.x to 1.
|
2
|
+
#Updating from Dorothy 1.0.x to >= 1.0.9##
|
3
3
|
#######################################
|
4
4
|
|
5
|
-
Dorothy 1.
|
5
|
+
Dorothy 1.0.9 introduces several features that improve the overall framework.
|
6
6
|
Below, the recommended steps needed to update your Dorothy environment.
|
7
7
|
|
8
8
|
a) Remove the Dorothy configuration file
|
data/bin/dorothy_start
CHANGED
@@ -120,9 +120,9 @@ LOGGER.sev_threshold = DoroSettings.env[:loglevel]
|
|
120
120
|
|
121
121
|
|
122
122
|
if opts[:baseline]
|
123
|
-
puts "[DOROTHY]".yellow + "Creating a new process baseline."
|
123
|
+
puts "[" + "+".red + "] " + "[DOROTHY]".yellow + "Creating a new process baseline."
|
124
124
|
Dorothy.run_baseline
|
125
|
-
puts "[WARNING]".red + "Baseline run finished."
|
125
|
+
puts "[" + "+".red + "] " + "[WARNING]".red + "Baseline run finished."
|
126
126
|
exit(0)
|
127
127
|
end
|
128
128
|
|
@@ -140,7 +140,7 @@ baseline_procs = home + '/etc/baseline_processes.yml'
|
|
140
140
|
|
141
141
|
if opts[:DorothiveInit]
|
142
142
|
Util.init_db(opts[:DorothiveInit])
|
143
|
-
puts "[Dorothy]".yellow + " Database loaded, now you can restart Dorothy!"
|
143
|
+
puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Database loaded, now you can restart Dorothy!"
|
144
144
|
exit(0)
|
145
145
|
end
|
146
146
|
|
@@ -149,12 +149,12 @@ begin
|
|
149
149
|
db = Insertdb.new
|
150
150
|
rescue => e
|
151
151
|
if e.inspect =~ /exist/
|
152
|
-
puts "WARNING".yellow + " The database doesn't exist yet. Press Enter to load the ddl into the DB"
|
152
|
+
puts "[" + "+".red + "] " + "WARNING".yellow + " The database doesn't exist yet. Press Enter to load the ddl into the DB"
|
153
153
|
gets
|
154
154
|
Util.init_db(DoroSettings.dorothive[:ddl])
|
155
155
|
exit(0)
|
156
156
|
else
|
157
|
-
puts "ERROR".red + " Can't connect to the database"
|
157
|
+
puts "[" + "+".red + "] " + "ERROR".red + " Can't connect to the database"
|
158
158
|
puts e
|
159
159
|
exit(0)
|
160
160
|
end
|
@@ -162,9 +162,9 @@ end
|
|
162
162
|
|
163
163
|
|
164
164
|
if opts[:SandboxUpdate]
|
165
|
-
puts "[Dorothy]".yellow + " Loading #{sboxfile} into Dorothive"
|
165
|
+
puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Loading #{sboxfile} into Dorothive"
|
166
166
|
DoroConfig.init_sandbox(sboxfile)
|
167
|
-
puts "[Dorothy]".yellow + " Done."
|
167
|
+
puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Done."
|
168
168
|
exit(0)
|
169
169
|
end
|
170
170
|
|
@@ -178,20 +178,20 @@ if Util.exists?(sfile)
|
|
178
178
|
end
|
179
179
|
end
|
180
180
|
else
|
181
|
-
puts "[WARNING]".red + " A source file doesn't exist, please crate one into #{home}/etc. See the example file in #{HOME}/etc/sources.yml.example"
|
181
|
+
puts "[" + "+".red + "] " + "[WARNING]".red + " A source file doesn't exist, please crate one into #{home}/etc. See the example file in #{HOME}/etc/sources.yml.example"
|
182
182
|
exit(0)
|
183
183
|
end
|
184
184
|
|
185
185
|
unless Util.exists?(sboxfile)
|
186
|
-
puts "[WARNING]".red + " There is no sandbox configured yet. Please do it now."
|
186
|
+
puts "[" + "+".red + "] " + "[WARNING]".red + " There is no sandbox configured yet. Please do it now."
|
187
187
|
DoroConfig.create_sandbox(sboxfile)
|
188
188
|
DoroConfig.init_sandbox(sboxfile)
|
189
189
|
end
|
190
190
|
|
191
191
|
unless Util.exists?(baseline_procs)
|
192
|
-
puts "[WARNING]".red + " There is no process-baseline file yet, Dorothy is going to create one."
|
192
|
+
puts "[" + "+".red + "] " + "[WARNING]".red + " There is no process-baseline file yet, Dorothy is going to create one."
|
193
193
|
Dorothy.run_baseline
|
194
|
-
puts "[WARNING]".red + "Baseline run finished."
|
194
|
+
puts "[" + "+".red + "] " + "[WARNING]".red + " Baseline run finished."
|
195
195
|
exit(0)
|
196
196
|
end
|
197
197
|
|
@@ -199,13 +199,13 @@ BASELINE_PROCS = YAML.load_file(baseline_procs)
|
|
199
199
|
|
200
200
|
#Check DB sandbox data
|
201
201
|
if db.table_empty?("sandboxes")
|
202
|
-
puts "[WARNING]".red + " No sandbox found in Dorothive, the DB will be filled with " + sboxfile
|
202
|
+
puts "[" + "+".red + "] " + "[WARNING]".red + " No sandbox found in Dorothive, the DB will be filled with " + sboxfile
|
203
203
|
DoroConfig.init_sandbox(sboxfile)
|
204
204
|
end
|
205
205
|
|
206
206
|
if opts[:source] && !sources.key?(opts[:source])
|
207
|
-
puts "[WARNING]".red + " The selected source is not yet configured.\nThe available sources are: "
|
208
|
-
puts sources.keys
|
207
|
+
puts "[" + "+".red + "] " + "[WARNING]".red + " The selected source is not yet configured.\nThe available sources are: "
|
208
|
+
puts "[" + "+".red + "] " + sources.keys
|
209
209
|
exit(0)
|
210
210
|
end
|
211
211
|
|
@@ -213,9 +213,11 @@ db.close
|
|
213
213
|
|
214
214
|
begin
|
215
215
|
Dorothy.start sources[opts[:source]], daemon
|
216
|
+
rescue SignalException
|
217
|
+
Dorothy.stop_running_analyses
|
216
218
|
rescue => e
|
217
|
-
puts "[Dorothy]".yellow + " An error occurred: ".red + $!
|
218
|
-
puts "[Dorothy]".yellow + " For more information check the logfile" + $! if daemon
|
219
|
+
puts "[" + "+".red + "] " + "[Dorothy]".yellow + " An error occurred: ".red + $!
|
220
|
+
puts "[" + "+".red + "] " + "[Dorothy]".yellow + " For more information check the logfile" + $! if daemon
|
219
221
|
LOGGER.error "Dorothy", "An error occurred: " + $!
|
220
222
|
LOGGER.debug "Dorothy", "#{e.inspect} --BACKTRACE: #{e.backtrace}"
|
221
223
|
LOGGER.info "Dorothy", "Dorothy has been stopped"
|
data/etc/extensions.yml
CHANGED
@@ -17,7 +17,7 @@ dll:
|
|
17
17
|
prog_path: C:\windows\system32\rundll32.exe
|
18
18
|
prog_args:
|
19
19
|
|
20
|
-
html:
|
20
|
+
html: # c:\Program Files\Internet Explorer\iexplore.exe" -new
|
21
21
|
prog_name: Microsoft Explorer IEXPLORE.EXE
|
22
22
|
prog_path: C:\windows\system32\cmd.exe
|
23
23
|
prog_args: /C start "C:\\Programmi\\Internet Explorer\\IEXPLORE.EXE"
|
data/lib/doroParser.rb
CHANGED
data/lib/dorothy2.rb
CHANGED
@@ -44,6 +44,7 @@ module Dorothy
|
|
44
44
|
|
45
45
|
|
46
46
|
def start_analysis(bins)
|
47
|
+
#Create a mutex for monitoring the access to the methods
|
47
48
|
@binum = bins.size
|
48
49
|
bins.each do |bin|
|
49
50
|
next unless check_support(bin)
|
@@ -157,7 +158,7 @@ module Dorothy
|
|
157
158
|
LOGGER.info "VSM",vm_log_header + "Copying #{bin.md5} to VM"
|
158
159
|
|
159
160
|
filecontent = File.open(bin.binpath, "rb") { |byte| byte.read } #load filebinary
|
160
|
-
vsm.copy_file(bin.
|
161
|
+
vsm.copy_file(bin.full_filename,filecontent) #Using full_filename, we do want to be sure that it has an extension
|
161
162
|
|
162
163
|
#Start Sniffer
|
163
164
|
dumpname = anal_id.to_s + "-" + bin.md5
|
@@ -170,20 +171,22 @@ module Dorothy
|
|
170
171
|
@screenshots = Array.new
|
171
172
|
|
172
173
|
#Execute File into VM
|
173
|
-
LOGGER.info "VSM",vm_log_header + "Executing #{bin.
|
174
|
+
LOGGER.info "VSM",vm_log_header + "Executing #{bin.full_filename} with #{EXTENSIONS[bin.extension]["prog_name"]}"
|
174
175
|
|
175
176
|
if MANUAL
|
176
177
|
LOGGER.debug "VSM",vm_log_header + " MANUAL mode detected. You can now logon to rdp:// "
|
177
178
|
|
178
|
-
|
179
|
+
menu="
|
179
180
|
|
180
181
|
Choose your next action:
|
181
182
|
1) Take Screenshot
|
182
183
|
2) Take ProcessList
|
183
|
-
3) Execute #{bin.
|
184
|
+
3) Execute #{bin.full_filename}
|
184
185
|
0) Continue and revert the machine.
|
185
186
|
|
186
187
|
Select a nuber:"
|
188
|
+
|
189
|
+
LOGGER.info "MANUAL-MODE",vm_log_header + menu
|
187
190
|
answer = gets.chop
|
188
191
|
|
189
192
|
until answer == "0"
|
@@ -194,12 +197,15 @@ module Dorothy
|
|
194
197
|
when "2"
|
195
198
|
@current_procs = vsm.get_running_procs
|
196
199
|
LOGGER.info "MANUAL-MODE",vm_log_header + "Current ProcessList taken"
|
200
|
+
@current_procs.each_key do |pid|
|
201
|
+
LOGGER.info "MANUAL-MODE", vm_log_header + "[" + "+".red + "]" + " PID: #{pid}, NAME: #{@current_procs[pid]["pname"]}, COMMAND: #{@current_procs[pid]["cmdLine"]}"
|
202
|
+
end
|
197
203
|
when "3"
|
198
|
-
guestpid = vsm.exec_file("C:\\#{bin.
|
204
|
+
guestpid = vsm.exec_file("C:\\#{bin.full_filename}",EXTENSIONS[bin.extension])
|
199
205
|
LOGGER.debug "MANUAL-MODE",vm_log_header + "Program executed with PID #{guestpid}"
|
200
206
|
#when "x" then -- More interactive actions to add
|
201
207
|
else
|
202
|
-
LOGGER.info "MANUAL-MODE",vm_log_header +
|
208
|
+
LOGGER.info "MANUAL-MODE",vm_log_header + menu
|
203
209
|
end
|
204
210
|
answer = gets.chop
|
205
211
|
end
|
@@ -208,7 +214,7 @@ module Dorothy
|
|
208
214
|
|
209
215
|
|
210
216
|
else
|
211
|
-
guestpid = vsm.exec_file("C:\\#{bin.
|
217
|
+
guestpid = vsm.exec_file("C:\\#{bin.full_filename}",EXTENSIONS[bin.extension])
|
212
218
|
LOGGER.debug "VSM",vm_log_header + "Program executed with PID #{guestpid}" if VERBOSE
|
213
219
|
sleep 1
|
214
220
|
returncode = vsm.get_status(guestpid)
|
@@ -231,7 +237,7 @@ module Dorothy
|
|
231
237
|
|
232
238
|
|
233
239
|
#Stop Sniffer, revert the VM
|
234
|
-
stop_nam_revertvm(@nam, pid, vsm,
|
240
|
+
stop_nam_revertvm(@nam, pid, vsm, reverted, vm_log_header)
|
235
241
|
|
236
242
|
vsm.revert_vm
|
237
243
|
reverted = true
|
@@ -338,9 +344,9 @@ module Dorothy
|
|
338
344
|
FileUtils.rm(bin.binpath)
|
339
345
|
LOGGER.info "VSM", vm_log_header + "Process compleated successfully"
|
340
346
|
|
341
|
-
rescue SignalException
|
347
|
+
rescue SignalException, RuntimeError
|
342
348
|
LOGGER.warn "DOROTHY", "SIGINT".red + " Catched, exiting gracefully."
|
343
|
-
stop_nam_revertvm(@nam, pid, vsm,
|
349
|
+
stop_nam_revertvm(@nam, pid, vsm, reverted, vm_log_header)
|
344
350
|
LOGGER.debug "VSM", vm_log_header + "Removing working dir"
|
345
351
|
FileUtils.rm_r(sample_home)
|
346
352
|
if in_transaction
|
@@ -354,7 +360,7 @@ module Dorothy
|
|
354
360
|
|
355
361
|
LOGGER.warn "Dorothy", vm_log_header + "Stopping NAM instances if presents, reverting the Sandbox, and removing working directory"
|
356
362
|
|
357
|
-
stop_nam_revertvm(@nam, pid, vsm,
|
363
|
+
stop_nam_revertvm(@nam, pid, vsm, reverted, vm_log_header)
|
358
364
|
LOGGER.debug "VSM", vm_log_header + "Removing working dir"
|
359
365
|
|
360
366
|
FileUtils.rm_r(sample_home)
|
@@ -372,83 +378,90 @@ module Dorothy
|
|
372
378
|
end
|
373
379
|
|
374
380
|
#Stop NAM instance and Revert VM
|
375
|
-
def stop_nam_revertvm(nam, pid, vsm,
|
381
|
+
def stop_nam_revertvm(nam, pid, vsm, reverted, vm_log_header)
|
376
382
|
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
383
|
+
if pid
|
384
|
+
LOGGER.info "VSM", vm_log_header + " Stopping sniffing module " + pid.to_s
|
385
|
+
nam.stop_sniffer(pid)
|
386
|
+
end
|
381
387
|
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
388
|
+
unless reverted || vsm.nil?
|
389
|
+
LOGGER.info "VSM", vm_log_header + " Reverting VM"
|
390
|
+
vsm.revert_vm
|
391
|
+
sleep 3 #wait some seconds for letting the vm revert..
|
392
|
+
end
|
386
393
|
end
|
387
|
-
end
|
388
394
|
|
389
395
|
###Create Baseline
|
390
|
-
def run_baseline
|
396
|
+
def self.run_baseline
|
391
397
|
db = Insertdb.new
|
398
|
+
db.vm_init
|
392
399
|
guestvm = db.find_vm
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
400
|
+
if guestvm
|
401
|
+
begin
|
402
|
+
LOGGER.info "VSM","VM#{guestvm[0]}".red + " Executng the baseline run"
|
403
|
+
vsm = Doro_VSM::ESX.new(DoroSettings.esx[:host],DoroSettings.esx[:user],DoroSettings.esx[:pass],guestvm[1], guestvm[3], guestvm[4])
|
404
|
+
vsm.check_internet
|
405
|
+
LOGGER.info "VSM","VM#{guestvm[0]}".red + " Sleeping #{DoroSettings.sandbox[:sleeptime]} seconds".yellow
|
406
|
+
sleep DoroSettings.sandbox[:sleeptime]
|
407
|
+
vsm.get_running_procs(nil, true) #save on file
|
408
|
+
LOGGER.info "VSM", "VM#{guestvm[0]} ".red + "Reverting VM".yellow
|
409
|
+
vsm.revert_vm
|
410
|
+
db.free_vm(guestvm[0])
|
411
|
+
db.close
|
412
|
+
rescue => e
|
413
|
+
LOGGER.error "VSM", "VM#{guestvm[0]} ".yellow + "An error occurred while performing the BASELINE run, please retry"
|
414
|
+
LOGGER.debug "Dorothy" , "VM#{guestvm[0]} ".yellow + "#{$!}\n #{e.inspect} \n #{e.backtrace}" if VERBOSE
|
415
|
+
LOGGER.warn "VSM", "VM#{guestvm[0]} ".yellow + "[RECOVER] Reverting VM"
|
416
|
+
vsm.revert_vm
|
417
|
+
db.free_vm(guestvm[0])
|
418
|
+
db.close
|
419
|
+
end
|
420
|
+
else
|
421
|
+
LOGGER.fatal "VSM", "[CRITICAL]".red + " There are no free VM at the moment..how it is possible?"
|
422
|
+
end
|
423
|
+
end
|
411
424
|
|
412
425
|
########################
|
413
426
|
## VTOTAL SCAN ####
|
414
427
|
########################
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
428
|
+
private
|
429
|
+
def scan(bin)
|
430
|
+
#puts "TOTAL", "Forking for VTOTAL"
|
431
|
+
@vtotal_threads << Thread.new(bin.sha) {
|
432
|
+
LOGGER.info "VTOTAL", "Scanning file #{bin.md5}".yellow
|
420
433
|
|
421
|
-
|
422
|
-
|
434
|
+
vt = Vtotal.new
|
435
|
+
id = vt.analyze_file(bin.binpath)
|
423
436
|
|
424
|
-
|
437
|
+
LOGGER.debug "VTOTAL", "Sleeping"
|
425
438
|
|
426
|
-
|
439
|
+
sleep 15
|
427
440
|
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
441
|
+
until vt.get_report(id)
|
442
|
+
LOGGER.info "VTOTAL", "Waiting a while and keep retring..."
|
443
|
+
sleep 30
|
444
|
+
end
|
432
445
|
|
433
|
-
|
434
|
-
|
446
|
+
LOGGER.info "VTOTAL", "#{bin.md5} Detection Rate: #{vt.rate}"
|
447
|
+
LOGGER.info "VTOTAL", "#{bin.md5} Family by McAfee: #{vt.family}"
|
435
448
|
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
449
|
+
LOGGER.info "VTOTAL", "Updating DB"
|
450
|
+
vtvalues = [bin.sha, vt.family, vt.vendor, vt.version, vt.rate, vt.updated, vt.detected]
|
451
|
+
db = Insertdb.new
|
452
|
+
db.begin
|
453
|
+
begin
|
454
|
+
db.insert("malwares", vtvalues)
|
455
|
+
db.close
|
456
|
+
rescue
|
457
|
+
db.rollback
|
458
|
+
LOGGER.error "VTOTAL", "Error while inserting values in malware table"
|
459
|
+
end
|
447
460
|
|
448
|
-
|
449
|
-
|
461
|
+
#TODO upload evidence to RT
|
462
|
+
}
|
450
463
|
|
451
|
-
|
464
|
+
end
|
452
465
|
|
453
466
|
|
454
467
|
|
@@ -456,118 +469,129 @@ end
|
|
456
469
|
## MAIN #
|
457
470
|
#########################
|
458
471
|
|
459
|
-
|
472
|
+
def self.start(source=nil, daemon=nil)
|
460
473
|
|
461
|
-
|
462
|
-
|
474
|
+
@db = Insertdb.new
|
475
|
+
daemon ||= false
|
463
476
|
|
464
|
-
|
477
|
+
puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Process Started"
|
465
478
|
|
466
479
|
|
467
|
-
|
480
|
+
LOGGER.info "Dorothy", "Started".yellow
|
468
481
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
482
|
+
if daemon
|
483
|
+
check_pid_file DoroSettings.env[:pidfile]
|
484
|
+
puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Going in backround with pid #{Process.pid}"
|
485
|
+
puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Logging on #{DoroSettings.env[:logfile]}"
|
486
|
+
Process.daemon
|
487
|
+
create_pid_file DoroSettings.env[:pidfile]
|
488
|
+
puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Going in backround with pid #{Process.pid}"
|
489
|
+
end
|
477
490
|
|
478
|
-
|
479
|
-
|
491
|
+
#Creating a new NAM object for managing the sniffer
|
492
|
+
@nam = Doro_NAM.new(DoroSettings.nam)
|
493
|
+
#Be sure that there are no open tcpdump instances opened
|
494
|
+
@nam.init_sniffer
|
480
495
|
|
481
|
-
|
482
|
-
|
483
|
-
|
496
|
+
@vtotal_threads = []
|
497
|
+
@vtotal_threads = []
|
498
|
+
@analysis_threads = []
|
484
499
|
|
485
|
-
|
500
|
+
infinite = true
|
486
501
|
|
487
|
-
|
488
|
-
|
502
|
+
#be sure that all the vm are available by forcing their release
|
503
|
+
@db.vm_init
|
489
504
|
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
start_analysis(dfm.bins)
|
494
|
-
infinite = daemon #exit if wasn't set
|
495
|
-
wait_end
|
496
|
-
LOGGER.info "Dorothy", "SLEEPING" if daemon
|
497
|
-
sleep DoroSettings.env[:dtimeout] if daemon # Sleeping a while if -d wasn't set, then quit.
|
498
|
-
end
|
499
|
-
else # no sources specified, analyze all of them
|
500
|
-
while infinite #infinite loop
|
501
|
-
sources = YAML.load_file(DoroSettings.env[:home] + '/etc/sources.yml')
|
502
|
-
sources.keys.each do |sname|
|
503
|
-
dfm = DorothyFetcher.new(sources[sname])
|
505
|
+
if source # a source has been specified
|
506
|
+
while infinite #infinite loop
|
507
|
+
dfm = DorothyFetcher.new(source)
|
504
508
|
start_analysis(dfm.bins)
|
509
|
+
infinite = daemon #exit if wasn't set
|
510
|
+
wait_end
|
511
|
+
LOGGER.info "Dorothy", "SLEEPING" if daemon
|
512
|
+
sleep DoroSettings.env[:dtimeout] if daemon # Sleeping a while if -d wasn't set, then quit.
|
513
|
+
end
|
514
|
+
else # no sources specified, analyze all of them
|
515
|
+
while infinite #infinite loop
|
516
|
+
sources = YAML.load_file(DoroSettings.env[:home] + '/etc/sources.yml')
|
517
|
+
sources.keys.each do |sname|
|
518
|
+
dfm = DorothyFetcher.new(sources[sname])
|
519
|
+
start_analysis(dfm.bins)
|
520
|
+
end
|
521
|
+
infinite = daemon #exit if wasn't set
|
522
|
+
wait_end
|
523
|
+
LOGGER.info "Dorothy", "SLEEPING" if daemon
|
524
|
+
sleep DoroSettings.env[:dtimeout].to_i if daemon # Sleeping a while if -d wasn't set, then quit.
|
505
525
|
end
|
506
|
-
infinite = daemon #exit if wasn't set
|
507
|
-
wait_end
|
508
|
-
LOGGER.info "Dorothy", "SLEEPING" if daemon
|
509
|
-
sleep DoroSettings.env[:dtimeout].to_i if daemon # Sleeping a while if -d wasn't set, then quit.
|
510
526
|
end
|
511
|
-
end
|
512
527
|
|
513
|
-
|
528
|
+
@db.close
|
514
529
|
|
515
|
-
|
530
|
+
end
|
516
531
|
|
517
|
-
|
532
|
+
def wait_end
|
518
533
|
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
534
|
+
unless @vtotal_threads.empty?
|
535
|
+
@vtotal_threads.each { |aThread| aThread.join}
|
536
|
+
LOGGER.info "VTOTAL","Process compleated successfully"
|
537
|
+
end
|
523
538
|
|
524
|
-
|
525
|
-
|
539
|
+
@analysis_threads.each { |aThread| aThread.join }
|
540
|
+
LOGGER.info "Dorothy", "Process finished"
|
526
541
|
|
527
|
-
|
542
|
+
end
|
528
543
|
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
544
|
+
def check_pid_file(file)
|
545
|
+
if File.exist? file
|
546
|
+
# If we get Errno::ESRCH then process does not exist and
|
547
|
+
# we can safely cleanup the pid file.
|
548
|
+
pid = File.read(file).to_i
|
549
|
+
begin
|
550
|
+
Process.kill(0, pid)
|
551
|
+
rescue Errno::ESRCH
|
552
|
+
stale_pid = true
|
553
|
+
end
|
539
554
|
|
540
|
-
|
541
|
-
|
542
|
-
|
555
|
+
unless stale_pid
|
556
|
+
puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Dorothy is already running (pid=#{pid})"
|
557
|
+
exit(1)
|
558
|
+
end
|
543
559
|
end
|
544
560
|
end
|
545
|
-
end
|
546
561
|
|
547
|
-
|
548
|
-
|
562
|
+
def create_pid_file(file)
|
563
|
+
File.open(file, "w") { |f| f.puts Process.pid }
|
549
564
|
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
565
|
+
# Remove pid file during shutdown
|
566
|
+
at_exit do
|
567
|
+
LOGGER.info "Dorothy", "Shutting down." rescue nil
|
568
|
+
if File.exist? file
|
569
|
+
File.unlink file
|
570
|
+
end
|
555
571
|
end
|
556
572
|
end
|
557
|
-
end
|
558
573
|
|
574
|
+
def self.stop_running_analyses
|
575
|
+
LOGGER.info "Dorothy", "Killing curent live analysis threads.."
|
576
|
+
@analysis_threads.each { |aThread|
|
577
|
+
aThread.raise
|
578
|
+
aThread.join
|
579
|
+
}
|
580
|
+
end
|
559
581
|
## Sends SIGTERM to process in pidfile. Server should trap this
|
560
582
|
# and shutdown cleanly.
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
583
|
+
def self.stop
|
584
|
+
puts "[" + "+".red + "]" + " Dorothy is shutting now.."
|
585
|
+
LOGGER.info "Dorothy", "Shutting down."
|
586
|
+
pid_file = DoroSettings.env[:pidfile]
|
587
|
+
if pid_file and File.exist? pid_file
|
588
|
+
pid = Integer(File.read(pid_file))
|
589
|
+
Process.kill(-2,-pid)
|
590
|
+
LOGGER.info "Dorothy", "Process #{pid} terminated"
|
591
|
+
puts "[" + "+".red + "]" + " Dorothy Process #{pid} terminated"
|
592
|
+
else
|
593
|
+
LOGGER.info "Dorothy", "Can't find PID file, is Dorothy really running?"
|
594
|
+
end
|
570
595
|
end
|
571
|
-
end
|
572
596
|
|
573
|
-
end
|
597
|
+
end
|
data/lib/dorothy2/NAM.rb
CHANGED
@@ -22,12 +22,31 @@ module Dorothy
|
|
22
22
|
Net::SSH.start(@server, @user, :password => @pass, :port =>@port) do |@ssh|
|
23
23
|
MANUAL ? not_rdp = "and not port 3389" : not_rdp = ""
|
24
24
|
@ssh.exec "nohup sudo tcpdump -i #{interface} -s 1514 -w #{pcaphome}/#{name}.pcap host #{vmaddress} #{not_rdp} 2> log.tmp & "
|
25
|
-
|
26
|
-
|
25
|
+
|
26
|
+
begin
|
27
|
+
t = @ssh.exec!"ps aux |grep #{name}|grep -v grep|grep -v bash"
|
28
|
+
pid = t.split(" ")[1]
|
29
|
+
rescue
|
30
|
+
r = 0
|
31
|
+
if r <= 2
|
32
|
+
r = r+1
|
33
|
+
LOGGER.warn "NSM", " NAM has failed to catch the Tcpdump PID, retry n. #{r}/3"
|
34
|
+
sleep 2
|
35
|
+
retry
|
36
|
+
end
|
37
|
+
LOGGER.warn "NSM", " NAM has failed to catch the Tcpdump PID, retry n. #{r}/3"
|
38
|
+
raise
|
39
|
+
end
|
27
40
|
return pid.to_i
|
28
41
|
end
|
29
42
|
end
|
30
43
|
|
44
|
+
def init_sniffer
|
45
|
+
Net::SSH.start(@server, @user, :password => @pass, :port =>@port) do |ssh|
|
46
|
+
ssh.exec "sudo killall tcpdump"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
31
50
|
def stop_sniffer(pid)
|
32
51
|
Net::SSH.start(@server, @user, :password => @pass, :port =>@port) do |ssh|
|
33
52
|
ssh.exec "sudo kill -2 #{pid}"
|
data/lib/dorothy2/VSM.rb
CHANGED
@@ -6,7 +6,6 @@ module Dorothy
|
|
6
6
|
|
7
7
|
#Dorothy module-class for managig the virtual sandboxes
|
8
8
|
class Doro_VSM
|
9
|
-
|
10
9
|
#ESX vSphere5 interface
|
11
10
|
class ESX
|
12
11
|
|
@@ -73,7 +72,7 @@ module Dorothy
|
|
73
72
|
|
74
73
|
def exec_file(filename, program)
|
75
74
|
program["prog_args"].nil? ? args = "" : args = program["prog_args"]
|
76
|
-
args
|
75
|
+
args += " #{filename}"
|
77
76
|
cmd = { :programPath => program["prog_path"], :arguments => args }
|
78
77
|
pid = @pm.StartProgramInGuest(:vm => @vm , :auth => @auth, :spec => cmd )
|
79
78
|
pid.to_i
|
@@ -100,7 +99,7 @@ module Dorothy
|
|
100
99
|
@pp2 = Hash.new
|
101
100
|
procs = @pm.ListProcessesInGuest(:vm => @vm , :auth => @auth, :pids => pid )
|
102
101
|
procs.each {|pp2| @pp2.merge! Hash[pp2.pid, Hash["pname", pp2.name, "owner", pp2.owner, "cmdLine", pp2.cmdLine, "startTime", pp2.startTime, "endTime", pp2.endTime, "exitCode", pp2.exitCode]]}
|
103
|
-
|
102
|
+
if save_tofile
|
104
103
|
Util.write(filename, @pp2.to_yaml)
|
105
104
|
LOGGER.info "VSM", "Current running processes saved to #{filename}"
|
106
105
|
end
|
data/lib/dorothy2/do-utils.rb
CHANGED
@@ -5,7 +5,6 @@
|
|
5
5
|
module Dorothy
|
6
6
|
|
7
7
|
module Util
|
8
|
-
|
9
8
|
extend self
|
10
9
|
|
11
10
|
def write(file, string)
|
@@ -89,6 +88,8 @@ module Dorothy
|
|
89
88
|
value1 = value
|
90
89
|
elsif value =~ /currval/
|
91
90
|
value1 = value
|
91
|
+
elsif table == "sys_procs" #avoiding noising escape-issue for \u
|
92
|
+
value1 = "E'#{value.inspect}'"
|
92
93
|
else
|
93
94
|
#if present, remove ""
|
94
95
|
value.gsub! /^"|"$/, '' if values.class.inspect == "String"
|
@@ -230,6 +231,7 @@ module Dorothy
|
|
230
231
|
attr_reader :md5
|
231
232
|
attr_reader :binpath
|
232
233
|
attr_reader :filename
|
234
|
+
attr_reader :full_filename #here i'm sure that the file has an extension and can be executed by windows
|
233
235
|
attr_reader :ctime
|
234
236
|
attr_reader :size
|
235
237
|
attr_reader :pcapsize
|
@@ -266,20 +268,25 @@ module Dorothy
|
|
266
268
|
@ctime= timetmp.strftime("%m/%d/%y %H:%M:%S")
|
267
269
|
@type = fm.file(file)
|
268
270
|
|
271
|
+
|
269
272
|
if @extension.nil? #no extension, trying to put the right one..
|
270
273
|
case @type
|
271
274
|
when /^PE32/ then
|
272
275
|
@extension = (@type =~ /DLL/ ? "dll" : "exe")
|
276
|
+
when /^COM/ then
|
277
|
+
@extension = "exe"
|
273
278
|
when /^MS-DOS/ then
|
274
279
|
@extension = "bat"
|
275
280
|
when /^HTML/ then
|
276
281
|
@extension = "html"
|
277
282
|
else
|
278
|
-
@extension =
|
283
|
+
@extension = "unknown"
|
279
284
|
end
|
285
|
+
@full_filename = @filename + "." + @extension
|
286
|
+
else
|
287
|
+
@full_filename = @filename
|
280
288
|
end
|
281
289
|
|
282
|
-
|
283
290
|
@size = File.size(file)
|
284
291
|
end
|
285
292
|
|
data/lib/dorothy2/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dorothy2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 1.0.9
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- marco riccardi
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2013-
|
18
|
+
date: 2013-09-24 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: net-scp
|