Ubuntu · Accounts & authentication

Keep system details out of the message of the day

When it fails: The message of the day shows the operating system version and kernel release to every user who logs in. Every login session, including a compromised account, learns the exact release and kernel to target.

CB-UBU-AUTH-002lowactiveautomatedautomated fixprofile core

As released in v0.1.1 (beta). Current state.

Description

After login, pam_motd shows /etc/motd and a dynamic message built by running the executable scripts of /etc/update-motd.d/ with run-parts. On Ubuntu, 00-header prints the full distribution version and the kernel name, release and architecture (Welcome to Ubuntu 24.04.5 LTS (GNU/Linux 6.8.0-147-generic x86_64)), and some other scripts can repeat the version (on 20.04, the ESM notice of 91-contract-ua-esm-status).

This control runs the dynamic message as pam_motd does and checks that its output names neither the running kernel release nor the Ubuntu <version> of the system.

The fix records the new mode with dpkg-statoverride, so a package upgrade (for example of base-files, which owns 00-header) does not make the script executable again. Ubuntu ships no /etc/motd; its check only applies when an administrator created one.

Rationale

The kernel release and the exact distribution version tell an attacker who reached a shell, or who only watches a session, which local privilege escalation exploits apply. There is no need to show them to every user at every login.

Impact

Users no longer see the welcome line with the version and kernel at login, nor any other script that printed them. They can still run uname -a or lsb_release -a. The other parts of the message (help links, system load, pending updates) are kept.

Setting

Location/etc/motd, /etc/update-motd.d/00-header
Default00-header executable, no /etc/motd
RecommendedScripts that print the version or kernel not executable, /etc/motd without OS details

Audit

  1. If /etc/motd exists, it must not contain the escape sequences \m, \r, \s, \v or Ubuntu followed by a version.
  2. Run run-parts --lsbsysinit /etc/update-motd.d as root. The output must not contain the result of uname -r, nor Ubuntu followed by the VERSION_ID of /etc/os-release.

Automated: every check must pass. Scanners report each check by its id.

motd-no-os

/etc/motd does not reveal the system version (not applicable when absent).

path: /etc/motd
pattern: \\[mrsv]|Ubuntu [0-9]
expect: no_match

dynamic-motd

Output of the dynamic MOTD scripts, as built by pam_motd.

script: |
  [ -d /etc/update-motd.d ] || { echo "no /etc/update-motd.d"; exit 2; }
  v=$(. /etc/os-release && printf '%s' "$VERSION_ID")
  k=$(uname -r)
  hits=$(timeout 120 run-parts --lsbsysinit /etc/update-motd.d 2>/dev/null | grep -F -e "$k" -e "Ubuntu $v")
  if [ -n "$hits" ]; then printf '%s\n' "$hits" | head -n 3; exit 1; fi
  echo "no kernel release or Ubuntu $v in the dynamic MOTD"

Remediation

  1. For each script of /etc/update-motd.d/ whose output shows the version or the kernel (00-header by default), remove the execute bit in a way that survives upgrades: dpkg-statoverride --update --add root root 0644 /etc/update-motd.d/00-header.
  2. Remove OS names, versions and escape sequences from /etc/motd, if it exists.
Restartnone
Disruptionnone

Rollback

For each changed script: dpkg-statoverride --remove /etc/update-motd.d/<script> then chmod 755 /etc/update-motd.d/<script>.

scripts

#!/bin/sh
# CB-UBU-AUTH-002 / scripts - stop the dynamic MOTD scripts that print the OS version or the kernel release.
# The mode is recorded with dpkg-statoverride so package upgrades keep it. Idempotent.
# SPDX-License-Identifier: Apache-2.0
set -eu
d=/etc/update-motd.d
[ -d "$d" ] || exit 0
v=$(. /etc/os-release && printf '%s' "$VERSION_ID")
k=$(uname -r)
for f in "$d"/*; do
  [ -f "$f" ] && [ -x "$f" ] || continue
  timeout 60 "$f" 2>/dev/null | grep -qF -e "$k" -e "Ubuntu $v" || continue
  if [ -L "$f" ]; then
    echo "$f is a symbolic link and prints system details: review it by hand" >&2
    exit 1
  fi
  dpkg-statoverride --list "$f" >/dev/null 2>&1 || dpkg-statoverride --update --add root root 0644 "$f"
  chmod 0644 "$f"
  echo "disabled $f"
done

Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.

References

Mappings

FrameworkIDRelationNote
NIST SP 800-53 Rev. 55.1.1AC-8related

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

People and lifecycle

Authors
Verified2026-10-19 by Sn0wAlice on 20.04, Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic. Fails on a fresh install (00-header prints the version and kernel, the ESM notice repeats Ubuntu 20.04); fix sets both scripts to 0644 through dpkg-statoverride; kept after reboot; fix idempotent.
Verified2026-10-19 by Sn0wAlice on 22.04, Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic. Fails on a fresh install (00-header prints the version and kernel); fix sets it to 0644 through dpkg-statoverride; kept after reboot; fix idempotent.
Verified2026-10-19 by Sn0wAlice on 24.04, Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic. Fails on a fresh install (00-header prints the version and kernel); fix sets it to 0644 through dpkg-statoverride; kept after reboot; fix idempotent.
Verified2026-10-19 by Sn0wAlice on 26.04, Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic. Fails on a fresh install (00-header prints the version and kernel); fix sets it to 0644 through dpkg-statoverride; kept after reboot; fix idempotent.
Introduced inv0.1.0
Created, updated2026-10-19, 2026-10-19
Translationsfr