Skip to content

wtmp, btmp et lastlog en forensique : wtmpdb et lastlog2

Les enregistrements de connexion Linux : struct utmp sur x86_64 et aarch64, échecs dans btmp, emplacements lastlog, SQLite wtmpdb et lastlog2, et altération.

Publié le 5 min de lecture

En bref. wtmp est l’historique des sessions interactives et des redémarrages, btmp celui des échecs de connexion, utmp la liste des sessions ouvertes à l’instant présent, et lastlog la dernière connexion de chaque UID. Ce sont des enregistrements binaires de taille fixe écrits en dehors de syslog : ils survivent quand les journaux texte sont modifiés, et les contredisent quand ils ne le sont pas. Lisez-les avec un parseur qui connaît la taille d’enregistrement de l’architecture source, et sur les distributions récentes cherchez leurs remplaçants SQLite, wtmp.db et lastlog2.db.

Les fichiers

FichierCheminLecteurContenu
wtmp/var/log/wtmp, wtmp.1lastConnexions, déconnexions, démarrages, arrêts
btmp/var/log/btmp, btmp.1lastbÉchecs de connexion (root uniquement, sensible)
utmp/run/utmpwho, wSessions en cours, vidé au démarrage
lastlog/var/log/lastloglastlogUn emplacement par UID
wtmpdb/var/lib/wtmpdb/wtmp.db, /var/log/wtmp.db (Debian 13)wtmpdb lastRemplaçant SQLite de wtmp
lastlog2/var/lib/lastlog/lastlog2.dblastlog2Remplaçant SQLite de lastlog

logrotate tourne généralement wtmp et btmp chaque mois et conserve une ancienne génération : attendez-vous à un ou deux mois d’historique. lastlog conserve un enregistrement par UID indéfiniment.

struct utmp : 384 octets, ou 400

wtmp, btmp et utmp partagent struct utmp :

ChampContenu
ut_type1 RUN_LVL, 2 BOOT_TIME, 5 INIT_PROCESS, 6 LOGIN_PROCESS, 7 USER_PROCESS (connexion), 8 DEAD_PROCESS (déconnexion)
ut_pidPID du processus de connexion (sshd pour SSH)
ut_lineTerminal, pts/1, tty1, 32 octets
ut_userNom d’utilisateur, 32 octets ; vide dans les enregistrements de déconnexion
ut_hostHôte ou IP distant ; version du noyau dans les enregistrements de redémarrage ; 256 octets
ut_tvSecondes et microsecondes, UTC
ut_addr_v6Adresse distante

Avec la glibc x86_64, l’enregistrement fait 384 octets : les champs d’heure sont sur 32 bits pour la compatibilité avec les programmes 32 bits. Sur aarch64, ils sont sur 64 bits et l’enregistrement fait 400 octets. last lit les enregistrements avec la structure de la machine sur laquelle il s’exécute : un wtmp aarch64 lu avec last sur un poste x86_64 donne des données incohérentes, ou rien. Exécutez last sur une architecture correspondante, ou utilisez un parseur qui détecte la structure d’après la taille du fichier et la plausibilité des champs.

Une déconnexion est un enregistrement DEAD_PROCESS sur le même ut_line avec un utilisateur vide ; last les apparie. Une connexion sans déconnexion correspondante apparaît comme gone - no logout, ou crash lorsqu’un enregistrement de démarrage suit.

lastlog : un emplacement par UID

/var/log/lastlog est un tableau de struct lastlog indexé par UID : 292 octets par emplacement sur x86_64 (heure sur 32 bits, ligne de 32 octets, hôte de 256 octets), 296 sur aarch64. L’offset de l’UID 1001 est 1001 × 292. Le fichier est creux (sparse) : une connexion de l’UID 100000 le fait paraître énorme alors que seuls quelques blocs sont utilisés. Collectez-le avec tar --sparse.

La valeur lastlog est écrasée à chaque connexion ; elle ne donne donc que la plus récente, mais elle est écrite indépendamment de wtmp. Une heure lastlog pour laquelle wtmp n’a aucune session est un signal fort que wtmp a perdu un enregistrement.

wtmpdb et lastlog2

