Skip to content

USB & devicesPersistenceLogs

udev and USB Device History on Linux

Reconstruct USB device connections on Linux from kernel, udisks and USBGuard logs, and check udev rules used for RUN+= persistence.

Location
/etc/udev/rules.d/, /var/log/kern.log
Proves
Which USB devices (vendor, product, serial) were attached and when, where storage was mounted, and whether udev rules run code
Timestamps
Journal: UTC microseconds; syslog: host local time; dmesg: seconds since boot
Access
root (or adm/systemd-journal group) for logs; rules readable by any user
Retention
Follows journal and kern.log/messages rotation; rules until deleted
Collection
UAC, Velociraptor, journalctl, cp -a
  • journalctlCLI · built into Linux
  • grep / zgrepCLI · built into Linux
  • udevadmCLI · built into Linux
  • PlasoCLI · open source

What it is

Linux has no central USB registry like Windows. Device history comes from log messages written when a device is enumerated: the kernel USB core announces vendor and product IDs and descriptor strings, storage drivers name the block device, and desktop services such as udisks log the mount. systemd-udevd then applies rules from several directories to name devices, set permissions and optionally run programs.

Those same rule files are a persistence location. A rule with RUN+= executes a program as root whenever a matching device event occurs, and a rule matching a device that always exists at boot runs at every start (the sedexp malware used a rule on /dev/random).

Where it lives

ItemLocationNotes
Kernel USB messagesjournal (_TRANSPORT=kernel); /var/log/kern.log and syslog (Debian/Ubuntu with rsyslog); /var/log/messages (RHEL/Fedora with rsyslog)Also in dmesg on a live host
Local admin rules/etc/udev/rules.d/*.rulesHighest priority; same name overrides vendor rules
Runtime rules/run/udev/rules.d/Volatile
Vendor rules/usr/lib/udev/rules.d/, /usr/local/lib/udev/rules.d/, /lib/udev/rules.d/ (non-merged-usr Debian/Ubuntu)Owned by packages
udev database/run/udev/data/Live only, current devices
Live device tree/sys/bus/usb/devices/*/ (idVendor, idProduct, serial, manufacturer, product)Live only
udisks mount points/media/<user>/<label> (Debian/Ubuntu), /run/media/<user>/<label> (Fedora/RHEL)Usually removed after unmount
USBGuard (if installed)/etc/usbguard/rules.conf, /var/log/usbguard/usbguard-audit.logAllow/block decisions per device

What it proves

  • A USB device was connected: time, bus port, vendor and product ID, and (when the device reports one) manufacturer, product and serial number strings.
  • Mass storage: the device was bound to usb-storage or uas and became sdX, with capacity and removable flag.
  • Mounting and by whom: udisks logs the device, mount point and UID for desktop auto-mounts.
  • Disconnect time (USB disconnect, device number N).
  • Persistence or tampering via custom udev rules.
  • It does not prove which files were copied. Combine with recent files, shell history and file timestamps on both sides. Device strings and serials are chosen by the device and can be spoofed.

Key fields

Typical kernel sequence for a flash drive:

usb 1-2: new high-speed USB device number 7 using xhci_hcd
usb 1-2: New USB device found, idVendor=0781, idProduct=5583, bcdDevice= 1.00
usb 1-2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 1-2: Product: Ultra Fit
usb 1-2: Manufacturer: SanDisk
usb 1-2: SerialNumber: 4C530001230704116195
usb-storage 1-2:1.0: USB Mass Storage device detected
sd 6:0:0:0: [sdb] Attached SCSI removable disk
udisksd[1432]: Mounted /dev/sdb1 at /media/alice/ULTRAFIT on behalf of uid 1000
usb 1-2: USB disconnect, device number 7
ElementMeaning
usb 1-2Bus 1, port 2 (port path, stable per physical port)
device number 7Address on the bus, reused over time
idVendor / idProductUSB IDs; look up in the usb.ids database
Product, Manufacturer, SerialNumberDescriptor strings; printed only when the kernel has CONFIG_USB_ANNOUNCE_NEW_DEVICES and the device supplies them
[sdb]Block device assigned; ties later filesystem and mount messages to the device
on behalf of uidAccount that requested the udisks mount

The descriptor lines, the storage line and the udisks line are separate messages, so correlate them by port path, device name and time.

A udev rule is a list of match keys (ACTION, SUBSYSTEM, KERNEL, ATTR{...}, ATTRS{...}, ENV{...}) followed by assignments. Watch for RUN+= and PROGRAM=. The sedexp rule looked like ACTION=="add", ENV{MAJOR}=="1", ENV{MINOR}=="8", RUN+="..." (the /dev/random device).

Timestamps

  • Journal entries carry __REALTIME_TIMESTAMP in microseconds since the Unix epoch (UTC); journalctl displays local time unless you pass --utc.
  • Text logs use the syslog daemon's format: local time, with or without offset and year (see auth.log and syslog).
  • dmesg output is seconds since boot. Converting with dmesg -T uses the current boot time and drifts after suspend, so prefer the journal.
  • Rule files: inode mtime and ctime show when a rule was written.

Retention

Nothing is retained beyond the logs. Kernel messages survive only as long as the journal (size-based limits) and rotated kern.log/messages/syslog generations. With a volatile journal (/run/log/journal) and no rsyslog, USB history is lost at reboot. Rules persist until deleted; the /run copies vanish at shutdown.

Collection

E=/mnt/evidence
journalctl -D "$E/var/log/journal" -k --utc -o short-iso-precise > kernel.txt
journalctl -D "$E/var/log/journal" --utc -o json _COMM=udisksd > udisks.json
tar -C "$E" -cpf /cases/2026-017/udev.tar etc/udev usr/lib/udev/rules.d lib/udev/rules.d \
  etc/usbguard var/log/usbguard var/log/kern.log* var/log/messages* var/log/syslog* 2>/dev/null

UAC collects the three rule directories, /var/log and runs lsusb and lsusb -vv in live response. On a live host, udevadm info --export-db dumps the current udev database.

Parsing

# Connection history from the journal
journalctl -D journal/ -k --utc -o short-iso-precise \
  | grep -E 'New USB device found|Product:|Manufacturer:|SerialNumber:|USB disconnect|Attached SCSI'
# Same from text logs, including rotated copies
zgrep -hE 'usb [0-9-]+(\.[0-9]+)*: (New USB device|Product|Manufacturer|SerialNumber)' kern.log* messages*
# Rules that run programs, non-package locations first
grep -rnE 'RUN\+?=|PROGRAM=' etc/udev/rules.d run/udev/rules.d 2>/dev/null

Check whether a rule in /usr/lib/udev/rules.d belongs to a package (dpkg -S or rpm -qf against the image, see package manager logs). Plaso picks up kernel lines through its syslog and journal parsers.

Investigator tips

  • The same serial number across several hosts links one physical device to multiple systems; an absent serial is common on cheap devices and some card readers.
  • RUN+= is meant for short foreground tasks: the udevd sandbox blocks network access and mounts, and any forked or detached child is killed when event handling ends. A rule that needs a long-running payload therefore has to hand off outside udev, for example to a systemd unit pulled in with ENV{SYSTEMD_WANTS}. Follow the chain.
  • A rule named like a vendor rule (99-...rules, 70-persistent-...) in /etc/udev/rules.d with no package owner deserves reading line by line.
  • Keyboard-class devices (HID) appearing unexpectedly on a server can indicate a malicious USB device; look for input: lines next to the USB enumeration.
  • USBGuard block events prove an attempt even when the device never became usable.
  • New modules loaded right after a device appears are worth checking against kernel modules.

See also