使用systemtap进行抓包
systemtap是一个监控追踪系统调用、调试内核的工具,可以在系统运行时动态的调试内核,可以说是内核开发者必须要掌握的工具。
我使用这个工具是因为线上的应用存在fastjson的漏洞,被黑客执入了木马,在骨干交换机上抓到异常流量后,确定了内网服务器IP,登录服务器后使用lsmod、ps、netstat、ss、lsof这些命令无法发现异常,甚至连tcpdump都无法抓到这个木马,猜测是rootkit木马加载了内核模块、实现了无文件、无进程,只有每20分钟一次的心跳包,想揪出来非常困难。
一、安装
编译是比较麻烦的,centos base源里就有systemtap,主要有三个依赖:
- kernel-debuginfo 在CentOS-Debuginfo源里,默认没启用
- elfutils
- kernel-devel ,和 elfutils 一样,bases源里就有,yum会自己解决依赖
kernel-debuginfo和kernel-devel 需要保证和当前运行的内核小版本号一致
使用yum安装:
yum install systemtap systemtap-devel systemtap-runtime
yum --enablerepo=base-debuginfo install kernel-debuginfo kernel-debuginfo-common kernel-devel
如果base源里没有找到systemtap,可以试试centosplus源:
yum --enablerepo=centosplus install systemtap systemtap-devel systemtap-runtime
当然也可以安装时直接指定版本,建议用这种方式:
yum install -y kernel-devel-$(uname -r)
yum --enablerepo=base-debuginfo install -y kernel-debuginfo-$(uname -r) kernel-debuginfo-common-$(uname -m)-$(uname -r)
按上面命令装好后验证一下kernel-debuginfo、kernel-devel版本是否和内核一致:
rpm -qa |grep kernel
如果系统内有多个版本的内核,建议卸载多余的内核、kernel-headers、kernel-devel和kernel-firmware,不然运行systemtap,容易出现错误。
二、systemtap脚本
目的是揪出来异常流量对应的进程、文件及文件位置,先来看看官网给的监控进程创建的脚本:
probe kprocess.create
{
printf("%-25s: %s (%d) created %d\n",
ctime(gettimeofday_s()), execname(), pid(), new_pid)
}
probe kprocess.exec
{
printf("%-25s: %s (%d) is exec'ing %s\n",
ctime(gettimeofday_s()), execname(), pid(), filename)
}
systemtap脚本执行需要root权限,将上述脚本保存为forktracker.stp,执行方法:
stap forktracker.stp
可以将输出保存的文件:
stap -v forktracker.stp -o fork.log
改进一下,更详细一点:
probe kprocess.create {
printf("%-25s: %s (%d:%d) created %d:%d\n",
ctime(gettimeofday_s()), execname(), pid(), tid(), new_pid, new_tid)
}
probe kprocess.exec {
printf("%-25s: %s (%d) is exec'ing %s\n",
ctime(gettimeofday_s()), execname(), pid(), filename)
}
在来一个监控目的端口为443的流量。
cat tcp.stp
#! /usr/bin/env stap
probe syscall.connect {
if(uaddr_ip_port=="443"){
printf("ip:%s port:%s cmd:%s pid:%d ppid:%d\n",uaddr_ip, uaddr_ip_port,execname(),pid(),ppid())
}
}
执行stap tcp.stp,然安在另外一个窗口执行 telnet 1.1.1.1 53进行测试,输出
ip:1.1.1.1 port:53 cmd:telnet pid:14934 ppid:10130
上面的信息不够详细,在来一个输出更详细的版本:
#! /usr/bin/env stap
probe syscall.connect {
if(uaddr_ip_port=="1521"){
printf("Time:%s remote_ip:%s remote_port:%s local_cmd:%s pid:%d local_pcmd:%s ppid:%d euid:%d egid:%d env_PWD:%s \n",
tz_ctime(gettimeofday_s()),uaddr_ip, uaddr_ip_port,execname(),pid(),pexecname(),ppid(),euid(),egid(),env_var("PWD"))
}
}
执行 telnet 8.8.8.8 1521进行测试,输出
Time:Tue Jul 21 15:34:50 2020 HKT remote_ip:8.8.8.8 remote_port:1521 local_cmd:telnet pid:23892 local_pcmd:bash ppid:10130 euid:500 egid:500 env_PWD:/home/tomcat
上面两个只能监控tcp流量,在改进一下监控目的端口,不区分协议的,监控从本机出去到53端口的流量的进程
probe netfilter.ip.local_out {
if (dport == 53)
printf("%s[%d] %s:%d\n", execname(), pid(), daddr, dport)
printf("Time:%s remote_ip:%s remote_port:%s local_cmd:%s pid:%d local_pcmd:%s ppid:%d euid:%d egid:%d env_PWD:%s \n",
tz_ctime(gettimeofday_s()),uaddr_ip, uaddr_ip_port,execname(),pid(),pexecname(),ppid(),euid(),egid(),env_var("PWD"))
}
输出
telnet[28032] 1.1.1.1:53
Time:Tue Jul 21 15:34:50 2020 HKT remote_ip:1.1.1.1 remote_port:53 local_cmd:telnet pid:28032 local_pcmd:bash ppid:10130 euid:500 egid:500 env_PWD:/home/tomcat
通过上面的操作基本上就确定了木马的文件名,接下来就是清理木马。centos单用户模式是维护模式,类似windows的安全模式,正常只会加载系统启动所需的最少服务,木马未针对单用户模式进行处理,所以在单用户模式下没有加载隐藏进程的内核模块,重启进入系统后直接find文件名清理即可。
参考文章:
https://sourceware.org/systemtap/documentation.html
https://sourceware.org/systemtap/examples/