In the Internet I've found the xkey.c source code, which select inputs from all the windows tree and in this way it is able to listen to key pressed events happening in the X server (indeed it can listen to every events of the windows of the display). This is seriously a danger. You don't need to be root to listen to window events.
Just execute xkey and you can store key hits of every kind, password included!
To test this exploit I have modified the xkey.c code, so that it updates the windows it is listening to (otherwise windows opened after the execution of xkey are not listened). Then I tried to modify system date from the KDE clock applet; a request root password dialog pops up. Inserting the password, I can see it echoed on the shell where xkey is running! Wow!
I tried also opening a new shell in Konsole. It can hear. But if you change the virtual terminal (e.g. CTRL+ALT+F1), you are safe, I suppose because you are not attacched to the X display where xkey is listening. I tried also to listen to other virtual X terminal (as :1), but this, luckly, failed. So you can listen only to your terminal.
We can conclude that installing third parties software (precompiled or not) can hide this great threat. Less than 150 lines of code put inside a long source can be enough to spy to our keystrokes! They get compiled even if are you the one that launchs the ./configure make sequence —having source don't save from this exploit!
Things can be better if we are able to check the source. But still we need a little bit of knowledge about the language we are dealing with if we want be sure of our checks and then remove the malicious piece of code.
In order to listen to keystrokes from all windows of our display of the X server (say, :0), we need to query the X tree that stores the opened windows starting from the first one, the root. We must then iterate (since it is a tree, we must sure we can reach all the leaves walking all the braches).
The dangerous code would do something like this (check the xkey.c source for details) [pseudocode]
function ListenTo(rootwindow) is
XQueryTree(display, rootwindow, rootwindow, returnparent,
returnchildren, num_of_children)
if num_of_children is not 0 then
XSelectInput(display, rootwindow, KeyPressMask)
for each element in the returnchildren array do
XSelectInput(display, element, KeyPressMask)
ListenTo(element)
end for
XFree(returnchildren)
end if
end function ListenTo.
The XQueryTree is per display (so if we go to a safe display, we can be happier); it explores the tree starting from a specified window (the first one will be the root window); it returns the root of the starting window (as rootwindow itself), the parent (returnparent), an array of children (returnchildren) and the number of elements in this array (num_of_children).
As Dominic Giampaolo (xkey's author) states, instead of simply selecting the input, we could act on the window in different ways, e.g. destroying it or whatelse! [I didn't test it but I hope there exist security settings that don't let you kill root window, forcing de facto a log out]
The whole point of this exercise being that I shouldn't be allowed to manipulate resources which do not belong to me. We cannot disagree with this statement of D. Giampaolo, but it is not correct since all windows opened on the display I see are mine. What he wanted to say is really true when belong to me refers to the process/thread asking to manipulate the resources.
When you see such a recursive function in a code, you must become sospiciuos. When a code does a XQueryTree starting from the root window, you must check it carefully! The use of safe toolkit instead of directly program the Xlib, is a good point, but we then should be sure that the toolkit does not provide access to the same functionality given by XQueryTree with a different name!
The following piece of shell commands can help swiftly checking of a source code tree. We are looking for XQueryTree calls. When a program does not call it, we can be more relaxed...
$ cd /path/where/the/sources/are
$ find . -iregex ".*\.[ch]$" -exec egrep -H XQueryTree {} \;
This checks files ending in .c or .h, change the suffixes according to your need; e.g. .*\.\([ch]\|cpp\) should match C++ source too. We check the .h because of possible sospicious #define
When you have one or more matches, you should check if it is really dangerous. Check if the second argument of the XQueryTree is the root window. This alas means understanding a little bit the code. Check if there's an assignment like variable=DefaultRootWindow(display), or if it is used directly, i.e. XQueryTree(display, DefaultRootWindow(display) ...).
Consider positive matches as warnings, but there are good program that needs those functions...
The next check is: what the program does with the returned children? If it use XSelectInput on them, then we are in front of a probable spy! Find a trusted channel where to ask if you are not able to check how harmful is the code. Complex parsers could check this for you, but... do they exist?
Once the program has selected the input of every queried window in the tree, it must wait for events from the X server. This is done via the XNextEvent, XPeekEvent XCheckMaskEvent and maybe similar function. Since the evil program selected the input from every window, it receives all the events. Check for these functions! If they are present altogether with a XSelectInput done on every children got by exploring the full tree starting from root, then it's likely your program is evil.
Again find a trusted channel where to ask for a check.
By the way, it must be noted that we are interested in spying... but if the program would be a sort of killer of windows, running it will simply kill windows... (Imagine such a code to kill windows that have a specific name...). Since we are interested in spying, 1) we do not check for other sospicious behaviour and 2) we should check how the gathered information are sent out the program: are they put inside a file readable by anyone? Are they sent through a socket? Etc.
Here, unless we are able to disassemble and understand the code, the only thing we can do is to check for the symbols XQueryTree and XSelectInput, even though we can't say how they are used. They could be used finely rather than evilshly.
$ objdump -Tt executable |egrep "XQueryTree|XSelectInput"
When these appear and the piece of software you are looking at is not logically interested in looking at the tree, then you should start to be sospicious...
This check over kdm gives as only result the XQueryTree function. Can this make us happier? Not too much... instead of selecting input, we could pick up event from windows (collected by looking at the tree) through a loop. It is not the case of kdm, which I searched with
$ objdump -Tt `which kdm` |egrep "XQueryTree|XSelectInput|X[A-Za-z]*Event"
Interesting, isn't it? Window managers do not need to call XSelectInput, nor to explore events with any X*Event function... it seems so.
Here follows a list of binaries that call XQueryTree... Of course I did not
check all the X
binaries of the world...
| Program | XQueryTree | XSelectInput | Comments |
|---|---|---|---|
| skype | yes | yes | Skype connects to the Internet |
| xine | yes | yes | multiwindow X interface |
| bumpmaptest | no | yes | Imlib2 tool |
| color_spaces | no | yes | no comment |
| gforce | no | yes | graphics effect |
| gifview | no | yes | no comment |
| imlib2 | no | yes | no comment |
| imlib2_test | no | yes | no comment |
| imlib2_view | no | yes | no comment |
| irxevent | yes | no | infrared X-event sender |
| playdv | no | yes | no comment |
| polytest | no | yes | no comment |
| povray | no | yes | no comment |
| timidity | no | yes | no comment |
| wmf2x | no | yes | no comment |
| xboard | yes | no | no comment |
| xscreensaver | yes | yes | to log user idle time |
| xscreensaver-command | yes | yes | no comment |
| xscreensaver-demo | yes | yes | no comment |
| xscreensaver-getimage | yes | yes | no comment |
| BackGround | yes | no | no comment |
| fvwm | yes | yes | window manager |
| kinput2 | yes | yes | input method |
| smproxy | yes | yes | no comment |
| twm | yes | yes | session manager proxy |
| unclutter | yes | yes | to log idle cursor |
| vncevent | no | yes | no comment |
| vncviewer | yes | yes | X viewer client for VNC |
| xbanner | no | yes | no comment |
| xdm | yes | no | no comment |
| Xdmx | no | yes | no comment |
| xev | no | yes | no comment |
| xf4vncviewer | yes | yes | no comment |
| xkill | yes | no | must search the tree to identify the window to kill |
| xlsclients | yes | no | no comment |
| xlsfonts | yes | no | no comment |
| xprop | yes | yes | property displayer |
| xrandr | no | yes | no comment |
| xterm | yes | yes | no comment |
| xwd | yes | no | no comment |
| xwininfo | yes | no | no comment |
| xwinswitch | yes | no | no comment |
| gimp-remote-2.2 | yes | no | no comment |
| xchat | yes | no | no comment |
| zenity | yes | yes | no comment |
| dcop | no | yes | no comment |
| dcopquit | no | yes | no comment |
| kdesktop_lock | yes | yes | no comment |
| kdm | yes | no | no comment |
| kdm_greet | yes | no | no comment |
| kfax | no | yes | no comment |
| kompmgr | yes | yes | compositing manager |
| krandom.kss | yes | no | no comment |
| krandrtray | yes | yes | no comment |
| ksnapshot | yes | no | no comment |
| ksplashsimple | no | yes | no comment |
| ksplashx | no | yes | no comment |
| ksystraycmd | yes | no | no comment |
| kxsconfig | no | yes | no comment |
| nspluginviewer | yes | no | no comment |
1803 executables checked!
Added 2008-05-07: 907 + 2288 shared objects (dynamic linking libraries) checking follows:
| Library | XQueryTree | XSelectInput | Comments |
|---|---|---|---|
| libawt.so | yes | yes | no comment |
| libqt-mt.so.3.3.4 | yes | yes | no comment |
| libgforce.so | no | yes | no comment |
| libtitlefader.so | no | yes | no comment |
| libtk8.4.so | yes | yes | no comment |
| libaa.so.1.0.4 | no | yes | no comment |
| X11.so | no | yes | no comment |
| libpy2qt.so.2.0.0 | yes | yes | no comment |
| vidsite.so | yes | yes | no comment |
| libSDL-1.2.so.0.7.1 | no | yes | no comment |
| libgdraw.so.1.0.8 | yes | yes | no comment |
| libflashplayer.so | yes | no | no comment |
| interface_i.so | no | yes | no comment |
| libMagick.so.6.2.3 | yes | yes | no comment |
| xineplug_vo_out_xshm.so | yes | yes | no comment |
| xineplug_vo_out_xxmc.so | yes | yes | no comment |
| xineplug_vo_out_vidix.so | yes | yes | no comment |
| xineplug_vo_out_xv.so | yes | yes | no comment |
| xineplug_vo_out_opengl.so | yes | yes | no comment |
| libnullplugin.so | no | yes | no comment |
| ximcp.so.2 | yes | yes | no comment |
| libXmu.so.6.2 | yes | no | no comment |
| libXmuu.so.1.0 | yes | no | no comment |
| libXt.so.6.0 | no | yes | no comment |
| nppdf.so | yes | yes | no comment |
| libXm.so.3.0.2 | yes | yes | no comment |
| libX11.so.6.2 | yes | yes | no comment |
| libI810XvMC.so.1.0 | yes | no | no comment |
| libxrx.so.6.8 | no | yes | no comment |
| Library | XQueryTree | XSelectInput | Comments |
|---|---|---|---|
| libxmmskde.so | yes | no | no comment |
| kded_klaptopdaemon.so | yes | yes | no comment |
| kcm_khotkeys.so | yes | no | no comment |
| kwin3_b2.so | no | yes | no comment |
| dockbar_panelextension.so | no | yes | no comment |
| kcm_screensaver.so | no | yes | no comment |
| libkdeinit_kpowersave.so | yes | no | no comment |
| libkdeinit_kdesktop.so | yes | yes | no comment |
| libkdeinit_kwin.so | yes | yes | no comment |
| libkdeinit_ksmserver.so | no | yes | no comment |
| libksplashthemes.so.0.0.0 | no | yes | no comment |
| libkdecore.so.4.2.0 | yes | yes | no comment |
| libkdefx.so.4.2.0 | no | yes | no comment |
| libkdeui.so.4.2.0 | yes | yes | no comment |
| libxvim.so.0.0.0 | no | yes | no comment |
| libkscreensaver.so.4.2.0 | yes | no | no comment |
| libgdk-1.2.so.0.9.1 | yes | yes | no comment |
| libgtk-1.2.so.0.9.1 | no | yes | no comment |
| libgdk-x11-2.0.so.0.800.3 | yes | yes | no comment |
| libgail.so | no | yes | no comment |
| libgtk-x11-2.0.so.0.800.3 | no | yes | no comment |
| libgdk_pixbuf_xlib-2.0.so.0.800.3 | yes | no | no comment |
A command line (or script) you can use to do it yourself is:
for el in /exec/paths/* ;
do
if [[ -h "$el" ]]; then
continue;
fi
if [[ $(file "$el" |egrep -c ELF) -eq 0 ]];
then continue;
fi
echo "+ $el"
objdump -Tt "$el" |egrep "XQueryTree|XSelectInput"
done >OUTPUT
(I used a script to generate the HTML table with all the information in it, and then edited to add some comments)
We notice that multiwindow interfaces that access directly the Xlib use both XQueryTree and XSelectInput. We can check the code for xine (I compiled it by myself:) but what about Skype? It can connect to the Internet and send out our keystrokes! Anyway it should be easy to check outbound connections and be sure that no clear data are sent... clear not, but crypted some way?!
I guess that any complex widget can use XQueryTree and XSelectInput. Here the point is: XQueryTree is called recursively starting from the root? We cannot know it with this simple analysis.
Here we use imagination since I still won't recompile Xorg... (My first try was unsuccessful because of lacking of something, can't remember, was eons ago)
The basic idea is to patch XQueryTree so that it returns only windows whose owner is the process/thread calling it. This breaks a lot of useful programs. To avoid this we can create a group that is allowed to explore the full tree. Safe and known softwares that need to access the full tree will have the group set to the proper one. Administrator will be responsible to set it accordingly the needs without compromising the security of each user.
I suppose this road can be taken.
Here just remember that we can refuse undesired connections; e.g. on my system (as default) I cannot connect to another one's session to spy his/her keystrokes. Generally this is a good behaviour, unless we need to accept outer connection to the display. When this happens we must be sure that the computer allowed to connect to our display is trustworthy, clean and secure. But we already know that we should always be sure about who can connect to us!
Even though not all our keystrokes will be safe, we can grab the keyboard for ourselves when we must input things like password. This should prevent others to listen to that events. I will test this one day.