大家好,我们是 NOP Team,今天这篇文章要介绍的是如何处置 ICMP/DNS 隧道,处理思路是一致的,但 icmp 更具有代表性,所以下文均以 icmp 隧道视角讲述
在 2024-05-23 这天,有朋友通过公众号留言,询问了我关于 ICMP 隧道的处置方法,但是后来没聊几句这位朋友就没回复了,可能是解决了吧
昨天有看到,一个朋友在朋友圈发布了一款 ICMP 通信工具,还提到演练将至,给蓝队兄弟上点强度
于是便有了今天这篇文章,当然这是玩笑话,根本原因在于,我看到的网络安全文章中,基本上都在讨论如何发现 ICMP 隧道,感觉要把它拆开了,揉碎了,仔细研究一遍,但对于应急响应人员来说,如何发现意义不大,那是安全设备的功能,我们要考虑的是如何处置,而处置的难点在于:如何找到发出ICMP数据包的进程,这个内容我没有在网络安全文章中看到,反而是搞网络的朋友们可能研究过,这里涉及一个近几年很火的技术 —— BPF
BPF 是 Berkeley Packet Filter 的缩写,它是一种高效的数据包过滤和程序执行框架,最初设计于1992年,用于提高网络数据包的处理性能。起初,它的主要应用是在Unix和Linux系统中作为数据包嗅探工具的一部分,比如tcpdump和Wireshark,用来在内核级别过滤网络数据包,从而减少不必要的数据从内核空间传递到用户空间的过程,提升了效率。
随着时间的发展,BPF的功能得到了极大的扩展,特别是在2013年之后,Alexei Starovoitov和Daniel Borkman等人对BPF进行了重新设计和实现,引入了eBPF(extended Berkeley Packet Filter),使其成为了一个更加通用的内核执行引擎。eBPF不仅限于网络数据包过滤,还可以用于各种内核跟踪、安全监控、网络功能、性能分析等广泛场景。
通过eBPF(extended Berkeley Packet Filter),我们编写程序来获取ICMP请求包与之关联的进程信息,包括但不限于以下几点:
由于并没有学过相关知识,所以这里使用 bpftrace 来利用 ebpf 帮助我们完成功能,这是一个高级的动态跟踪工具和域特定语言(Domain Specific Language, DSL),专为Linux系统设计,用于简化和加速系统及应用程序的监控及故障排查过程。它是基于eBPF(Extended Berkeley Packet Filter)技术构建的,允许用户编写脚本以收集内核和用户空间的运行时信息,而无需修改或重启系统
https://zh.wikipedia.org/wiki/BPF
其实核心思路很简单,icmp 协议的数据包没有端口的概念,当然icmp隧道可能会需要监听端口哈,但很难直接通过端口来判断,我们还是聚焦在 icmp 数据包对应的ip上
我们来处理 icmp 隧道,肯定是安全设备有告警了,那么我们需要获取到icmp隧道的对应的ip或者域名,如果是ip,我们后期在利用 ebpf 的过程中,筛选与该ip通信的icmp流量,之后查找pid 就好了
如果是域名,可能会涉及到cdn,我们可以通过修改系统 hosts
文件,将该域名指向到特定的ip,当然是存在的ip,之后进行过滤
首先,我们需要在要运行的服务器上安装 bpftrace
,以 Ubuntu 为例
sudo apt update
sudo apt install bpftrace
我们使用以下 bfptrace 脚本监控与某个特定IP的所有请求,包括icmp,也包括dns
request_monitor.bt
#!/usr/bin/bpftrace
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/socket.h>kprobe:__dev_queue_xmit
{
@dev_queue_xmit[tid]=count();
@skb[tid]=(struct sk_buff *)arg0;
}
kprobe:__dev_queue_xmit
/@skb[tid]/
{
$skb = @skb[tid];
$iph = (struct iphdr *)($skb->head + $skb->network_header);
$sip = ntop(AF_INET, $iph->saddr);
$dip = ntop(AF_INET, $iph->daddr);
if ($iph->daddr == $1 || $iph->daddr == $2){
printf("[+] Found the request to %s \n", $dip);
printf("[-] pid=%d, thread_id=%d, comm=%s \n", pid,tid,comm);
}
}
这里涉及到一个问题,我们是希望查看特定IP的请求,但是直接将IP传递给 bpftrace 脚本比较困难,它不是很好处理,所以这里使用 shell 脚本进行辅助,主要是帮我们把 IP 转化为数字,并且同时转化为大端和小端序,也就是说我们会同时监听大端和小端的地址
request_monitor.sh
#!/bin/bashconvert_ip_to_integers() {
local ip=$1
IFS='.' read -r a b c d <<< "$ip"
# 计算大端序 (big-endian)
be_ip_int=$((a << 24 | b << 16 | c << 8 | d))
# 计算小端序 (little-endian),需要颠倒字节顺序
le_ip_int=$((d << 24 | c << 16 | b << 8 | a))
echo "$be_ip_int $le_ip_int"
}
# IP地址参数
IP="$1"
# 调用函数,获取大端和小端的整数表示
read big_endian little_endian <<< $(convert_ip_to_integers "$IP")
# 假设BPFtrace脚本期望两个参数,分别对应大端和小端
echo "Start listening for the request to $IP"
echo ""
sudo ./request_monitor.bt $big_endian $little_endian
脚本参考
https://blog.csdn.net/native_lee/article/details/124751325
使用 pingtunnel 工具来模拟攻击者搭建的 icmp 隧道,服务端(攻击者)地址为 192.168.31.83
wget https://github.com/esrrhs/pingtunnel/releases/download/2.8/pingtunnel_linux_amd64.zip
unzip pingtunnel_linux_amd64.zip# 启动服务端,设置 key 为 1234
./pingtunnel -type server -key 1234
wget https://github.com/esrrhs/pingtunnel/releases/download/2.8/pingtunnel_linux_amd64.zip
unzip pingtunnel_linux_amd64.zip# 连接服务端
sudo ./pingtunnel -type client -l :4445 -s 192.168.31.83 -t 192.168.31.83:4444 -tcp 1 -key 1234
根据安全设备告警,得知存在 ICMP 隧道,连接地址为 192.168.31.83
通过 netstat
进行查看
果然看不到
因为当前服务器已经默认存在 bpftrace
,所以这里就先不重复安装了,将 request_monitor.sh
和 request_monitor.bt
复制到受害服务器上,给两个脚本赋予执行权限
chmod +x request_monitor.sh
chmod +x request_monitor.bt
之后执行以下命令
sudo ./request_monitor.sh 192.168.31.83
等待几秒后,开始监听
可以看到,发现了 pid 为 14990 的进程存在与 192.168.31.83 的通信,而且是多线程的,我们使用 ps -aux
验证一下是不是这样
可以看到, 14990 进程确实为 icmp 隧道的进程,成功找到icmp 隧道
发现进程id后剩下就没什么可说的了,常规处置流程了,至于DNS隧道就不掩饰了,大同小异
如果文中不好复制,可以通过以下 Github 地址获取
https://github.com/Just-Hack-For-Fun/request_monitor