wtmp, btmp y lastlog: análisis forense, wtmpdb y lastlog2
Registros de inicio de sesión de Linux: struct utmp en x86_64 y aarch64, fallos en btmp, ranuras de lastlog, SQLite de wtmpdb y lastlog2, y manipulación.
En resumen. wtmp es el historial de sesiones interactivas y reinicios, btmp los inicios de sesión fallidos, utmp las sesiones abiertas en este momento y lastlog el último inicio de sesión de cada UID. Son registros binarios de tamaño fijo escritos al margen de syslog, así que sobreviven cuando se editan los logs de texto y los contradicen cuando no se editan. Léelos con un analizador que conozca el tamaño de registro de la arquitectura de origen y, en las distribuciones más recientes, busca sus sustitutos en SQLite, wtmp.db y lastlog2.db.
Los archivos
| Archivo | Ruta | Lector | Contenido |
|---|---|---|---|
| wtmp | /var/log/wtmp, wtmp.1 | last | Inicios y cierres de sesión, arranques, apagados |
| btmp | /var/log/btmp, btmp.1 | lastb | Inicios de sesión fallidos (solo root, sensible) |
| utmp | /run/utmp | who, w | Sesiones actuales, se vacía al arrancar |
| lastlog | /var/log/lastlog | lastlog | Una ranura por UID |
| wtmpdb | /var/lib/wtmpdb/wtmp.db, /var/log/wtmp.db (Debian 13) | wtmpdb last | Sustituto de wtmp en SQLite |
| lastlog2 | /var/lib/lastlog/lastlog2.db | lastlog2 | Sustituto de lastlog en SQLite |
logrotate suele rotar wtmp y btmp mensualmente y conservar una generación antigua, así que cuenta con uno o dos meses. lastlog conserva un registro por UID indefinidamente.
struct utmp: 384 bytes, o 400
wtmp, btmp y utmp comparten struct utmp:
| Campo | Contenido |
|---|---|
ut_type | 1 RUN_LVL, 2 BOOT_TIME, 5 INIT_PROCESS, 6 LOGIN_PROCESS, 7 USER_PROCESS (inicio de sesión), 8 DEAD_PROCESS (cierre de sesión) |
ut_pid | PID del proceso de inicio de sesión (sshd para SSH) |
ut_line | Terminal, pts/1, tty1, 32 bytes |
ut_user | Nombre de usuario, 32 bytes; vacío en los registros de cierre de sesión |
ut_host | Host remoto o IP; versión del kernel en los registros de reinicio; 256 bytes |
ut_tv | Segundos y microsegundos, UTC |
ut_addr_v6 | Dirección remota |
Con la glibc de x86_64, el registro ocupa 384 bytes: los campos de hora son de 32 bits por compatibilidad con los programas de 32 bits. En aarch64 son de 64 bits y el registro ocupa 400 bytes. last lee los registros con la estructura de la máquina en la que se ejecuta, así que un wtmp de aarch64 leído con last en una estación x86_64 produce basura o nada. Ejecuta last en una arquitectura equivalente o usa un analizador que detecte la estructura a partir del tamaño del archivo y de la verosimilitud de los campos.
Un cierre de sesión es un registro DEAD_PROCESS en la misma ut_line con el usuario vacío; last los empareja. Un inicio de sesión sin cierre correspondiente aparece como gone - no logout, o como crash si le sigue un registro de arranque.
lastlog: una ranura por UID
/var/log/lastlog es un array de struct lastlog indexado por UID: 292 bytes por ranura en x86_64 (hora de 32 bits, línea de 32 bytes, host de 256 bytes) y 296 en aarch64. El desplazamiento del UID 1001 es 1001 × 292. El archivo es disperso (sparse): un inicio de sesión del UID 100000 hace que parezca enorme aunque solo se usen unos pocos bloques. Recópilalo con tar --sparse.
El valor de lastlog se sobrescribe en cada inicio de sesión, así que solo indica el último, pero se escribe con independencia de wtmp. Una hora de lastlog para la que wtmp no tiene sesión es una señal sólida de que wtmp perdió un registro.
wtmpdb y lastlog2
Los campos de hora de 32 bits se desbordan en 2038, por lo que las distribuciones más recientes pasan a SQLite:
- wtmpdb: tabla
wtmp(ID, Type, User, Login, Logout, TTY, RemoteHost, Service).LoginyLogoutson microsegundos desde la época Unix;Typevale 1BOOT_TIME, 2RUNLEVEL, 3USER_PROCESS. Una fila por sesión, con el cierre rellenado cuando termina la sesión, y el nombre delServicePAM (sshd,login), que el formato clásico nunca tuvo. - lastlog2: tabla
Lastlog2(Name, Time, TTY, RemoteHost, Service), conTimeen segundos y una fila por nombre de usuario.
openSUSE Tumbleweed los usa por defecto. Debian 13 eliminó last, lastb y lastlog y coloca el archivo de wtmpdb en /var/log/wtmp.db; los paquetes deben instalarse aparte, así que un host actualizado a trixie puede no tener ninguna base de datos de inicios de sesión. Recopila el archivo -wal junto a la base de datos si existe: las filas recientes pueden seguir en él.
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: inicios de sesión fallidos
btmp usa la misma estructura de registro. Cada intento fallido es un registro con el nombre de usuario probado, el host de origen y la hora. Es una tercera copia independiente de los intentos de adivinar contraseñas que también aparecen en auth.log y en el journal, útil cuando estos se limpiaron. Trátalo como sensible: los usuarios a veces escriben su contraseña en el campo del nombre de usuario.
Qué aspecto tiene la manipulación
- Registros puestos a cero. Los limpiadores de logs sobrescriben un registro de inicio de sesión con ceros en lugar de borrarlo, de modo que el tamaño del archivo sigue siendo múltiplo del tamaño de registro.
lastsimplemente lo omite. En la salida deutmpdumpaparece como un registro de tipo 0, sin usuario y con fecha de 1970. - Registro parcial al final. Un tamaño de archivo que no es múltiplo de 384 (o 400) indica una escritura truncada o un archivo copiado mientras se escribía.
- Hora que retrocede entre registros consecutivos.
- wtmp o btmp de cero bytes con una fecha de modificación antigua en un servidor en uso.
- Una sesión SSH interactiva en auth.log, el journal y audit.log, pero no en wtmp, aunque wtmp cubra ese periodo.
Recuerda el caso inocente: ssh host command, scp y sftp no abren un terminal y a menudo no dejan registro en wtmp. Compara solo las sesiones interactivas (un terminal pts en los logs).
Comandos
TZ=UTC last -F -i -x -f wtmp # fechas completas, IP, reinicios
TZ=UTC lastb -F -i -f btmp
utmpdump wtmp > wtmp.txt # todos los registros, incluidos los puestos a cero
wtmpdb last -F -f wtmp.db
lastlog2 -d lastlog2.db
last muestra las horas en la zona del equipo de análisis salvo que se defina TZ=UTC.
En el navegador
Linux Log Parser lee wtmp, btmp y utmp tanto en la estructura de 384 bytes como en la de 400, lastlog, wtmp.db y lastlog2.db; informa de los registros puestos a cero, parciales y con hora que retrocede, empareja inicios y cierres de sesión, los combina con las sesiones de sshd, logind y auditoría, y señala los inicios de sesión interactivos sin registro en wtmp. Consulta la guía sobre manipulación y la ficha de wtmp.