7.9 隧道原理
「隧道与代理」系列 Part 1(免费)。 这一篇先不碰任何具体工具,把"隧道"这件事的底层原理一次讲透:它到底在封装什么、为什么能穿破防火墙、正向和反向到底差在哪、遇到不同场景该怎么选工具。 Part 2(7.10 SSH 三种转发)动手用 SSH 的
-L/-R/-D打隧道;Part 3(7.11 chisel)讲单文件跨平台神器 chisel;后面还会有 ligolo-ng 的网卡级全局代理、frp 的多级跳板,以及 proxychains 怎么把工具的流量"塞进"代理。
一、先打个比方:隧道就是"信封套信封"
想象你要给一个住在大院里的朋友寄信。这个大院门房特别严格,只认一个对外的通信口——所有进出的信件都得交给门房,由门房统一收发。你的朋友本人没有对外的通信地址,外人根本没法直接把信塞给他。
怎么办?你可以这样做:
- 先把写给朋友的真正的信装进一个小信封,封好,写上"大院里的朋友收";
- 再把这个小信封,塞进一个外面的大信封里;
- 大信封上写的收件人,是那个门房(中转站) ——因为只有门房的地址是对外能通的;
- 信寄到门房后,门房拆开大信封,把里面那封小信封转交给真正的朋友;
- 朋友回信时反向再来一遍:小信封塞给门房,门房装进大信封寄回给你。
整个过程里,门外的快递员只看到一个"大信封寄给门房"的正常包裹,他压根不知道里面还夹着一封私信。这就是隧道。
换成网络语言:
- 小信封 = 你真正想传的数据(比如你访问内网某个数据库的请求);
- 大信封 = 一个防火墙允许通过的协议(比如 SSH、HTTPS、HTTP);
- 门房 = 那台你已经控制、且网络能双向可达的中转机器(跳板机);
- 信封套信封这一步 = 封装(Encapsulation) ,把原始数据包整个塞进允许通行的协议载荷里。
一句话:隧道就是在一个协议里封装另一个协议,让数据穿过原本不通的网络。 外面看,它只是一条再正常不过的 SSH 连接或 HTTPS 请求;里面跑的,却是你访问整个内网的流量。
二、为什么非得打隧道?NAT 和防火墙把你卡死了
很多新手第一次打内网靶场都会懵:我明明已经在边界机器上拿到 Shell 了,为什么 nmap 一扫内网、什么都扫不到?为什么我想访问那台数据库,提示"连接超时"?
这不是你技术不行,是网络本身就不让你直连。搞懂下面两道墙,你就明白隧道存在的意义。
2.1 第一道墙:NAT,内网主机根本没有"门牌号"
企业内网里几百台机器,绝大多数用的是 192.168.x.x、10.x.x.x、172.16.x.x 这种私网地址。私网地址的意思是:它只在内网内部有意义,在公网上是不可路由的。所有内网机器要上公网,都得经过一台出口设备做 NAT(网络地址转换) ,把它们的私网地址"伪装"成同一个公网出口地址。
这就带来一个致命后果:你在公网上的攻击机,根本无法主动连到一台私网地址的内网机器——因为公网上没有任何路由能把包送进那个私网段。NAT 是"单向"的:内网机器主动出去可以,外面的人主动进来,没门。
2.2 第二道墙:防火墙只放行"该出去"的流量
就算你想办法摸进了内网一台机器,企业防火墙通常还有一条铁律:只允许内网主动往外连特定的几个端口,最典型的就是:
- 出站 TCP 80(HTTP)、443(HTTPS)放行;
- 出站 SSH(22)有时放行、有时封;
- 出站随便连一个高位端口(比如我想在攻击机开个 4444 反弹 Shell),大概率被防火墙直接掐断;
- 入站方向更是几乎全封。
于是你面对的局面是:
你的攻击机(公网 VPS) ──?──> 内网跳板机(私网,无公网IP) ──> 内网数据库
❌ 连不进去 只能自己往外连 ❌ 你也够不着隧道要解决的,就是这两条"❌"。 它让跳板机"主动"往你的攻击机方向建立一条防火墙允许的连接(比如走 443),然后把你想访问内网的所有请求,都塞进这条已经建立好的连接里运过去。外面的防火墙一看:哦,是内网服务器在正常访问公网的 HTTPS 服务嘛,放行。它不知道这条 HTTPS 连接里跑的其实是你正在对内网数据库进行的 SQL 查询。
看懂这张图,你就抓住了隧道的全部动机:你打不进去,就让它连出来;它连出来的那条路,再反过来帮你"伸进"内网。
这里再补一个新手最容易忽略的细节:出方向端口放行,往往不等于任意高位端口都放行。 很多人以为"防火墙允许出 443",就能随便在跳板机上反弹一个 4444 回来。错。企业的出站策略通常是"目标端口为 80/443 的 TCP 放行",而不是"任意目标端口放行"。所以你的隧道入口端口,最好就贴着 443 来开——把 chisel server 挂在 VPS 的 443、把 SSH 也尽量走 443 转发,让出站流量看起来就是一次正常的 HTTPS 访问。这也是为什么后面讲工具时,反复强调"伪装成 443 上的 HTTPS"不是花架子,而是过防火墙的硬要求。
三、转发(Forwarding)和封装(Encapsulation):别混为一谈
这是新手最容易糊涂的一对概念。网上教程张口就"打个隧道",但其实底层有两种完全不同的玩法,搞混了后面学工具时会一直绕。
3.1 端口转发:只是"搬运",不改包
第一种叫端口转发(Port Forwarding)。它的思路很朴素:在某个地方监听一个端口,谁往这个端口发数据,我就原封不动地把这坨字节搬到另一个地址:端口上去。搬过去之后,再把对方的回复原样搬回来。
注意关键点:它不关心、也不修改里面的内容。它就是一个"数据搬运工",左边收什么,右边发什么。就像你在两家公司之间开了条专线,专线本身不懂传真内容,只是把传真纸原封不动递过去。
[本地监听 :8080] ──字节原样搬运──> [目标 192.168.1.100:80]3.2 隧道封装:把整个包"塞"进新协议
第二种叫隧道封装(Encapsulation / Tunneling)。它更进一步:不是单纯搬运字节流,而是把一个完整的网络数据包(甚至一个完整的 TCP/UDP 连接)整个当成"载荷",塞进一个新协议的肚子里,由新协议负责在网络上传输,到了另一端再把原始包拆出来、重新交给协议栈处理。
最经典的例子就是 SSH 隧道:你访问 localhost:1080 的流量,被 SSH 客户端截下来,封装进 SSH 加密通道里发给跳板机,跳板机上的 SSH 服务端把它拆出来,再由跳板机去真正连接目标。SSH 通道本身跑的是 TCP,但它在里面"装"下了任意 TCP 应用层流量。
两者的数据包结构对比一下,区别一目了然:
3.3 封装和解封装,到底发生在哪两头
这里还有个新手特别容易想错的点:封装和封解,是对称地发生在隧道两端的。发起隧道的那一端(通常是你在本地敲命令的那台)负责把应用流量"抓下来、包进去";隧道的另一端(跳板机)负责"拆包、还原、再转发"。回程流量则把整个过程反过来:跳板机把目标返回的数据重新包进隧道,你这边再拆出来交给应用。
也就是说,隧道本质上是两段"应用↔隧道"的对接,中间夹着一段"隧道↔隧道"的加密通道。你本地的浏览器并不知道中间有隧道,它以为自己就是在跟一个普通的 Web 服务器说话;内网那台数据库也不知道前面站的是你的攻击机,它以为连接是来自跳板机的。整条隧道对两端的应用都是透明的——这正是它好用的地方:你不需要修改任何工具,只要把工具的流量"指"到隧道本地监听的那个端口上就行。
理解这一点,你就明白为什么 proxychains 这类工具能"什么都不改动地让 nmap 走代理":它做的事,就是在你本地这一侧,把工具原本要发出去的连接"截"下来,塞进 SOCKS 隧道口。工具以为自己连上了目标,其实连上的是隧道入口。后面 7.10 讲 -D 和 proxychains 时,你会再见到这套机制。
| 对比项 | 端口转发 Port Forwarding | 隧道封装 Encapsulation |
|---|---|---|
| 做了什么 | 把一个端口的字节流搬到另一个地址 | 把原始数据包整个包进新协议再传输 |
| 内容是否修改 | 不修改,原样搬运 | 增加外层协议头/加密/封装,到端再解封 |
| 能否跑多协议 | 一般只能跑 TCP 流 | 可封装 TCP/UDP/ICMP,甚至整层网络 |
| 典型工具 | nc、SSH -L、chisel 单端口 | SSH 通道、chisel SOCKS、ligolo-ng、DNS 隧道 |
| 伪装能力 | 弱(直接看就是某端口转发) | 强(伪装成 SSH/HTTPS/DNS 流量) |
现实里两者经常叠在一起:SSH 的
-L本质是"在 SSH 这条已经封装好的加密通道上,再做一层端口转发"。所以你听到"打 SSH 隧道"时,它既做了封装(SSH 加密通道),又做了转发(把本地端口搬到目标)。工具细节后面两篇拆开讲。
四、一次 SSH 隧道里,数据到底经历了什么
光说"封装""解封装"还是有点抽象。我们用一张时序图,把一次"你用浏览器访问内网 Web,走 SSH 隧道"的完整过程拆开看。这张图建议多盯两遍,它是理解后面所有工具的地基。
这张图里最该记住的两点:
- 真正发起对内网 Web 服务器连接的,是跳板机(S),不是你的攻击机(U)。 你的攻击机自始至终只跟跳板机说话。这就是为什么跳板机在了你"网络可达"的那一侧——它替你去够那些你够不着的机器。
- U 和 C 之间、S 和 T 之间,都是明文的正常协议;C 和 S 之间那一整段,是被 SSH 加密封装过的。 防火墙/IDS 看到的,只是 C 和 S 之间一条正常的 SSH 连接,它既看不到里面跑的是 HTTP,也看不到你访问的是哪个内网地址。
五、隧道的方向性:正向和反向,先搞清楚谁连谁
学隧道最绕、也最关键的一个概念,就是方向。一句话区分:
- 正向隧道(Local Forward):你的攻击机主动连跳板机,然后借跳板机的手去够它身后的目标。前提是——你能直接连到跳板机。
- 反向隧道(Remote / Reverse Forward):跳板机主动连你的攻击机,然后通过这条已建立的连接,把你的端口流量送回去。用在跳板机你根本连不到、但它能连到你的场景。
把两种方向的数据流画在一起对比:
怎么判断自己该用哪个?问自己一个问题:"我现在能不能直接 SSH(或直接 TCP 连接)到那台跳板机?"
- 能 → 正向。比如你拿到的跳板机本身就是一台有公网 IP 的 Web 服务器,你直接
ssh user@它就行。 - 不能 → 反向。比如跳板机藏在公司内网深处,你的公网 VPS 根本路由不到它,但它能上网、能连到你的 VPS。这时候必须让它主动来连你,再把隧道方向反过来用。
记住一个反直觉的点:"反向"不是说流量反过来跑,而是说"谁主动发起这条连接"反过来了。 连接一旦建立,数据双向都能跑,只是最初那一下握手,是由内网那台机器发起的——因为只有它有能力发起这一下。
实战里还有个高频误区要提前打预防针:方向选错了,命令怎么敲都不通。 很多人拿到一台内网 Windows 跳板,上来就在自己攻击机上敲 ssh -L ... 跳板机,结果一直连不上,还以为是密码错了。其实问题出在——攻击机根本路由不到跳板机,TCP 三次握手都到不了,哪来的密码错误?正确做法是反过来:在跳板机上敲命令,让它去连你的公网 VPS。判断标准永远回到那句"我能不能直接连到它",别凭直觉猜。另外,反向隧道还有个隐藏的坑:默认情况下远程转发只绑在跳板机自己的 127.0.0.1 上,别的机器连不上,得改服务端的 GatewayPorts 配置才能对外监听——这个细节 7.10 会专门讲,这里先埋个伏笔。
六、封装协议深挖:TCP over TCP、UDP over TCP、HTTP 伪装
理解了方向,再往里看一层:隧道把流量封装进底层协议时,会碰到一些很有意思的"兼容性"问题。这部分是区分"会用工具"和"懂原理"的分水岭。
6.1 TCP over TCP:为什么 SSH 隧道有时候会卡
SSH 隧道本质是 TCP over TCP——它把一条 TCP 应用流,塞进另一条 SSH 的 TCP 连接里跑。这里藏着一个著名的性能坑:TCP 重传叠加。
想象一下:内层 TCP(你和内网数据库之间的逻辑连接)和外层 TCP(SSH 加密通道)都有自己的"超时重传"机制。当网络丢包时:
- 外层 TCP(SSH)发现丢包,自己先重传一次;
- 但内层 TCP 也在等它自己的确认,它又会再重传一次;
- 两个重传叠加,就像两个人同时拉一根橡皮筋,互相等对方先动,结果延迟越堆越高、吞吐越跑越低。
这就是为什么在弱网下,SSH 长隧道偶尔会"卡一下又自己好"。它不是 bug,是 TCP 套 TCP 的结构性问题。后面 ligolo-ng 走 TUN 网卡、chisel 做 UDP 时,都会想办法绕开这个坑。
6.2 UDP over TCP:DNS、SNMP 这类怎么跑
很多内网服务是 UDP 的——最典型的是 DNS(53/UDP)、SNMP(161/UDP)。但 SSH 隧道原生只擅长 TCP。怎么办?答案是 UDP over TCP:把每个 UDP 包拆出来、标好边界、塞进 TCP 流里传过去,对端再按边界还原成一个个 UDP 包发出去。
代价是:UDP 本来是"不可靠、不管丢不丢、追求快"的,硬塞进可靠的 TCP 后,就变成了"宁可慢也要保证不丢"。对 DNS 这种小包查询影响不大,但对实时性要求高的场景(比如视频、游戏)就不合适了。chisel 的 /udp 后缀、ligolo-ng 都支持这种封装。
6.3 HTTP 隧道:把流量伪装成"访问网页"
还有一种更隐蔽的玩法——HTTP 隧道。当防火墙严格到只允许 80/443 出站、还会检查"这是不是个正常的 HTTP 请求"时,你就不能直接裸奔 SSH 了。HTTP 隧道把要传输的数据,藏进正常的 HTTP 请求/响应体里:
- client 把数据 POST 到 server 的一个 URL;
- server 处理后,把结果塞进 HTTP 响应 body 里回给 client;
- 外层看,这就是一次次平平无奇的 HTTPS 访问,和员工摸鱼刷网页一模一样。
chisel 默认就走 HTTP/WebSocket,正是这个思路;更极端的还有把流量藏进 DNS 协议查询的 DNS 隧道(iodine、dnscat2),连 80/443 都封死、只允许 DNS 出的时候用。
| 封装方式 | 底层协议 | 能跑什么 | 隐蔽性 | 典型工具 |
|---|---|---|---|---|
| SSH 隧道 | TCP | 任意 TCP | 中(能看到是 SSH 连接) | ssh -L/-R/-D |
| HTTP/WebSocket 隧道 | HTTP/HTTPS | TCP、SOCKS | 较高(像正常网页流量) | chisel |
| UDP over TCP | TCP | UDP(DNS/SNMP) | 中 | chisel /udp、ligolo-ng |
| DNS 隧道 | DNS 报文 | 任意(极慢) | 极高(像正常 DNS 查询) | iodine、dnscat2 |
| ICMP 隧道 | ICMP 回显 | 任意(很慢) | 高(像 ping) | ptunnel |
七、隧道决策树:遇到场景,一分钟选出工具
原理讲完,落到实战。内网渗透里工具很多,新手最怕"不知道该用哪个"。下面这棵决策树,照着问自己几个问题就能选出来。
再配一张工具能力矩阵,横向对比时一眼看清:
| 能力 | SSH (-L/-R/-D) | chisel | ligolo-ng | frp | dnscat2/iodine |
|---|---|---|---|---|---|
| TCP 单端口转发 | ✅ | ✅ | ✅ | ✅ | ❌ |
| SOCKS 代理 | ✅(-D) | ✅(含反向) | ✅ | ✅ | ❌ |
| UDP 支持 | ❌ | ✅(/udp) | ✅ | ✅ | ✅ |
| ICMP / 透明代理 | ❌ | ❌ | ✅(TUN) | ❌ | 部分 |
| 反向连接 | ✅(-R,受 GatewayPorts 限制) | ✅(--reverse 方便) | ✅ | ✅ | ✅ |
| 伪装成 HTTP/HTTPS | ❌ | ✅(默认 WebSocket) | ❌ | ✅ | DNS 伪装 |
| 单文件无依赖 | ❌(需装 SSH) | ✅(Go 静态二进制) | ✅(Go 静态) | ✅ | 部分 |
| 多级跳板 | 手动串联 | 级联 | 级联 | ✅(强项) | ❌ |
| 性能 | 中 | 较高 | 高(内核态转发) | 高 | 很低 |
这张表不用背,收藏起来,实战时照着"我要什么能力"那一列往下挑就行。
八、常见隧道工具全景(先混个脸熟)
最后给一张"全家福",把这一系列后面会讲到的工具先列出来,知道各自的定位,学的时候心里有数:
| 工具 | 一句话定位 | 什么时候第一个想到它 |
|---|---|---|
| SSH | 系统自带的加密管道,最轻量 | 目标有 SSH、只想简单转发几个端口 |
| chisel | Go 写的单文件 TCP/UDP/SOCKS 隧道 | 目标没 SSH、要单文件上传、要反向 SOCKS |
| ligolo-ng | 基于 TUN 网卡的全局透明代理 | 要把整个内网当成本地网络、ICMP 也要通 |
| frp | 本来做内网穿透的多级转发利器 | 两层以上跳板链、要稳定长期挂着 |
| ngrok | 一键把本地端口暴露到公网 | 快速把内网服务临时暴露出来演示 |
| iodine / dnscat2 | DNS 隧道 | 80/443 全封死、只剩 DNS 能出 |
| ptunnel | ICMP/ping 隧道 | 连 DNS 都封、但允许 ping 出站的极端环境 |
九、为什么说隧道是内网渗透的"高速公路"
最后说句掏心窝的。很多人刚接触内网渗透,把大量时间花在"怎么拿到第一台机器的 Shell"上,却忽略了一件事:你在那一台机器上敲命令,视野永远只有那台机器本身。
没有隧道,你的攻击面被死死钉在"跳板机这一个 shell 窗口"里:想扫隔壁数据库?你得在跳板机上手动敲 nmap,输出一坨文字贴回来;想跑浏览器看内网 OA?根本没有图形界面。整个内网对来说,就是个隔着毛玻璃的黑盒。
有了隧道,一切都变了:你把整条内网映射回你自己的攻击机上。你可以在自己熟悉的 Kali 里用 Burp 抓内网 OA 的包、用 nmap 优雅地扫 C 段、用 sqlmap 打内网数据库、用浏览器点点点。隧道一打通,整个内网都落进了你工具的射程之内——你不再是"在跳板机上干活",而是"把整个内网接到了你的工作台上"。
这就是为什么我们说:拿 Shell 只是进场,打隧道才是真正开始打内网。 下一篇,我们就用每个人系统里都自带的 SSH,亲手把这条高速公路的第一段修起来。
最后再强调一个心法:学隧道,别死记命令,要先在脑子里把"谁连谁、监听在哪、目标是谁看的"这三件事想清楚。 命令随时能查,但这三个方向一旦搞反,再简单的工具也用不明白。后面 SSH、chisel、ligolo-ng 无论参数怎么变,你只要追问这三个问题,方向就不会错。这也是这一篇不急于讲具体工具、先把原理铺开的原因——地基打牢,工具只是填砖。
十、本篇小结
- 隧道本质:在一个能通的协议里封装另一个不通的协议,"信封套信封",让数据伪装成允许的流量穿过网络。
- 存在的理由:NAT 让内网主机没有公网 IP(你进不去),防火墙只放行少数端口出站(你出得别扭),隧道解决这两件事。
- 转发 ≠ 封装:转发只搬运端口字节流不改内容;封装把整个数据包塞进新协议,可跑多协议、能伪装。
- 方向是分水岭:能直接连跳板用正向(Local),连不到、只能它连你就用反向(Remote/Reverse)。
- 选型:单端口 SSH -L,多工具代理 SSH -D/chisel socks,全局透明代理 ligolo-ng,多级跳板 frp/chisel。
下一篇动手,看 7.10 SSH 三种转发,把 -L / -R / -D 这三种模式一条一条跑通。