Skip to content
Browse files

Critical bug fixed (in multithread mode, there was a misused variable…

… which was preventing the VSM to correctly execute files)

Several minus bugs fixed
Improved manual mode (now it prints out the running process)
Added cosmetic improvements to the log interface
  • Loading branch information...
1 parent 270fa82 commit 42182e3fde03106d457e696c795ab895827e13dc @m4rco- committed Sep 22, 2013
Showing with 103 additions and 59 deletions.
  1. +18 −16 bin/dorothy_start
  2. +1 −1 etc/extensions.yml
  3. +51 −34 lib/dorothy2.rb
  4. +21 −2 lib/dorothy2/NAM.rb
  5. +2 −3 lib/dorothy2/VSM.rb
  6. +10 −3 lib/dorothy2/do-utils.rb
View
34 bin/dorothy_start
@@ -120,9 +120,9 @@ LOGGER.sev_threshold = DoroSettings.env[:loglevel]
if opts[:baseline]
- puts "[DOROTHY]".yellow + "Creating a new process baseline."
+ puts "[" + "+".red + "] " + "[DOROTHY]".yellow + "Creating a new process baseline."
Dorothy.run_baseline
- puts "[WARNING]".red + "Baseline run finished."
+ puts "[" + "+".red + "] " + "[WARNING]".red + "Baseline run finished."
exit(0)
end
@@ -140,7 +140,7 @@ baseline_procs = home + '/etc/baseline_processes.yml'
if opts[:DorothiveInit]
Util.init_db(opts[:DorothiveInit])
- puts "[Dorothy]".yellow + " Database loaded, now you can restart Dorothy!"
+ puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Database loaded, now you can restart Dorothy!"
exit(0)
end
@@ -149,22 +149,22 @@ begin
db = Insertdb.new
rescue => e
if e.inspect =~ /exist/
- puts "WARNING".yellow + " The database doesn't exist yet. Press Enter to load the ddl into the DB"
+ puts "[" + "+".red + "] " + "WARNING".yellow + " The database doesn't exist yet. Press Enter to load the ddl into the DB"
gets
Util.init_db(DoroSettings.dorothive[:ddl])
exit(0)
else
- puts "ERROR".red + " Can't connect to the database"
+ puts "[" + "+".red + "] " + "ERROR".red + " Can't connect to the database"
puts e
exit(0)
end
end
if opts[:SandboxUpdate]
- puts "[Dorothy]".yellow + " Loading #{sboxfile} into Dorothive"
+ puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Loading #{sboxfile} into Dorothive"
DoroConfig.init_sandbox(sboxfile)
- puts "[Dorothy]".yellow + " Done."
+ puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Done."
exit(0)
end
@@ -178,44 +178,46 @@ if Util.exists?(sfile)
end
end
else
- 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"
+ 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"
exit(0)
end
unless Util.exists?(sboxfile)
- puts "[WARNING]".red + " There is no sandbox configured yet. Please do it now."
+ puts "[" + "+".red + "] " + "[WARNING]".red + " There is no sandbox configured yet. Please do it now."
DoroConfig.create_sandbox(sboxfile)
DoroConfig.init_sandbox(sboxfile)
end
unless Util.exists?(baseline_procs)
- puts "[WARNING]".red + " There is no process-baseline file yet, Dorothy is going to create one."
+ puts "[" + "+".red + "] " + "[WARNING]".red + " There is no process-baseline file yet, Dorothy is going to create one."
Dorothy.run_baseline
- puts "[WARNING]".red + "Baseline run finished."
+ puts "[" + "+".red + "] " + "[WARNING]".red + "Baseline run finished."
exit(0)
end
BASELINE_PROCS = YAML.load_file(baseline_procs)
#Check DB sandbox data
if db.table_empty?("sandboxes")
- puts "[WARNING]".red + " No sandbox found in Dorothive, the DB will be filled with " + sboxfile
+ puts "[" + "+".red + "] " + "[WARNING]".red + " No sandbox found in Dorothive, the DB will be filled with " + sboxfile
DoroConfig.init_sandbox(sboxfile)
end
if opts[:source] && !sources.key?(opts[:source])
- puts "[WARNING]".red + " The selected source is not yet configured.\nThe available sources are: "
- puts sources.keys
+ puts "[" + "+".red + "] " + "[WARNING]".red + " The selected source is not yet configured.\nThe available sources are: "
+ puts "[" + "+".red + "] " + sources.keys
exit(0)
end
db.close
begin
Dorothy.start sources[opts[:source]], daemon
+rescue SignalException
+ Dorothy.stop_running_analyses
rescue => e
- puts "[Dorothy]".yellow + " An error occurred: ".red + $!
- puts "[Dorothy]".yellow + " For more information check the logfile" + $! if daemon
+ puts "[" + "+".red + "] " + "[Dorothy]".yellow + " An error occurred: ".red + $!
+ puts "[" + "+".red + "] " + "[Dorothy]".yellow + " For more information check the logfile" + $! if daemon
LOGGER.error "Dorothy", "An error occurred: " + $!
LOGGER.debug "Dorothy", "#{e.inspect} --BACKTRACE: #{e.backtrace}"
LOGGER.info "Dorothy", "Dorothy has been stopped"
View
2 etc/extensions.yml
@@ -17,7 +17,7 @@ dll:
prog_path: C:\windows\system32\rundll32.exe
prog_args:
-html:
+html: # c:\Program Files\Internet Explorer\iexplore.exe" -new
prog_name: Microsoft Explorer IEXPLORE.EXE
prog_path: C:\windows\system32\cmd.exe
prog_args: /C start "C:\\Programmi\\Internet Explorer\\IEXPLORE.EXE"
View
85 lib/dorothy2.rb
@@ -44,6 +44,7 @@ def get_time(local=Time.new)
def start_analysis(bins)
+ #Create a mutex for monitoring the access to the methods
@binum = bins.size
bins.each do |bin|
next unless check_support(bin)
@@ -56,12 +57,12 @@ def start_analysis(bins)
db.close
else #Use multithreading
@analysis_threads << Thread.new(bin.filename){
- db = Insertdb.new
- sleep rand(@binum * 2) #OPTIMIZE #REVIEW
- sleep rand(30) while !(guestvm = db.find_vm) #guestvm struct: array ["sandbox id", "sandbox name", "ipaddress", "user", "password"]
- analyze(bin, guestvm)
- db.free_vm(guestvm[0])
- db.close
+ db = Insertdb.new
+ sleep rand(@binum * 2) #OPTIMIZE #REVIEW
+ sleep rand(30) while !(guestvm = db.find_vm) #guestvm struct: array ["sandbox id", "sandbox name", "ipaddress", "user", "password"]
+ analyze(bin, guestvm)
+ db.free_vm(guestvm[0])
+ db.close
}
end
end
@@ -157,7 +158,7 @@ def analyze(bin, guestvm)
LOGGER.info "VSM",vm_log_header + "Copying #{bin.md5} to VM"
filecontent = File.open(bin.binpath, "rb") { |byte| byte.read } #load filebinary
- vsm.copy_file(bin.filename,filecontent)
+ vsm.copy_file(bin.full_filename,filecontent) #Using full_filename, we do want to be sure that it has an extension
#Start Sniffer
dumpname = anal_id.to_s + "-" + bin.md5
@@ -170,20 +171,22 @@ def analyze(bin, guestvm)
@screenshots = Array.new
#Execute File into VM
- LOGGER.info "VSM",vm_log_header + "Executing #{bin.md5} with #{EXTENSIONS["prog_args"]}"
+ LOGGER.info "VSM",vm_log_header + "Executing #{bin.full_filename} with #{EXTENSIONS[bin.extension]["prog_name"]}"
if MANUAL
LOGGER.debug "VSM",vm_log_header + " MANUAL mode detected. You can now logon to rdp:// "
- LOGGER.info "MANUAL-MODE",vm_log_header + "
+ menu="
Choose your next action:
1) Take Screenshot
2) Take ProcessList
- 3) Execute #{bin.filename}
+ 3) Execute #{bin.full_filename}
0) Continue and revert the machine.
Select a nuber:"
+
+ LOGGER.info "MANUAL-MODE",vm_log_header + menu
answer = gets.chop
until answer == "0"
@@ -194,12 +197,15 @@ def analyze(bin, guestvm)
when "2"
@current_procs = vsm.get_running_procs
LOGGER.info "MANUAL-MODE",vm_log_header + "Current ProcessList taken"
+ @current_procs.each_key do |pid|
+ LOGGER.info "MANUAL-MODE", vm_log_header + "[" + "+".red + "]" + " PID: #{pid}, NAME: #{@current_procs[pid]["pname"]}, COMMAND: #{@current_procs[pid]["cmdLine"]}"
+ end
when "3"
- guestpid = vsm.exec_file("C:\\#{bin.filename}",EXTENSIONS[bin.extension])
+ guestpid = vsm.exec_file("C:\\#{bin.full_filename}",EXTENSIONS[bin.extension])
LOGGER.debug "MANUAL-MODE",vm_log_header + "Program executed with PID #{guestpid}"
#when "x" then -- More interactive actions to add
else
- LOGGER.info "MANUAL-MODE",vm_log_header + "Please select a number"
+ LOGGER.info "MANUAL-MODE",vm_log_header + menu
end
answer = gets.chop
end
@@ -208,7 +214,7 @@ def analyze(bin, guestvm)
else
- guestpid = vsm.exec_file("C:\\#{bin.filename}",EXTENSIONS[bin.extension])
+ guestpid = vsm.exec_file("C:\\#{bin.full_filename}",EXTENSIONS[bin.extension])
LOGGER.debug "VSM",vm_log_header + "Program executed with PID #{guestpid}" if VERBOSE
sleep 1
returncode = vsm.get_status(guestpid)
@@ -231,7 +237,7 @@ def analyze(bin, guestvm)
#Stop Sniffer, revert the VM
- stop_nam_revertvm(@nam, pid, vsm, guestvm[0], reverted, vm_log_header)
+ stop_nam_revertvm(@nam, pid, vsm, reverted, vm_log_header)
vsm.revert_vm
reverted = true
@@ -338,9 +344,9 @@ def analyze(bin, guestvm)
FileUtils.rm(bin.binpath)
LOGGER.info "VSM", vm_log_header + "Process compleated successfully"
- rescue SignalException
+ rescue SignalException, RuntimeError
LOGGER.warn "DOROTHY", "SIGINT".red + " Catched, exiting gracefully."
- stop_nam_revertvm(@nam, pid, vsm, guestvm[0], reverted, vm_log_header)
+ stop_nam_revertvm(@nam, pid, vsm, reverted, vm_log_header)
LOGGER.debug "VSM", vm_log_header + "Removing working dir"
FileUtils.rm_r(sample_home)
if in_transaction
@@ -354,7 +360,7 @@ def analyze(bin, guestvm)
LOGGER.warn "Dorothy", vm_log_header + "Stopping NAM instances if presents, reverting the Sandbox, and removing working directory"
- stop_nam_revertvm(@nam, pid, vsm, guestvm[0], reverted, vm_log_header)
+ stop_nam_revertvm(@nam, pid, vsm, reverted, vm_log_header)
LOGGER.debug "VSM", vm_log_header + "Removing working dir"
FileUtils.rm_r(sample_home)
@@ -372,19 +378,19 @@ def analyze(bin, guestvm)
end
#Stop NAM instance and Revert VM
-def stop_nam_revertvm(nam, pid, vsm, guestvm, reverted, vm_log_header)
+ def stop_nam_revertvm(nam, pid, vsm, reverted, vm_log_header)
- if pid
- LOGGER.info "VSM", vm_log_header + " Stopping sniffing module " + pid.to_s
- nam.stop_sniffer(pid)
- end
+ if pid
+ LOGGER.info "VSM", vm_log_header + " Stopping sniffing module " + pid.to_s
+ nam.stop_sniffer(pid)
+ end
- unless reverted || vsm.nil?
- LOGGER.info "VSM", vm_log_header + " Reverting VM"
- vsm.revert_vm
- sleep 3 #wait some seconds for letting the vm revert..
+ unless reverted || vsm.nil?
+ LOGGER.info "VSM", vm_log_header + " Reverting VM"
+ vsm.revert_vm
+ sleep 3 #wait some seconds for letting the vm revert..
+ end
end
-end
###Create Baseline
def run_baseline
@@ -461,22 +467,24 @@ def self.start(source=nil, daemon=nil)
@db = Insertdb.new
daemon ||= false
- puts "[Dorothy]".yellow + " Process Started"
+ puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Process Started"
LOGGER.info "Dorothy", "Started".yellow
if daemon
check_pid_file DoroSettings.env[:pidfile]
- puts "[Dorothy]".yellow + " Going in backround with pid #{Process.pid}"
- puts "[Dorothy]".yellow + " Logging on #{DoroSettings.env[:logfile]}"
+ puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Going in backround with pid #{Process.pid}"
+ puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Logging on #{DoroSettings.env[:logfile]}"
Process.daemon
create_pid_file DoroSettings.env[:pidfile]
- puts "[Dorothy]".yellow + " Going in backround with pid #{Process.pid}"
+ puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Going in backround with pid #{Process.pid}"
end
#Creating a new NAM object for managing the sniffer
@nam = Doro_NAM.new(DoroSettings.nam)
+ #Be sure that there are no open tcpdump instances opened
+ @nam.init_sniffer
@vtotal_threads = []
@vtotal_threads = []
@@ -538,7 +546,7 @@ def check_pid_file(file)
end
unless stale_pid
- puts "[Dorothy]".yellow + " Dorothy is already running (pid=#{pid})"
+ puts "[" + "+".red + "] " + "[Dorothy]".yellow + " Dorothy is already running (pid=#{pid})"
exit(1)
end
end
@@ -549,22 +557,31 @@ def create_pid_file(file)
# Remove pid file during shutdown
at_exit do
- Logger.info "Dorothy", "Shutting down." rescue nil
+ LOGGER.info "Dorothy", "Shutting down." rescue nil
if File.exist? file
File.unlink file
end
end
end
+ def self.stop_running_analyses
+ LOGGER.info "Dorothy", "Killing curent live analysis threads.."
+ @analysis_threads.each { |aThread|
+ aThread.raise
+ aThread.join
+ }
+ end
## Sends SIGTERM to process in pidfile. Server should trap this
# and shutdown cleanly.
def self.stop
+ puts "[" + "+".red + "]" + " Dorothy is shutting now.."
LOGGER.info "Dorothy", "Shutting down."
pid_file = DoroSettings.env[:pidfile]
if pid_file and File.exist? pid_file
pid = Integer(File.read(pid_file))
- Process.kill(-15, -pid)
+ Process.kill(-2,-pid)
LOGGER.info "Dorothy", "Process #{pid} terminated"
+ puts "[" + "+".red + "]" + " Dorothy Process #{pid} terminated"
else
LOGGER.info "Dorothy", "Can't find PID file, is Dorothy really running?"
end
View
23 lib/dorothy2/NAM.rb
@@ -22,12 +22,31 @@ def start_sniffer(vmaddress, interface, name, pcaphome)
Net::SSH.start(@server, @user, :password => @pass, :port =>@port) do |@ssh|
MANUAL ? not_rdp = "and not port 3389" : not_rdp = ""
@ssh.exec "nohup sudo tcpdump -i #{interface} -s 1514 -w #{pcaphome}/#{name}.pcap host #{vmaddress} #{not_rdp} 2> log.tmp & "
- t = @ssh.exec!"ps aux |grep #{vmaddress}|grep -v grep|grep -v bash"
- pid = t.split(" ")[1]
+
+ begin
+ t = @ssh.exec!"ps aux |grep #{name}|grep -v grep|grep -v bash"
+ pid = t.split(" ")[1]
+ rescue
+ r = 0
+ if r <= 2
+ r = r+1
+ LOGGER.warn "NSM", " NAM has failed to catch the Tcpdump PID, retry n. #{r}/3"
+ sleep 2
+ retry
+ end
+ LOGGER.warn "NSM", " NAM has failed to catch the Tcpdump PID, retry n. #{r}/3"
+ raise
+ end
return pid.to_i
end
end
+ def init_sniffer
+ Net::SSH.start(@server, @user, :password => @pass, :port =>@port) do |ssh|
+ ssh.exec "sudo killall tcpdump"
+ end
+ end
+
def stop_sniffer(pid)
Net::SSH.start(@server, @user, :password => @pass, :port =>@port) do |ssh|
ssh.exec "sudo kill -2 #{pid}"
View
5 lib/dorothy2/VSM.rb
@@ -6,7 +6,6 @@ module Dorothy
#Dorothy module-class for managig the virtual sandboxes
class Doro_VSM
-
#ESX vSphere5 interface
class ESX
@@ -73,7 +72,7 @@ def copy_file(filename,file)
def exec_file(filename, program)
program["prog_args"].nil? ? args = "" : args = program["prog_args"]
- args << " #{filename}"
+ args += " #{filename}"
cmd = { :programPath => program["prog_path"], :arguments => args }
pid = @pm.StartProgramInGuest(:vm => @vm , :auth => @auth, :spec => cmd )
pid.to_i
@@ -100,7 +99,7 @@ def get_running_procs(pid=nil, save_tofile=false, filename="#{DoroSettings.env[:
@pp2 = Hash.new
procs = @pm.ListProcessesInGuest(:vm => @vm , :auth => @auth, :pids => pid )
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]]}
- if save_tofile
+ if save_tofile
Util.write(filename, @pp2.to_yaml)
LOGGER.info "VSM", "Current running processes saved to #{filename}"
end
View
13 lib/dorothy2/do-utils.rb
@@ -5,7 +5,6 @@
module Dorothy
module Util
-
extend self
def write(file, string)
@@ -89,6 +88,8 @@ def insert(table,values)
value1 = value
elsif value =~ /currval/
value1 = value
+ elsif table == "sys_procs" #avoiding noising escape-issue for \u
+ value1 = "E'#{value.inspect}'"
else
#if present, remove ""
value.gsub! /^"|"$/, '' if values.class.inspect == "String"
@@ -230,6 +231,7 @@ class Loadmalw
attr_reader :md5
attr_reader :binpath
attr_reader :filename
+ attr_reader :full_filename #here i'm sure that the file has an extension and can be executed by windows
attr_reader :ctime
attr_reader :size
attr_reader :pcapsize
@@ -266,20 +268,25 @@ def initialize(file)
@ctime= timetmp.strftime("%m/%d/%y %H:%M:%S")
@type = fm.file(file)
+
if @extension.nil? #no extension, trying to put the right one..
case @type
when /^PE32/ then
@extension = (@type =~ /DLL/ ? "dll" : "exe")
+ when /^COM/ then
+ @extension = "exe"
when /^MS-DOS/ then
@extension = "bat"
when /^HTML/ then
@extension = "html"
else
- @extension = nil
+ @extension = "unknown"
end
+ @full_filename = @filename + "." + @extension
+ else
+ @full_filename = @filename
end
-
@size = File.size(file)
end

0 comments on commit 42182e3

Please sign in to comment.
Something went wrong with that request. Please try again.