dorothy2 1.0.9 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|