Investigating a suspected Linux intrusion starts with preserving volatile host evidence before containment or cleanup changes the scene. The first pass separates background probes from a real account login, privilege escalation, persistence, or a live process that still needs isolation.

Host evidence usually spans systemd journals, session databases, account records, SSH keys, schedulers, sockets, processes, kernel messages, and recently changed files. Reading those sources in time order turns separate warnings into a timeline that can be escalated, scoped, and later compared with firewall, endpoint, or SIEM records.

Use read-only commands first and save output with timestamps and time zone context. Login databases, rotated logs, shell history, and journal retention vary across distributions, and current Ubuntu releases may use wtmpdb where older systems used only /var/log/wtmp, so corroborate each high-signal finding against another source before removing users, keys, jobs, or files.

Steps to investigate a Linux intrusion from host evidence:

  1. Record the collection time before reviewing logs.
    $ date --iso-8601=seconds
    2026-04-14T12:06:22+08:00

    Keep the host time zone with every copied excerpt so later correlation does not mix local time, UTC, and SIEM display time.

  2. Check active sessions before users disconnect or source terminals disappear.
    $ w
     12:06:24 up 4 min,  2 users,  load average: 0.12, 0.08, 0.03
    USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU  WHAT
    user     pts/0    203.0.113.44     12:04    1:31   0.03s  0.01s sshd: user
    analyst  pts/1    198.51.100.10    12:05    0.00s  0.02s  w

    w is a volatile view of who is logged in right now. Capture it before restarting network services or killing sessions.
    Related: How to show logged-in users in Linux

  3. Review the login database for recent sessions and reboots.
    $ last -a -i -F
    user     pts/0        Tue Apr 14 12:04:52 2026   still logged in                 203.0.113.44
    reboot   system boot  Tue Apr 14 12:02:18 2026   still running                   0.0.0.0
    user     pts/0        Mon Apr 13 23:17:09 2026 - Mon Apr 13 23:21:02 2026 (00:03) 198.51.100.25
    ##### snipped #####

    If last is missing on a host that uses wtmpdb, run wtmpdb last --time-format iso --hostlast for the same session-database check. Empty or reboot-only output does not prove an account was unused.
    Related: How to check user login history in Linux

  4. Build the first authentication timeline from SSH and sudo journal entries.
    $ sudo journalctl --since "24 hours ago" -t sshd -t sudo --output=short-iso --no-pager
    2026-04-14T12:04:48+08:00 host sshd[2236]: Failed password for invalid user backup from 203.0.113.44 port 59562 ssh2
    2026-04-14T12:04:49+08:00 host sshd[2236]: Connection closed by invalid user backup 203.0.113.44 port 59562 [preauth]
    2026-04-14T12:04:52+08:00 host sshd[2245]: Accepted password for user from 203.0.113.44 port 42956 ssh2
    2026-04-14T12:04:52+08:00 host sshd[2245]: pam_unix(sshd:session): session opened for user user(uid=1000) by user(uid=0)
    2026-04-14T12:05:03+08:00 host sudo[2331]:     user : PWD=/home/user ; USER=root ; COMMAND=/usr/bin/systemctl enable --now ssh
    2026-04-14T12:05:03+08:00 host sudo[2331]: pam_unix(sudo:session): session opened for user root(uid=0) by user(uid=1000)
    ##### snipped #####

    On non-systemd hosts or hosts without retained journal entries, check /var/log/auth.log on Debian-family systems or /var/log/secure on RHEL-family systems.
    Related: How to check authentication logs in Linux

  5. Read the target account shell history without treating it as complete evidence.
    $ sudo cat /home/user/.bash_history
    sudo systemctl enable --now ssh
    mkdir -p ~/.cache/.svc
    python3 ~/.cache/.svc/agent.py
    ssh-keygen -lf ~/.ssh/authorized_keys
    ##### snipped #####

    Shell history can be disabled, truncated, edited, or written only when an interactive shell exits. Check .zsh_history, .ash_history, and other shell-specific files when the account does not use bash.
    Related: How to check commands run by a user in Linux

  6. Confirm the target account identity and privilege groups.
    $ id user
    uid=1000(user) gid=1000(user) groups=1000(user),27(sudo),1001(appdeploy)

    Unexpected new accounts, new membership in sudo or wheel, and new files under /etc/sudoers.d/ are high-signal privilege changes during triage.
    Related: How to audit local user accounts in Linux

  7. Check the authorized_keys timestamp and ownership for the account.
    $ sudo stat /home/user/.ssh/authorized_keys
      File: /home/user/.ssh/authorized_keys
      Size: 91        	Blocks: 8          IO Block: 4096   regular file
    Device: 252,0	Inode: 1179935     Links: 1
    Access: (0600/-rw-------)  Uid: ( 1000/    user)   Gid: ( 1000/    user)
    Access: 2026-04-14 12:04:43.803000071 +0800
    Modify: 2026-04-14 12:04:43.803000071 +0800
    Change: 2026-04-14 12:04:43.803000071 +0800
     Birth: 2026-04-14 12:02:23.729000004 +0800

    Correlate key-file Modify and Change times with sshd, sudo, and shell-history entries before deleting the key.
    Related: How to check SSH authorized keys in Linux

  8. Record fingerprints for any unexpected SSH public keys.
    $ sudo ssh-keygen -lf /home/user/.ssh/authorized_keys
    256 SHA256:GBeV49yqFZTNz76D2Nyb/EnWxv5zDjf0FbVxgWIjpQI incident@example (ED25519)

    Compare the fingerprint with inventory, ticket, or administrator records before deciding whether the key is legitimate.
    Tool: Secure Shell (SSH) Key Fingerprint Checker

  9. Check the account crontab for logout-surviving commands.
    $ sudo crontab -l -u user
    @reboot /usr/bin/python3 /home/user/.cache/.svc/agent.py

    Copy the exact command, owner, and schedule before disabling a suspicious cron entry; deleting it first can remove the only readable persistence clue.
    Related: How to check for persistence via cron and systemd in Linux

  10. Check systemd timers for service-based persistence.
    $ sudo systemctl list-timers --all --no-pager
    NEXT                        LEFT      LAST                        PASSED UNIT                         ACTIVATES
    Tue 2026-04-14 12:10:00 +08 3min left -                           -      app-cache-refresh.timer      app-cache-refresh.service
    Tue 2026-04-14 12:17:13 +08 10min left Tue 2026-04-14 12:02:31 +08 4min   systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
    Tue 2026-04-14 12:49:23 +08 42min left Mon 2026-04-13 12:49:23 +08 23h    apt-daily-upgrade.timer      apt-daily-upgrade.service
    ##### snipped #####

    Investigate unfamiliar timer units with sudo systemctl cat <unit>.timer and sudo systemctl cat <unit>.service before stopping them.
    Related: How to check for persistence via cron and systemd in Linux

  11. List listening sockets and connected endpoints.
    $ sudo ss -tulpn
    Netid State  Recv-Q Send-Q Local Address:Port Peer Address:Port Process
    tcp   LISTEN 0      4096        0.0.0.0:22        0.0.0.0:*     users:(("sshd",pid=2220,fd=3),("systemd",pid=1,fd=165))
    tcp   LISTEN 0      5           0.0.0.0:8888      0.0.0.0:*     users:(("python3",pid=2229,fd=3))
    udp   UNCONN 0      0       127.0.0.53:53         0.0.0.0:*     users:(("systemd-resolve",pid=541,fd=15))

    New public listeners, user-owned processes bound to high ports, and outbound sessions to unfamiliar addresses should be tied back to process paths and login times.
    Related: How to check active network connections in Linux

  12. Sort processes by shortest elapsed runtime to surface recent launches.
    $ ps -eo pid,ppid,user,etimes,lstart,cmd --sort=etimes
        PID    PPID USER     ELAPSED                  STARTED CMD
       2384    2359 user          18 Tue Apr 14 12:06:09 2026 ps -eo pid,ppid,user,etimes,lstart,cmd --sort=etimes
       2229       1 user         101 Tue Apr 14 12:04:43 2026 python3 /home/user/.cache/.svc/agent.py
       2220       1 root         102 Tue Apr 14 12:04:43 2026 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
    ##### snipped #####

    Give extra attention to processes launched from user-writable paths such as /tmp, /var/tmp, /dev/shm, hidden directories under home directories, and application cache directories.

  13. Check current-boot kernel warnings for low-level changes.
    $ sudo journalctl -k -p warning..alert -b --no-pager
    -- No entries --

    If kernel warnings are present, copy the module, device, audit, or crash lines into the incident timeline and compare them with process and file-change times.

  14. Search high-value paths for files changed during the suspected window.
    $ sudo find /etc /home/user -xdev -type f -newermt "2026-04-14 12:00:00" -ls
       656900      4 -rw-r-----   1 root shadow       927 Apr 14 12:04 /etc/shadow
       656899      4 -rw-r--r--   1 root root        1650 Apr 14 12:04 /etc/passwd
      1341641      4 -rw-rw-r--   1 user user         241 Apr 14 12:04 /home/user/.cache/.svc/agent.py
      1179935      4 -rw-------   1 user user          91 Apr 14 12:04 /home/user/.ssh/authorized_keys
    ##### snipped #####

    Wide file scans can take time and can touch access timestamps on some mounts. Start with the incident window and the directories that matter most to the suspected access path.
    Related: How to check file changes in Linux

  15. Search the current filesystem for setuid-root binaries.
    $ sudo find / -xdev -type f -perm -4000 -ls
     1071014     68 -rwsr-xr-x   1 root root        67664 Dec  2  2024 /usr/lib/polkit-1/polkit-agent-helper-1
     1072165    324 -rwsr-xr-x   1 root root       330104 Mar  5 01:55 /usr/lib/openssh/ssh-keysign
     1052235    328 -rwsr-xr-x   1 root root       335120 Jun 25  2025 /usr/bin/sudo
    ##### snipped #####

    Compare this list with a known-good baseline when available. A new setuid-root binary in a writable or unusual path deserves immediate escalation.
    Related: How to list files and folders recursively in Linux

  16. Hash suspicious files before moving or deleting them.
    $ sha256sum /home/user/.cache/.svc/agent.py
    c309f4c1b0b62f007cd9694cfb5b5f6bbac7d5fce91597b5400a1c584280ca94  /home/user/.cache/.svc/agent.py

    Hash files from their original path first, then copy them to evidence storage that ordinary users cannot edit.

  17. Record high-signal indicators in an incident timeline before containment.
    incident-timeline.tsv
    timestamp	source	indicator
    2026-04-14T12:04:52+08:00	sshd	Accepted password for user from 203.0.113.44
    2026-04-14T12:05:03+08:00	sudo	user ran /usr/bin/systemctl enable --now ssh
    2026-04-14T12:04:43+08:00	process	python3 /home/user/.cache/.svc/agent.py pid 2229
    2026-04-14T12:04:43+08:00	file	/home/user/.ssh/authorized_keys modified

    Include timestamps, accounts, source addresses, command lines, file paths, hashes, and the evidence source for each row.