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.
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
| Fichier | Chemin | Lecteur | Contenu |
|---|---|---|---|
| wtmp | /var/log/wtmp, wtmp.1 | last | Connexions, déconnexions, démarrages, arrêts |
| btmp | /var/log/btmp, btmp.1 | lastb | Échecs de connexion (root uniquement, sensible) |
| utmp | /run/utmp | who, w | Sessions en cours, vidé au démarrage |
| lastlog | /var/log/lastlog | lastlog | Un emplacement par UID |
| wtmpdb | /var/lib/wtmpdb/wtmp.db, /var/log/wtmp.db (Debian 13) | wtmpdb last | Remplaçant SQLite de wtmp |
| lastlog2 | /var/lib/lastlog/lastlog2.db | lastlog2 | Remplaç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 :
| Champ | Contenu |
|---|---|
ut_type | 1 RUN_LVL, 2 BOOT_TIME, 5 INIT_PROCESS, 6 LOGIN_PROCESS, 7 USER_PROCESS (connexion), 8 DEAD_PROCESS (déconnexion) |
ut_pid | PID du processus de connexion (sshd pour SSH) |
ut_line | Terminal, pts/1, tty1, 32 octets |
ut_user | Nom d’utilisateur, 32 octets ; vide dans les enregistrements de déconnexion |
ut_host | Hôte ou IP distant ; version du noyau dans les enregistrements de redémarrage ; 256 octets |
ut_tv | Secondes et microsecondes, UTC |
ut_addr_v6 | Adresse 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).LoginetLogoutsont exprimés en microsecondes depuis l’epoch ;Typevaut 1BOOT_TIME, 2RUNLEVEL, 3USER_PROCESS. Une ligne par session, dont la déconnexion est renseignée à la fin de la session, avec le nom duServicePAM (sshd,login) que le format classique n’a jamais eu. - lastlog2 : table
Lastlog2(Name, Time, TTY, RemoteHost, Service), avecTimeen 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.
lastl’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.