Debian服务器长时间运行后,最棘手的问题往往不是性能下降,而是那些无法通过常规日志直接定位的隐蔽进程。这类进程可能是被替换的系统工具、定时拉起的挖矿程序,或者伪装成内核线程的后门。单纯依赖top或ps做人工巡检,漏报率极高,因为攻击者通常会修改进程名、父进程ID或启动时间戳来规避检查。部署一套自动化的恶意进程监控工具,核心思路不是追求“检测所有恶意行为”,而是建立基线、捕获异常、联动处置。下面直接给出在Debian 11/12环境下可落地的监控方案与具体配置。
监控工具选型与核心逻辑在Debian生态中,成熟的恶意进程监控工具主要有三套:Osquery、Auditd配合自定义脚本、以及Wazuh Agent。Osquery的优势在于用SQL方式查询进程、网络连接、文件哈希等系统状态,适合做周期性基线比对;Auditd是内核级审计框架,能捕获execve系统调用,适合记录每一次进程启动;Wazuh则偏向完整的HIDS,自带规则引擎和主动响应。实际部署时,不建议只装其中一种,而是以Auditd做底层事件采集,Osquery做周期性状态快照,再用一个轻量脚本做异常判定和告警。这样既覆盖实时启动事件,又能发现“已经存在但行为异常”的进程。
安装与配置Auditd捕获进程启动事件Debian仓库自带的Auditd版本足够使用,直接安装:
apt update && apt install auditd audispd-plugins -y
安装后先停止服务,添加针对进程执行和关键目录的监控规则。编辑/etc/audit/rules.d/monitor.rules,写入以下内容:
-a always,exit -F arch=b64 -S execve -k process_exec -w /tmp -p x -k tmp_exec -w /var/tmp -p x -k tmp_exec -w /dev/shm -p x -k shm_exec -w /root -p x -k root_exec
第一条规则记录所有64位架构下的execve调用,后面几条重点监控攻击者常用的可执行文件落地目录。保存后重启auditd使规则生效:
systemctl restart auditd
此时任何进程启动都会被记录到/var/log/audit/audit.log。但原始日志量很大,直接看没有意义,需要配合后续的解析脚本。注意不要监控整个根目录的execve,否则在高负载服务器上会拖慢系统。
部署Osquery做周期性进程快照与基线比对Osquery在Debian上的安装需要添加官方仓库。执行以下命令:
apt install curl gnupg -y curl -fsSL https://pkg.osquery.io/deb/pubkey.gpg | gpg --dearmor -o /usr/share/keyrings/osquery-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/osquery-keyring.gpg] https://pkg.osquery.io/deb deb main" > /etc/apt/sources.list.d/osquery.list apt update && apt install osquery -y
安装完成后,先手动跑一条查询,验证能否正常获取进程列表和对应的启动时间、父进程、可执行文件哈希:
osqueryi --json "SELECT pid, name, parent, uid, gid, path, start_time, sha256 FROM processes;"
这条查询会返回当前所有进程的详细信息。恶意进程的常见特征包括:启动时间在服务器非业务时段、父进程是已退出的短生命周期进程、可执行文件位于/tmp或/dev/shm、哈希值与系统包管理器记录不一致。Osquery本身不做判断,它只负责提供数据。你需要写一个定时任务,把查询结果保存为JSON快照,然后与上一轮快照做差异比对。
编写异常检测脚本实现自动化判定光有Auditd和Osquery的数据还不够,必须有一个脚本把“数据”变成“告警”。下面给出一个基于Osquery快照比对的Python脚本核心逻辑,部署在/usr/local/bin/proc_monitor.py:
#!/usr/bin/env python3
import subprocess, json, os, hashlib, sys
from datetime import datetime
SNAPSHOT_DIR = "/var/lib/proc_monitor"
BASELINE_FILE = os.path.join(SNAPSHOT_DIR, "baseline.json")
CURRENT_FILE = os.path.join(SNAPSHOT_DIR, "current.json")
ALERT_LOG = "/var/log/proc_monitor_alerts.log"
def get_processes():
cmd = ["osqueryi", "--json", "SELECT pid, name, parent, uid, path, start_time, sha256 FROM processes;"]
out = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
return json.loads(out)
def load_json(path):
if os.path.exists(path):
with open(path) as f:
return json.load(f)
return []
def save_json(data, path):
with open(path, "w") as f:
json.dump(data, f, indent=2)
def alert(msg):
with open(ALERT_LOG, "a") as f:
f.write(f"{datetime.now().isoformat()} {msg}\n")
def main():
os.makedirs(SNAPSHOT_DIR, exist_ok=True)
current = get_processes()
baseline = load_json(BASELINE_FILE)
save_json(current, CURRENT_FILE)
if not baseline:
save_json(current, BASELINE_FILE)
print("Baseline created.")
return
baseline_map = {p["pid"]: p for p in baseline}
current_map = {p["pid"]: p for p in current}
for pid, proc in current_map.items():
if pid not in baseline_map:
if proc["path"].startswith(("/tmp", "/var/tmp", "/dev/shm")):
alert(f"NEW_PROC_IN_TMP pid={pid} name={proc['name']} path={proc['path']} sha256={proc['sha256']}")
elif proc["uid"] == "0" and proc["parent"] not in current_map:
alert(f"NEW_ROOT_PROC_ORPHAN pid={pid} name={proc['name']} path={proc['path']}")
else:
old = baseline_map[pid]
if old["path"] != proc["path"] and proc["uid"] == "0":
alert(f"PROC_PATH_CHANGED pid={pid} old={old['path']} new={proc['path']}")
save_json(current, BASELINE_FILE)
if __name__ == "__main__":
main()
这个脚本的核心判定规则有三条:第一,新出现的进程且可执行文件位于临时目录,直接告警;第二,新出现的root进程但父进程已经不存在,说明可能是脱离终端或守护进程化的可疑行为;第三,同一个PID的可执行文件路径发生变化,这在正常系统中几乎不可能发生。脚本每次运行后会把当前快照更新为基线,因此它更适合做周期性巡检,比如每5分钟跑一次。
配置Systemd定时器与告警联动把上面的脚本加入systemd timer,实现无人值守运行。创建/etc/systemd/system/proc-monitor.service:
[Unit] Description=Process anomaly monitor After=network.target [Service] Type=oneshot ExecStart=/usr/bin/python3 /usr/local/bin/proc_monitor.py
再创建/etc/systemd/system/proc-monitor.timer:
[Unit] Description=Run process monitor every 5 minutes [Timer] OnBootSec=2min OnUnitActiveSec=5min AccuracySec=30s [Install] WantedBy=timers.target
启用并启动定时器:
systemctl daemon-reload systemctl enable proc-monitor.timer systemctl start proc-monitor.timer
告警日志会持续写入/var/log/proc_monitor_alerts.log。如果服务器上已经部署了邮件或即时通讯告警通道,可以在脚本的alert函数里追加发送逻辑。更稳妥的做法是把告警日志接入rsyslog或直接由监控系统采集。
结合Auditd实时日志做补充检测Osquery快照比对存在一个盲区:如果恶意进程在两次巡检之间启动并退出,就不会被捕获。Auditd的execve日志正好弥补这个缺口。可以用ausearch命令筛选可疑的进程启动记录:
ausearch -k process_exec --start recent -i | grep -E "exe=.*(/tmp|/var/tmp|/dev/shm)"
这条命令会列出最近一段时间内从临时目录启动的所有进程。攻击者常用的手法是把恶意二进制释放到/tmp或/dev/shm后执行,因此这类记录需要重点关注。如果想把Auditd的实时事件也接入自动告警,可以配置audispd将日志转发到syslog,再用rsyslog的过滤规则匹配关键字后触发告警脚本。但要注意控制告警量,避免在业务高峰期被正常程序(如某些安装脚本)淹没。
加固建议与误报处理部署监控工具后,前一两周一定会出现误报。常见的误报来源包括:包管理器在/tmp下执行临时脚本、某些Java应用使用/tmp存放JNI库、以及容器运行时在/dev/shm创建共享内存文件。处理误报的原则是“先确认、再加白名单”,不要因为误报多就关闭规则。可以在脚本中增加一个白名单文件/var/lib/proc_monitor/whitelist.json,记录允许的进程名、路径哈希或父进程特征。白名单的粒度要尽量细,只放行经过验证的特定行为,而不是整个目录。
另外,Debian服务器上建议同步开启以下加固措施:限制/tmp目录的noexec挂载选项(如果业务允许)、定期用debsums校验系统二进制完整性、对/root和/etc目录启用文件完整性监控。恶意进程监控工具只是最后一道防线,真正的安全来自减少攻击面与快速发现异常的组合。
长期运维与基线更新服务器业务变更后,进程基线必须同步更新,否则会产生大量误报或漏报。建议每月人工检查一次基线文件,确认其中的进程列表与当前业务运行状态一致。如果服务器上部署了新的常驻服务,应该先手动运行一次脚本生成新基线,再恢复定时任务。对于Auditd日志,建议配置日志轮转,保留至少30天的历史数据,以便在发生入侵后进行溯源分析。监控工具本身也需要监控,定期检查proc-monitor.timer是否正常运行、告警日志是否有新内容,防止攻击者先停掉监控再实施后续动作。
这套方案在Debian 11和12上均可稳定运行,资源占用很低,Osquery每次查询耗时通常不超过1秒,Auditd的内核审计开销在可接受范围内。关键是坚持长期运行并根据实际环境调优规则,才能真正发挥恶意进程监控的价值。