Les champs d’heure 32 bits débordent en 2038 ; les distributions récentes passent donc à SQLite :

  • wtmpdb : table wtmp(ID, Type, User, Login, Logout, TTY, RemoteHost, Service). Login et Logout sont exprimés en microsecondes depuis l’epoch ; Type vaut 1 BOOT_TIME, 2 RUNLEVEL, 3 USER_PROCESS. Une ligne par session, dont la déconnexion est renseignée à la fin de la session, avec le nom du Service PAM (sshd, login) que le format classique n’a jamais eu.
  • lastlog2 : table Lastlog2(Name, Time, TTY, RemoteHost, Service), avec Time en secondes et une ligne par nom d’utilisateur.

openSUSE Tumbleweed les utilise par défaut. Debian 13 a retiré last, lastb et lastlog et place le fichier wtmpdb dans /var/log/wtmp.db ; les paquets doivent être installés séparément, si bien qu’un hôte mis à niveau vers trixie peut n’avoir aucune base de connexions. Collectez le fichier -wal à côté d’une base s’il existe : des lignes récentes peuvent encore s’y trouver.

sqlite3 wtmp.db "SELECT User, TTY, RemoteHost, Service, datetime(Login/1000000,'unixepoch'), datetime(Logout/1000000,'unixepoch') FROM wtmp;"
sqlite3 lastlog2.db "SELECT Name, datetime(Time,'unixepoch'), TTY, RemoteHost, Service FROM Lastlog2;"

btmp : les échecs de connexion

btmp utilise la même structure d’enregistrement. Chaque tentative échouée est un enregistrement contenant le nom d’utilisateur essayé, l’hôte source et l’heure. C’est une troisième copie, indépendante, des essais de mots de passe que l’on trouve aussi dans auth.log et le journal, utile lorsque ceux-ci ont été nettoyés. Traitez-le comme sensible : il arrive que des utilisateurs tapent leur mot de passe dans le champ du nom d’utilisateur.

À quoi ressemble une altération

  • Enregistrements effacés. Les outils de nettoyage écrasent un enregistrement de connexion avec des zéros plutôt que de le supprimer, si bien que la taille du fichier reste un multiple de la taille d’enregistrement. last l’ignore simplement. Dans la sortie d’utmpdump, il apparaît comme un enregistrement de type 0, sans utilisateur et daté de 1970.
  • Enregistrement partiel en fin de fichier. Une taille de fichier qui n’est pas un multiple de 384 (ou 400) signale une écriture tronquée, ou un fichier copié pendant son écriture.
  • Heure qui recule entre des enregistrements consécutifs.
  • wtmp ou btmp de zéro octet avec une date de modification ancienne sur un serveur en service.
  • Une session SSH interactive présente dans auth.log, le journal et audit.log, mais pas dans wtmp, alors que wtmp couvre cette période.

N’oubliez pas le cas bénin : ssh host command, scp et sftp n’ouvrent pas de terminal et ne laissent souvent aucun enregistrement wtmp. Comparez uniquement les sessions interactives (un terminal pts dans les journaux).

Commandes

TZ=UTC last -F -i -x -f wtmp        # dates complètes, IP, redémarrages
TZ=UTC lastb -F -i -f btmp
utmpdump wtmp > wtmp.txt            # tous les enregistrements, y compris les effacés
wtmpdb last -F -f wtmp.db
lastlog2 -d lastlog2.db

last affiche les heures dans le fuseau de la machine d’analyse, sauf si TZ=UTC est défini.

Dans le navigateur

Linux Log Parser lit wtmp, btmp et utmp dans les structures de 384 et de 400 octets, lastlog, wtmp.db et lastlog2.db ; il signale les enregistrements effacés, partiels et à rebours, apparie connexions et déconnexions, les fusionne avec les sessions sshd, logind et audit, et repère les connexions interactives sans enregistrement wtmp. Voir le guide sur l’altération et la fiche wtmp.

Articles liés

Prouver que des journaux Linux ont été modifiés ou supprimés : trous de seqnum, lignes absentes d’auth.log, wtmp effacé, démons arrêtés, commandes d’effacement.
Comment les quatre familles de journaux Linux s’articulent dans une investigation, ce que chacune prouve, où elles divergent et comment les fusionner.
Reconstituer les commandes depuis audit.log : regroupement par événement, arguments EXECVE réassemblés, décodage hexadécimal, auid et ses à travers sudo.