Ubuntu系统在通过SSH登录时,默认会在终端显示一段欢迎信息,也就是motd(Message of the Day)。很多管理员发现,这段信息里直接暴露了当前操作系统的具体发行版本、内核版本,甚至某些基础软件包的详细版本号。例如,登录后第一眼就能看到“Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-91-generic x86_64)”这样的字样。这对于渗透测试者和恶意扫描器来说,等于直接送上了精准的攻击路标。处理这个问题,核心在于理解motd的信息生成机制,然后从源头掐断版本泄露的路径,而不是简单地删除文件了事。
motd版本信息泄露的根源在哪里很多人以为motd就是/etc/motd这一个静态文件,实际上Ubuntu从很早的版本开始,就采用了动态motd机制。真正的入口是PAM模块pam_motd.so,它会在每次登录时触发。关键目录是/etc/update-motd.d/,这个目录下的可执行脚本会按数字顺序依次运行,将它们输出的内容拼接起来,最终呈现给登录用户。版本信息的泄露主要来源于两个脚本:一个是00-header,另一个是10-help-text,在某些版本中还涉及50-landscape-sysinfo。00-header脚本负责生成系统名称、版本、内核等核心信息,它通过读取/etc/os-release和运行uname -a来获取数据。10-help-text则可能包含指向官方文档的链接,间接暴露了发行版类型。只要这些脚本还在运行,版本信息就会持续暴露。
直接禁用动态motd功能最彻底的方案是让PAM不再调用动态motd。编辑/etc/pam.d/sshd文件,找到包含pam_motd.so的那一行,通常长这样:
session optional pam_motd.so motd=/run/motd.dynamic
将其注释掉,或者直接删除该行。同时,在同一文件中检查是否还有调用pam_motd.so的其他行,一并处理。保存文件后,重启SSH服务:
systemctl restart sshd
这样做之后,登录时将不再显示任何动态生成的信息。如果你仍然希望显示一条简单的欢迎语,可以手动编辑/etc/motd文件,这是一个静态文件,内容完全由你控制。例如,只写入“Authorized access only. All activities are monitored.”这样的警示语,不包含任何系统信息。注意,如果/etc/motd不存在,需要手动创建,并且确保权限为644。这种方法的优点是干净利落,缺点是完全失去了动态信息展示的能力,比如系统负载、磁盘用量等有用的管理信息也会一并消失。
精准修改脚本,保留有用信息如果你希望保留动态motd的其他功能,只去掉版本信息,就需要对/etc/update-motd.d/目录下的脚本进行外科手术。首先看00-header,用文本编辑器打开它:
vi /etc/update-motd.d/00-header
你会看到类似这样的代码片段:
[ -r /etc/os-release ] && . /etc/os-release
if [ -z "$PRETTY_NAME" ]; then
PRETTY_NAME=$(uname -s)
fi
printf "Welcome to %s (%s %s %s)\n" "$PRETTY_NAME" "$(uname -o)" "$(uname -r)" "$(uname -m)"
这段代码就是罪魁祸首。直接将其全部注释掉,或者把printf那行改为输出一句不包含版本信息的话,比如:
printf "Welcome to the internal server.\n"
保存退出后,务必移除脚本的执行权限,或者至少确保修改后的脚本在运行时不报错。更优雅的做法是直接删除00-header的符号链接或文件本身,但保留其他脚本。接下来检查10-help-text,这个脚本通常输出一个指向帮助文档的URL,其中包含“http://www.ubuntu.com/support”这样的链接,直接暴露了发行版类型。打开该文件,注释掉所有echo或printf行,或者直接删除该文件。处理完脚本后,可以手动运行一次run-parts来验证效果:
run-parts /etc/update-motd.d/
观察输出是否符合预期。这种方法既屏蔽了版本信息,又保留了磁盘检查、重启提示等实用模块。
通过内核启动参数控制信息输出Linux内核在启动时会记录大量信息,其中一部分会通过/proc/version和uname命令暴露。虽然motd主要从os-release获取数据,但uname -r显示的内核版本同样敏感。要彻底隐藏内核版本,可以在GRUB配置中修改内核启动参数。编辑/etc/default/grub,找到GRUB_CMDLINE_LINUX这一行,添加quiet loglevel=0参数。注意,这并不能改变uname -r的输出,它只是减少了启动时的屏幕输出。真正要改变uname返回的信息,需要在内核编译阶段处理,对于二进制发行版不现实。但我们可以通过其他手段,比如在motd脚本中不调用uname,或者用自定义脚本覆盖其输出。一个取巧的办法是创建一个假的uname命令,放在受信任的路径中,但这对系统维护可能造成困扰,不推荐在生产环境使用。
利用/etc/os-release文件的定制化/etc/os-release是systemd系统用来标识操作系统的标准文件,00-header脚本正是读取了它里面的PRETTY_NAME字段。直接编辑这个文件,将PRETTY_NAME改为一个自定义的字符串,例如:
PRETTY_NAME="Enterprise Linux Server"
修改后,不仅motd会显示这个自定义名称,很多依赖这个文件的系统工具也会读取到假信息。这可能会影响某些管理软件的判断,比如监控代理、自动化运维工具等。因此,修改前需要评估对整体运维体系的影响。更安全兼具隐蔽性的做法是,不修改os-release本身,而是修改00-header脚本,让它从一个自定义的环境文件读取信息,或者干脆不读取os-release。这样os-release保持原样,不影响其他工具,而motd则显示定制内容。
处理SSH版本信息的泄露除了motd,SSH服务本身在建立连接时也会在banner中发送版本信息。Ubuntu默认的SSH配置通常会在连接时显示“SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.4”这样的字符串。攻击者借此可以知道操作系统类型和OpenSSH的具体版本。要隐藏这个信息,需要编辑/etc/ssh/sshd_config,找到DebianBanner这一行,设置为no:
DebianBanner no
同时,确保没有自定义的Banner文件包含敏感信息。如果存在Banner指令指向某个文件,检查该文件内容。保存后重启sshd服务。这样修改后,客户端在建立连接时只会看到“SSH-2.0-OpenSSH_8.9p1”,不再包含Ubuntu字样。这虽然仍暴露了OpenSSH版本,但至少隐藏了发行版信息。要彻底隐藏OpenSSH版本,需要修改源代码重新编译,对于大多数场景来说,隐藏发行版信息已经足够提高攻击门槛。
验证和持续维护所有修改完成后,务必进行多轮测试。新开一个终端窗口,尝试SSH登录,仔细观察登录前后的所有输出。不仅要看motd部分,还要注意登录前SSH banner、登录后的shell提示符是否包含版本信息。有些#有些默认的bash提示符会包含主机名和内核版本,检查/etc/bash.bashrc和用户目录下的.bashrc、.profile等文件,确保PS1变量没有使用\s或\v等转义符来显示版本。一个安全的PS1设置可以是:
PS1='\u@\h:\w\$ '
这只会显示用户名、主机名和当前目录。另外,系统更新可能会覆盖/etc/update-motd.d/下的脚本,尤其是在apt upgrade过程中。建议在更新后运行一次检查脚本,或者将自定义的motd脚本用配置管理工具固化。对于使用Ansible、Pupet等工具的环境,可以将这些文件的修改写成自动化任务,确保每次系统初始化或更新后,版本信息泄露的修补措施能自动生效。
进阶:使用内核安全模块限制信息读取对于安全要求极高的场景,可以考虑使用AppArmor或SELinux来限制非特权用户对/proc/version、/et/c/os-release等文件的读取。Ubuntu默认启用了AppArmor,可以为SSH守护进程或登录Shell编写额外的策略。例如,创建一个AppArmor配置文件,禁止sshd的子进程读取/etc/os-release。不过这种做法复杂度较高,容易造成系统异常,需要充分的测试。更实际的做法是通过文件系统权限控制:将/etc/os-release的权限改为640,所有者设为root,组设为某个只有管理员才能读取的组。但要注意,很多合法的系统服务可能需要读取这个文件,修改权限前必须梳理清楚依赖关系。通常,在标准的企业环境中,仅修改motd脚本和SSH配置就已经能将攻击面降低到可接受的水平。
整个处理过程的核心思路是:识别信息泄露的路径,从源头控制输出内容,而不是在末端拦截。动态motd的脚本机制给了管理员极大的定制空间,利用好这个机制,既能保留系统管理所需的动态信息,又能有效隐藏版本细节。每一次系统更新后,记得复查这些脚本是否被覆盖着一键还原,将检查步骤加入日常运维清单,才能长久保持安全状态。
