Linux 下的端口转发工具非常多,关于使用方法网上也是非常详尽。因此在本文中,就不再赘述前人的研究成果,主要侧重的是研究不同工具在做端口转发时的特征。
为了方便下面的讨论,首先交代一下场景:有两台主机A(172.17.0.2)和B(172.17.0.3),其中A开放SSH服务(22端口)。现在,基于一些不可描述的原因,B无法直接与A的22端口建立TCP连接,这时B若想通过SSH连接A,必须通过端口转发来实现。
既然是端口转发,那么一定需要一个“中介”,这个中介一方面要能将B的请求发送给A,另一方面也能将A的响应发回给B,否则通信就不能建立起来。这个中介可以是另一台主机,也可以是A自身。下面我们逐个讨论一下:
假设有一台主机C(172.17.0.4),C可以连接A的22端口,B也可以和C建立连接,那么B便可以将C作为跳板。具体操作如下:
通过socat
在C上执行:socat TCP-LISTEN:2222,fork TCP:172.17.0.2:22
此时C会监听2222端口,并且将从2222端口上接收到的数据转发给主机A的22端口。
查看端口转发时C的端口使用情况:
A的端口使用情况:
可以看出C其实用了两个端口,一个端口用于监听和接受来自B的请求,另一个端口用来和A建立连接。
通过ssh本地端口转发
实际上,我们最经常使用的ssh客户端也自带端口转发功能,使用方法如下:
在C上执行:ssh -gfNL 2222:localhost:22 root@172.17.0.2
实际上就是从C上SSH登陆A,只是比普通登陆多了几个选项,执行后,C的端口使用情况:
可以看出ssh客户端做了两件事,一是与A上的sshd建立连接,而是打开并监听2222端口等待连接。B连接C的2222端口后,A上多了两条连接:
可以看出,这种模式中数据的流向是上这样的:B与C的2222端口建立连接,将数据发给C的ssh客户端,C将数据加密后发给A的sshd服务器,A的sshd服务器将数据解密后,再通过一个TCP连接将数据送往目的地。
上面命令所用的几个参数中,需要着重介绍“-L port:host:hostport”这个参数,其中第一个port比较好理解,就是ssh客户端这面需要监听的端口,而“host:hostport”这个有点让人难以理解,为什么是“localhost:22”呢?通过查看ssh的帮助文档,发现这个端口实际是第二次转发的目的端口,所以填“localhost:22”就表示最终要连接的是自己的22端口,如果要填“www.baidu.com:80” 则表示最终要连接是的www.baidu.com的80端口。
通过ssh远程端口转发
上面两种模式都是C主动连接A,但是若C无法主动连接A,而A可以主动连接C,则必须要用ssh远程端口转发:
在A上执行:ssh -gfNR 2222:localhost:22 root@172.17.0.4
即A通过SSH登录C,命令执行后,查看C上端口监听情况:

可见虽然已经成功建立转发渠道,但是只能接受来自本地的连接,要想让C监听所有接口,需要将C的sshd的“GatewayPort”选项设为“yes”。
同样还是B连接C的2222端口,连接后A的端口使用情况为:
可以看出,远程转发和本地转发的区别在于:远程转发是A要求C转发,而本地转发则是C主动转发。
其他工具
除了上面说的几种方法以外,还有许多工具可以做端口转发,如iptables,rinetd,lcx等等,但是由于原理和特征都基本上相同,这里就不再讨论了。
总结一下上面所说的端口转发方式,从发起连接的主体来看可以分成本地转发和远程转发(类似于正向代理和反向代理),从转发的次数来看可以分为一次转发和两次转发两种。每种方法都有各自的特征和适用场景。

上面所说的方法全都需要跳板机才能实现,实际上,如果A对B开放了一部分端口(如2222),那么不经过其他主机也可以达到端口转发的目的。工具和上面所说的一样,仅仅是参数改变了一下而已。
通过socat
在主机A上执行:socat TCP-LISTEN:2222,fork TCP:localhost:22
B连接A的2222端口后,A上端口使用情况:
通过ssh本地转发
在主机A上执行:ssh -gfNL 2222:localhost:22 root@localhost
B连接A的2222端口后,A上端口使用情况:

通过ssh本地转发
在主机A上执行:ssh -gfNL 2222:localhost:22 root@localhost
B连接A的2222端口后,A上端口使用情况:
通过ssh远程转发
在主机A上执行:ssh -gfNR 2222:localhost:22 root@localhost,如果只能监听环回接口,则也需要和上面一样修改sshd配置
通过rinetd转发
编辑/etc/rinetd
添加一行:0.0.0.0 2222 127.0.0.1 22
重启rinetd
通过lcx转发
lcx -m 1 -p1 2222 -h2 127.0.0.1 -p2 22
通过nc转发
mkfifo /tmp/xx; (nc -lkp 2222) </tmp/xx | (nc localhost 22) >/tmp/xx 2>&1; rm –f /tmp/xx
比较一下使用跳板机和不使用跳板机两种方式,可以发现两种并没有本质区别,后者其实是前者的特例。其实只要了解其中的原理,那么自己实现一个端口转发的工具也不是难事。 这篇文章重点关注的是在做端口转发时的命令特征和连接特征。对于内网防护来说,研究端口转发一方面是能知道黑客能用端口转发做什么,另一方面是能够帮助网络管理员及时发现端口转发行为,定位到问题主机。
参考资料:
http://lisux.me/lishuai/?p=439
https://www.ibm.com/developerworks/cn/linux/l-cn-sshforward/index.html
https://my.oschina.net/sbcagf/blog/782952
通过ssh本地转发
在主机A上执行:ssh -gfNL 2222:localhost:22 root@localhost
B连接A的2222端口后,A上端口使用情况:
通过ssh远程转发
在主机A上执行:ssh -gfNR 2222:localhost:22 root@localhost,如果只能监听环回接口,则也需要和上面一样修改sshd配置
通过rinetd转发
编辑/etc/rinetd
添加一行:0.0.0.0 2222 127.0.0.1 22
重启rinetd
通过lcx转发
lcx -m 1 -p1 2222 -h2 127.0.0.1 -p2 22
通过nc转发
mkfifo /tmp/xx; (nc -lkp 2222) </tmp/xx | (nc localhost 22) >/tmp/xx 2>&1; rm –f /tmp/xx
比较一下使用跳板机和不使用跳板机两种方式,可以发现两种并没有本质区别,后者其实是前者的特例。其实只要了解其中的原理,那么自己实现一个端口转发的工具也不是难事。 这篇文章重点关注的是在做端口转发时的命令特征和连接特征。对于内网防护来说,研究端口转发一方面是能知道黑客能用端口转发做什么,另一方面是能够帮助网络管理员及时发现端口转发行为,定位到问题主机。
参考资料:
http://lisux.me/lishuai/?p=439
https://www.ibm.com/developerworks/cn/linux/l-cn-sshforward/index.html
https://my.oschina.net/sbcagf/blog/782952
-