Skip to content

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.

Publicado el 6 min de lectura

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

ArchivoRutaLectorContenido
wtmp/var/log/wtmp, wtmp.1lastInicios y cierres de sesión, arranques, apagados
btmp/var/log/btmp, btmp.1lastbInicios de sesión fallidos (solo root, sensible)
utmp/run/utmpwho, wSesiones actuales, se vacía al arrancar
lastlog/var/log/lastloglastlogUna ranura por UID
wtmpdb/var/lib/wtmpdb/wtmp.db, /var/log/wtmp.db (Debian 13)wtmpdb lastSustituto de wtmp en SQLite
lastlog2/var/lib/lastlog/lastlog2.dblastlog2Sustituto 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:

CampoContenido
ut_type1 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_pidPID del proceso de inicio de sesión (sshd para SSH)
ut_lineTerminal, pts/1, tty1, 32 bytes
ut_userNombre de usuario, 32 bytes; vacío en los registros de cierre de sesión
ut_hostHost remoto o IP; versión del kernel en los registros de reinicio; 256 bytes
ut_tvSegundos y microsegundos, UTC
ut_addr_v6Direcció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). Login y Logout son microsegundos desde la época Unix; Type vale 1 BOOT_TIME, 2 RUNLEVEL, 3 USER_PROCESS. Una fila por sesión, con el cierre rellenado cuando termina la sesión, y el nombre del Service PAM (sshd, login), que el formato clásico nunca tuvo.
  • lastlog2: tabla Lastlog2(Name, Time, TTY, RemoteHost, Service), con Time en 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. last simplemente lo omite. En la salida de utmpdump aparece 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.

Artículos relacionados

Cómo probar que se editaron o borraron logs de Linux: huecos de seqnum, líneas ausentes de auth.log, registros wtmp a cero, daemons detenidos y borrados.
Cómo encajan las cuatro familias de logs de Linux en una investigación, qué prueba cada una, dónde discrepan y cómo unirlas en una sola línea de tiempo.
Reconstruir líneas de comando desde audit.log de Linux: agrupar registros por evento, argumentos EXECVE, valores en hexadecimal, y auid y ses a través de sudo.