☆ rsh的网络通信细节 由tt调试rsh服务、查看相关源码,并解释了为什么rshd会检查源端口。scz负责前期 协议分析、后期测试代码编写。 rsh服务侦听514/TCP口,而514/UDP口对应syslog服务,与echo、chargen、daytime 等服务不同,这次514/TCP、514/UDP不属于同一服务。下面的讨论基于已经了解rsh 应用这个假设,不熟悉了可以查看rsh、rshd、rhosts、hosts.equiv手册页。 rsh服务是否有RFC对应呢,反正我是没有Google出来,在几本bible上也未找到更多 细节。在*nix上"man rshd"可以看到一些协议细节。如果有相关源码,最好看源码, man手册大多未能同步更新。 client建立到server的514/TCP的连接。 服务端会先检查TCP连接的源端口是否位于[512,1023]区间,否则服务端进程终止。 *nix最早要求范围是[1,1023],后来为消除一些安全隐患([1]),改成[512,1023]。 但这是实现相关的,并且各个系统的man手册可能与其当前实现不同步,某些版本 Solaris的man手册就有问题,应实测。 为什么rshd有这个限制? 假设B机上的root信任A机上的scz,并未信任A机上的tt,同时rshd对源端口没有限制。 tt登录A机,自己写了一个程序向B机的514/TCP发送"\0scz\0root\0id\0",尽管此时 源端口必将大于等于1024,命令仍将成功地远程执行。反之rshd对源端口有限制,则 tt必须设法使自己所写程序以root身份执行,才有可能指定小于1024的源端口。如果 tt就是root,本就无所谓安全隐患。如果tt不是root,就阻止了tt伪装成scz。 一般rsh、rcp、rlogin被设置成setuid-to-root,tt要想访问远端rshd,只能用系统 自带的rsh、rcp、rlogin,但这三个程序会如实指定client_user字段,不存在伪造 的可能。 *nix历史上多是大型主机带多个哑终端,做这样的安全防范可以理解。 假设client位于NAT后面,请求报文到达server时源端口很可能已经大于等于1024, 由于对源端口的检查,rsh不会成功。要是对协议细节不熟,就无法理解为什么会失 败。 实测中发现,某些NAT实现可能对rsh、rlogin通信过程做了特殊处理,报文经过NAT 后源端口仍将位于[512,1023]区间。 client向server发送如下请求数据: [port]\0\0\0\0 port ANSI字符串形式的端口号,不是短整型形式的端口号。 client_user 客户端当前用户名 server_user 试图远程使用的服务端用户名 command 试图远程使用的服务端命令 由于是TCP通信,客户端实现时未必一次性发送如上数据,有可能分成几次(比如四次 )发送,效果一致。 port本身是可选项。如果指定了port,客户端必须事先主动侦听在port上,服务端会 建立到客户端这个端口(port)的TCP连接,同时rshd会将stderr重定向到这条连接上。 如果不指定port,客户端就不必事先主动侦听在port上,服务端也不会回连,rshd将 stderr与stdout、stdin一并重定向到514/TCP口对应的那条TCP连接(即当前连接)上。 如果客户端提供了port字段,*nix的服务端要求port在[1,1023]上,并且能成功建立 到客户端的TCP连接,否则服务端进程终止。关于port的范围限制是实现相关的,各 个系统的man手册可能与其当前实现不同步,某些版本Linux的man手册就有问题,应 实测。服务端回连客户端时源端口也将在[512,1023]区间。 对port字段的限制其实道理同前,本质上还是阻止tt伪装成scz。不过这个限制实在 没必要,甚至没必要提供port字段。 提供正确的port字段后,server会回连client。一方面client必须事先侦听在port上 等待server的回连,这将多消耗一个套接字。另一方面如果NAT、FW等因素干挠了回 连过程,rsh将失败。FW一般需要特殊对待rsh通信,就像特殊对待FTP通信一样。 有人要问了,既然port是可选项,rsh命令本身一定会提供port字段吗?我们测试了 Solaris、Linux自带rsh命令,抓包表明它们都提供port字段,并且没有命令行开关 改变这个行为,意味着在这些系统上执行rsh很容易受NAT、FW干挠而失败。这些系统 上的rcp、rlogin不会提供这个变态的port字段,相比rsh就少了很多被干挠的可能。 需要说明的是,rcp实际上是rsh的特例,后面会详细解释。rcp为什么不提供port字 段,rsh为什么非要提供port字段?苍天啊、大地啊,哪位仙女能告诉我这是为什么 啊为什么。rlogin服务侦听513/TCP口,其上的协议与514/TCP口有些类似,但不相同。 rlogin服务根本不支持port字段,如果强行提供,rlogin服务会认为请求数据不合法 而终止进程。 Aix的rsh提供了一个命令行开关"-a",可以禁止客户端提供port字段: $ rsh -l scz -a nonexistcmd bash: nonexistcmd: command not found 用Ethereal抓包可以看到上述错误信息是直接由514/TCP回送的。当不指定"-a"时, 客户端将提供port字段,上述错误信息经由回连的TCP连接传输。至此,我实在想不 明白提供port字段的现实意义何在。 Aix的rshd会对客户端IP进行反向域名解析,解析不成功的话向客户端返回错误信息 并终止服务进程。解决方案有两种,一是在/etc/hosts中为客户端IP增加相应条目, 二是修改/etc/inetd.conf中rshd的命令行,增加命令行开关"-c",取消反向域名解 析。 client_user、server_user最长16个字符。command可以长些,但也有限制。缓冲区 溢出就不要想了,说不定历史上有过。 server向client发送如下响应数据: ret等于0x00表示成功,data对应执行结果,一般是\n分隔、结尾的文本。 ret等于0x01表示失败,data对应错误信息,一般也是\n分隔、结尾的文本。 看了一下rsh.c的实现,主连接(514/TCP)的源端口从1023开始递减选取,直至512, 哪个bind成功就用哪个,所以一般源端口会是1023/TCP。主连接源端口确定后,继续 递减选取stderr所用端口,直至512,哪个bind成功就用哪个,因此一般stderr所用 端口会是1022/TCP,在rsh请求报文中的port字段一般就是1022。