“ 本文主要研究在Windows平台下的 ICMP 隧道的简单开发和部署使用。”
本文适合了解ICMP隧道基础的安全编程爱好者,可以通过百度、谷歌或在微信公众号中搜索“ICMP隧道”了解该内容。
01
—
ICMP协议简介
ICMP协议是TCP/IP模型中的网络层协议,既不属于TCP协议,也不属于UDP协议,没有端口号的概念,通常用于探测网络是否通畅。Ping 命令就是 ICMP协议实现的一个案例。
02
—
ICMP隧道简介
红队攻击者通常利用ICMP数据包传输隐蔽信息,比如建立C2信道,俗称ICMP隧道,该隧道比较隐蔽。
03
—
ICMP隧道优缺点
防火墙对 ICMP_ECHO 数据包通常是放行的,并且一般情况下,内部主机不会检查 ICMP 数据包所携带的数据内容,所以隐蔽性比较高;
ICMP 隧道是无连接的,和 UDP 一样很不稳定,而且隐蔽隧道的带宽很低;
04
—
ICMP隧道项目
下面介绍一个简单的ICMP隧道开发项目,可用于上传或下载文件。
项目链接:https://github.com/bigbang95/icmp_tunnel。
项目使用C++语言编写,封装了CicmpClientSocket、CicmpServerSocket 等数据传输类,操作简单易懂。
可直接使用 VS2019 编译器 Release x64 方式编译即可。
05
—
Windows 下 ICMP 隧道服务端系统配置
Windows 防火墙新建自定义ICMP入站规则
【实验环境:Windows 10 - 20H2】
1. 点击新建规则
2. 设置规则类型
3. 设置程序(一定选“特定程序”,不能选“所有程序”,否则ICMP隧道会因为出问题,而不能使用)
4. 设置“协议和端口”
5. 设置“作用域”
6. 设置“操作“
7. 设置“配置文件”
8. 设置“名称”
9. 到此,已成功新建并启用了自定义ICMP入站规则。
06
—
ICMP隧道部署
一、服务端(VPS)设置,通常是控制端
启用 Windows 防火墙;
禁用入站规则:文件和打印机共享(回显请求 - ICMPv4-In);
新建并启用“ICMP自定义”规则(注意:配置为”指定程序“,而非”所有程序“);
二、客户端设置,通常是被控端
无需特殊设置;
三、运行程序
在服务端(VPS)Windows 中,管理员权限运行 server.exe;
在客户端(局域网)Windows 中,直接普通用户权限运行 client.exe;
四、开始测试ICMP隧道
07
—
注意事项
服务端程序必须以 “嗅探” 的方式,绑定指定网卡;
bind IP 地址不能是 0.0.0.0,必须是具体的 IP 地址,如:192.168.204.128;
如果 bind IP 地址是 0.0.0.0,WSAIoctl 函数 返回错误代码:10022,即 Invalid argument;
使用 Windows 作为 服务端 的系统时,(注意:是设置 “入站” 规则)
Windows 防火墙应 新建并启用 “自定义 ICMP 入站规则”,方法见上面的 “05 Windows 下 ICMP 隧道服务端系统配置”,而不应使用 规则 “文件和打印机共享(回显请求 - ICMPv4-In)”,此规则应该被禁用;
位于服务端的 Windows 系统,设置防火墙时,需要 重点 注意:
1. 关闭 Windows 防火墙,会使 系统 自动应答 ping 命令 请求包;
2. 启用 Windows 防火墙,但使用 “文件和打印机共享(回显请求 - ICMPv4-In)” 或 “自定义 ICMP 规则 配置为 所有程序”,均会使 系统 自动应答 ping 命令 请求包;
解决方案:
启用 Windows 防火墙,新建并设置 “自定义 ICMP 规则” 并配置为 “特定程序(如:c:\path\server.exe)”
Windows(ICMP 隧道)服务端程序,需要使用原始套接字,以混杂模式,接收 原始数据包(IP 协议数据包),然后寻找指定 ICMP 请求包(包含命令结果信息),并返回 响应包(包含命令指令信息);
注意:需要管理员权限;
Windows(ICMP 隧道)服务端程序,必须 设置 IP_HDRINCL 标志,即 用户 自己 填充 IP 头;否则,程序将自动填充 IP 头信息,其中指定的 协议 不是 icmp,因而导致 响应包 是畸形 icmp 响应包,造成错误;
SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
该行代码执行后,若程序没有管理员权限,则程序返回错误代码:10013,即 Permission denied)
ICMP 隧道中 设置 ICMP 响应包 时,应注意:
ICMP Head 部分 仅仅修改 type、checksum,将它们 都设置为 0,然后 重新计算 checksum 即可;
ICMP Data 部分 请求内容 和 响应内容 完全一样时,Wireshark 抓的 icmp 请求包与响应包 显示为匹配,无警告;
否则,Wireshark 抓的 icmp 请求包与响应包 显示为不匹配,有警告,此时 ICMP 隧道容易被发觉;
以太网中, MTU(最大传输单元) 设置为 1500;
所以,ICMP Data 部分 最大长度为:
MTU(1500字节) - IP 首部长度(20字节)- ICMP首部长度(8字节) = 1472 字节
08
—
常见错误 及 解决方法
VPS 系统为 Windows Server 2016,当前用户为 Administrator,使用本文方法设置 Windows 防火墙,ICMP 隧道 服务端程序 仍然无法接收 ICMP 请求包?
客户端发送 icmp 请求包后,服务端发送 icmp 响应包,但 WireShark 过滤 icmp 数据包后,只发现了 icmp 请求包,未发现 icmp 响应包(当前未设置 IP_HDRINCL 标志,即 不需要 自己填充 IP 头)
原因:考虑 sendto 发送 响应数据包,未返回错误,猜想 响应数据包 发送成功,但该响应包不具备 icmp 协议形式,所以 Wireshark 抓不到 icmp 响应包;
解决方法:既然响应包发送成功,不是 icmp 协议形式,有可能是其他协议形式,再次使用 Wireshark 过滤协议,这次过滤表达式为:ip.addr==120.229.78.16 && (!tcp && !tls),结果 发现 IPv4 畸形包正好对应 发送成功的 响应包;
09
—
icmp 校验和 算法 (经过验证)
USHORT CicmpSocket::CheckSum(const char* buf, size_t size)
{
size_t i;
ULONGLONG sum = 0;
for (i = 0; i < size; i += 2) {
sum += *(USHORT*)buf;
buf += 2;
}
if (size - i > 0) {
sum += *(BYTE*)buf;
}
while ((sum >> 16) != 0) {
sum = (sum & 0xffff) + (sum >> 16);
}
return (USHORT)~sum;
}
10
—
解决 从 ICMP 服务端 发送(68MB - 198 MB)大文件
到 ICMP 客户端 失败的 bug
原因:很可能是 icmp 请求包 Data 部分 数据长度 小于 32 字节,NAT 路由器设备丢弃了 icmp 响应包造成的;
解决办法:使 icmp 请求包 Data 部分 数据长度 大于等于 32 字节即可;(github 项目 https://github.com/bigbang95/icmp_tunnel 仅需如下更改即可)
// 修改文件 icmp_tunnel\icmp\icmpSocket.h
struct ICMP_DATA_HEAD
{
UINT seq;
USHORT flags;
char sz[26]; // 仅添加 此行代码 即可
};
11
—
参考链接
Python3实现ICMP远控后门(中)之“嗅探”黑科技-腾讯云开发者社区-腾讯云 (tencent.com) ——(https://cloud.tencent.com/developer/article/1152186)
GitHub - bigbang95/icmp_tunnel: 一个简单的 稳定 ICMP 传输 示例,在公网中传输可能有点慢,但很稳定,适合初学者——(https://github.com/bigbang95/icmp_tunnel)
12
—
声明
本文所述方法,仅供安全研究使用。凡擅自用于违法用途,将被追究法律责任。其违法行为均与本人无关。特此声明!
原文链接: