如何结束和暂停agent进程

admin 2021年9月24日08:57:06评论103 views字数 2890阅读9分38秒阅读模式

问题背景

就像 如何避免文件上报-制作大文件 说的,hids agent会上报很多信息,安全防护策略也是围绕"agent上报的信息"制定的。

那如果攻击者"结束、暂停agent进程",导致agent功能废掉,是不是就能够绕过hids的防护呢?

研究过程中,我发现并不能轻易地结束agent进程;"结束和暂停agent进程"会带来"心跳失联"的异常特征,可能引起"运营人员"的注意从而导致攻击行为暴露。

本文主要讨论以下问题:

  • 怎么结束agent进程?
  • 怎么暂停agent进程?
  • "心跳失联"

结束agent进程

  • 怎么结束agent进程?

    在linux上执行 kill -SIGKILL 进程号 是一定可以结束进程的,因为 SIGKILL信号不能被忽略或者做其他处理。

    但是我们杀掉hids agent进程后,agent进程会自动重启。

  • 怎么实现"挂掉自动重启"?

    经过调研,"挂掉自动重启"机制在linux最常见的实现方式包括:

    通过 systemd、supervisord 等实现

    crond:通过计划任务

    watchdog

    字节hids文档[1] 推荐的也是"systemd"和"crond"这两种方式。

    如何结束和暂停agent进程
    字节hids文档

    有对supervisord、systemd实现原理感兴趣的,可以看最后一节。

  • 怎么结束被systemd、supervisord管理的进程?

    supervisord和systemd等管理的进程可以通过"supervisorctl"和"systemctl"等命令关掉。

    这两命令实际也是通过"本地socket通信"告诉supervisord和systemd结束进程,所以当没有办法使用"supervisorctl"和"systemctl"命令时,也可以编写程序直接和"本地socket文件"通信。

暂停agent进程

  • 怎么暂停agent进程?

    向进程发送SIGSTOP、SIGTSTP、SIGTTIN等信号都可能暂停进程,推荐用SIGSTOP信号,原因是进程收到其他信号还能忽略,收到SIGSTOP信号却只能暂停进程。

    这个结论可以在man 7 signal看到

    The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.

  • "暂停的agent进程"是否会被自动重启或者恢复运行状态?

    这里还存在一个小问题:当子进程被暂停或者结束时,父进程是可以收到SIGCHLD信号的。那systemd遇到子进程暂停时,会不会重启进程?

    经过测试和源码对比,我的结论是:systemd不会。

  • 测试某hids

    经过对某hids做测试,发现stop状态的agent进程会被恢复成运行状态。

    agent进程的父进程的父进程才是systemd,所以估计是agent父进程收到SIGCHLD信号后向agent进程发送了SIGCONT信号恢复运行状态。

    当把hids所有的agent进程都暂停后,符合预期:所有agent进程一直被暂停。

"心跳失联"的问题

  • "心跳失联"是什么?

    正常情况下,agent按照一定频率会发送"心跳包"告诉server自己存活。

    如果"结束和暂停agent进程",就会影响到"心跳包"的发送。进而server端是可以感知到agent异常的。

    这种异常可能引起"运营人员"的注意从而导致攻击行为暴露。

  • 怎么解决"心跳失联"的问题?

    凭空想象的一种解决方法:恶意样本 先暂停agent进程,等到恶意行为完成后,再恢复agent进程。

    这样心跳也存在,也有可能绕过hids防护。

systemd是怎么知道"进程挂掉"?

先说我验证的结论:

  • systemd为sigchild信号注册了一个处理函数,函数内部调用waitid处理退出的子进程。
  • 当systemd管理的进程退出时,systemd进程会收到sigchild信号,进而跳转到处理函数。

systemd源码[2]可以看到manager_setup_sigchld_event_source函数就是"sigchild信号的处理函数"。如何结束和暂停agent进程

如果systemd进程号为1,也可以通过strace -p 1来验证结论:

结束systemd管理的进程时(下面例子中是83206号进程),可以看到waitid系统调用

waitid(P_ALL, 0, {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=83206, si_uid=0, si_status=SIGKILL, si_utime=0, si_stime=0}, WNOHANG|WEXITED|WNOWAIT, NULL) = 0
...
waitid(P_PID, 83206, {si_signo=SIGCHLD, si_code=CLD_KILLED, si_pid=83206, si_uid=0, si_status=SIGKILL, si_utime=0, si_stime=0}, WEXITED, NULL) = 0
waitid(P_ALL, 0, {}, WNOHANG|WEXITED|WNOWAIT, NULL) = 0

supervisord和systemd有点类似,不一样的是supervisord似乎只用wait等系统调用,来判断子进程是否已经退出。

[root@instance-fj5pftdp ~]# ps aux|grep supervi
root     13170  0.0  0.0  56368 12760 ?        Ss   6月01  71:47 /usr/bin/python /usr/bin/supervisord -c /data/poc-web-api/docker/supervisor.conf
[root@instance-fj5pftdp ~]# strace -p 13170 2>&1 | grep -i wait
...
wait4(-1, 0x7fffbe389d9c, WNOHANG, NULL) = 0
wait4(-1, 0x7fffbe389d9c, WNOHANG, NULL) = 0
wait4(-1, 0x7fffbe389d9c, WNOHANG, NULL) = 0
wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGKILL}], WNOHANG, NULL) = 5557

总结

  • "结束进程"需要考虑到"进程挂掉重启"的场景
  • "暂停进程"需要考虑到"父进程恢复进程状态"的场景

本文提到的手段没有在真实的对抗中实践过,仅仅是我自己的研究,欢迎与我交流。

参考资料

[1]

字节hids文档: https://github.com/bytedance/Elkeid/blob/main/server/docs/quick-start-zh_CN.md

[2]

systemd源码: https://github.com/systemd/systemd/blob/71a80dcc0b64b01c73e7141c4292ef301543a011/src/core/manager.c


  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年9月24日08:57:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   如何结束和暂停agent进程http://cn-sec.com/archives/555177.html

发表评论

匿名网友 填写信息