渗透&&探测 (之TCP探测篇)

TCP原理

  互联网由一整套协议构成。TCP 只是其中的一层,有着自己的分工。

  来来来,我们先复习一下TCP/IP五层结构,详细请参考笔者之前分享的网络原理篇

  • 以太网协议(Ethernet
    位于数据链路层,规定了电子信号如何组成数据包(packet),解决了 子网内部的 点对点 通信。

    但是,以太网协议不能解决多个局域网如何互通,这由 IP 协议解决。

  • IP协议
    IP 协议位于 网络层 ,定义了一套自己的 地址规则,称为 IP 地址。它实现了 路由 功能,允许某个局域网的A主机,向另一个局域网的B主机发送消息。

    路由的原理很简单。市场上所有的 路由器,背后都有很多 网口,要接入多根 网线。路由器内部有一张 路由表,规定了AIP地址走出口 B段地址走出口,......通过这套 指路牌,实现了数据包的 转发

    IP 协议只是一个 地址协议,并不保证数据包的 完整。如果路由器 丢包,就需要发现 丢了哪一个包,以及 如何重新发送 这个包。这就要依靠TCP协议。

TCP 报文

  • 源端口和目的端口
    分别占用16bit,表示 源端口号目的端口号 ;用于区别主机中的 不同进程,而 IP地址 是用来 区分不同的主机的源端口号和目的端口号 配合上IP首部中的源IP地址目的IP地址就能唯一的确定一个TCP连接

  • 序号
    Seq 序号,32bit,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。

  • 确认序号
    Ack 序号,32bit,只有ACK标志位为1时,确认序号字段才有效。【确认方Ack=发起方Req+1,两端配对

  • 数据偏移
    给出首部中32bit的数目,需要这个值是因为任选字段的长度是可变的。这个字段占4bit(最多能表示1532bit的的字,即4*15=60个字节的首部长度),因此TCP最多有60字节的首部。然而,没有任选字段,正常的长度是20字节;

  • 标志位
    6个,即URGACKPSHRSTSYNFIN等,具体含义如下:

    1. URG:紧急指针(urgent pointer)有效
    2. ACK:确认序号有效
    3. PSH接收方 应该尽快将这个报文交给 应用层
    4. RST:重置连接
    5. SYN:发起一个 新连接
    6. FIN释放一个连接
  • 窗口
    窗口大小,也就是有名的滑动窗口,用来进行流量控制

TCP 数据包的大小

  简单说,TCP 协议的作用是,保证数据通信的 完整性可靠性防止丢包以太网 数据包(packet)的大小是固定的,最初是1518字节,后来增加到1522字节。其中, 1500 字节是负载(payload),22字节是 头信息head)。

  IP 数据包在以太网数据包的负载里面,它也有自己的 头信息,最少需要20字节,所以IP数据包的负载最多为1480(1500-20)字节。

  TCP 数据包在 IP 数据包的负载里面。它的 头信息 最少也需要20字节,因此TCP数据包的最大负载是 1480 - 20 = 1460 字节。由于 IPTCP 协议往往有 额外的头信息,所以TCP 负载实际为1400字节左右。

  因此,一条1500字节的信息需要 两个 TCP 数据包。HTTP/2 协议的一大改进, 就是压缩 HTTP 协议的头信息,使得一个 HTTP 请求可以放在 一个 TCP 数据包里面,而不是分成多个,这样就提高了速度。

TCP 数据包的编号(SEQ

  一个包1400字节,那么一次性发送 大量数据,就必须分成多个包。比如,一个 10MB 的文件,需要发送7100多个包。

  发送的时候,TCP 协议为每个包 编号sequence number,简称 SEQ),以便接收的一方 按照顺序还原。万一发生 丢包,也可以知道丢失的是哪一个包。

  第一个包 的编号是一个 随机数。为了便于理解,这里就把它称为 1 号包。假定这个包的负载长度是 100 字节,那么可以推算出下一个包的编号应该是 101。这就是说,每个数据包都可以得到 两个 编号:自身的编号,以及 下一个包 的编号。接收方由此知道,应该按照什么 顺序 将它们 还原 成原始文件。

ps :当前包的编号是45943,下一个数据包的编号是46183,由此可知,这个包的负载是240字节。)

TCP 数据包的组装

  收到TCP数据包以后,组装还原 是操作系统内核完成的。应用程序 不会 直接处理TCP 数据包。

  对于 应用程序 来说,不用关心 数据通信 的细节。除非线路异常,收到的总是完整的数据。应用程序需要的数据放在TCP数据包里面,有自己的 格式(比如HTTP协议)。

  TCP 并没有提供任何 机制,表示 原始文件 的大小,这由 应用层 的协议来规定。比如,HTTP 协议就有一个头信息 Content-Length,表示 信息体 的大小。对于 操作系统 来说,就是持续地 接收 TCP 数据包,将它们按照顺序 组装好,一个包都不少。

  操作系统 不会去处理 TCP 数据包里面的数据。一旦组装好 TCP 数据包,就把它们转交给 应用程序TCP 数据包里面有一个 端口port)参数,就是用来指定转交给 监听该端口 的应用程序。

(PS:系统根据 TCP 数据包里面的端口,将组装好的数据转交给相应的应用程序。上图中,21端口是 FTP 服务器,25端口是 SMTP 服务,80端口是 Web 服务器。)

  应用程序 收到组装好的 原始数据,以 浏览器 为例,就会根据 HTTP 协议的Content-Length字段正确读出一段段的数据。这也意味着,一次 TCP 通信可以包括 多个 HTTP 通信。

TCP 三次握手

  1. 第一次握手
    建立连接, 客户端 发送连接 请求报文段,将SYN位置为1Sequence Numberx
    然后,客户端进入SYN_SEND状态,等待 服务器 的确认;

  2. 第二次握手
    服务器 收到SYN报文段。服务器 收到 客户端SYN报文段,需要对这个SYN报文段进行 确认,设置 Acknowledgment Numberx+1(Sequence Number+1);
    同时,自己自己还要发送SYN请求信息,将SYN位置为1Sequence Numbery
    ** 服务器端** 将上述所有信息放到一个 报文段(即SYN+ACK报文段)中,一并发送给 客户端,此时服务器进入SYN_RECV状态;

  3. 第三次握手
    客户端 收到 服务器SYN+ACK报文段。然后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端 都进入ESTABLISHED状态,完成TCP 三次握手

TCP四次分手

客户端服务器 通过 三次握手 建立了 TCP 连接以后,当数据 传送完毕,肯定是要 断开TCP连接 的啊。那对于TCP的断开连接,这里就有了神秘的 四次分手。(继续参考上图)

  1. 第一次分手
    主机A(可以使客户端,也可以是服务器端),设置Sequence NumberAcknowledgment Number,向 主机B 发送一个FIN报文段;
    此时,主机A 进入FIN_WAIT_1状态;
    这表示主机1没有数据要发送给主机2了;

  2. 第二次分手 主机B 收到了 主机A 发送的FIN报文段,向 主机A 回一个ACK报文段,Acknowledgment NumberSequence Number加1;
    主机A 进入FIN_WAIT_2状态;
    主机B告诉主机A,我同意你的关闭请求

  3. 第三次分手
    主机B主机A 发送FIN报文段,请求关闭连接,同时 主机B 进入 LAST_ACK 状态;

  4. 第四次分手
    主机A 收到 主机B 发送的FIN报文段,向 主机B 发送ACK报文段,然后主机A进入TIME_WAIT状态;
    主机B 收到 主机AACK报文段以后,就 关闭连接
    此时,主机A 等待2MSL后依然没有收到回复,则证明 Server端 已正常关闭,那好,主机A 也可以关闭连接了。

至此,TCP的四次分手就这么愉快的完成了。

为什么

  • 为什么要三次握手 在谢希仁著《计算机网络》第四版中讲 三次握手 的目的是 为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误 。在另一部经典的《计算机网络》一书中讲 三次握手 的目的是为了解决 网络中存在延迟的重复分组 的问题。

    在谢希仁著《计算机网络》书中同时举了一个例子,如下:

    “已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”

    这就很明白了,防止了服务器端的一直等待而浪费资源。

  • 为什么要四次分手
    那四次分手又是为何呢
    TCP 协议是一种面向 连接的可靠的基于字节流 的运输层通信协议。
    TCP全双工 模式,这就意味着,当主机A 发出 FIN 报文段时,只是表示 主机A 已经没有数据要发送了,主机A 告诉 主机B,它的数据已经 全部发送完毕了
    但是,这个时候 主机A 还是可以 接受 来自 主机B 的数据;
    主机B 返回ACK报文段时,表示它已经知道 主机A 没有数据发送了,但是 主机B 还是可以 发送 数据到 主机A 的;
    主机B 也发送了FIN报文段时,这个时候就表示主机B也没有数据要 发送了,就会告诉 主机B,我也 没有 数据要发送了。
    之后彼此就会愉快的 中断 这次TCP连接。

    1. FIN_WAIT_1
      这个状态要好好解释一下,其实FIN_WAIT_1FIN_WAIT_2状态的真正含义都是表示 等待对方的FIN报文
      而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKETESTABLISHED状态时,它想 主动 关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。
      而当 对方 回应ACK报文后,则进入到FIN_WAIT_2状态。
      当然在实际的正常情况下,无论对方何种情况下,都应该 马上 回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

    2. FIN_WAIT_2
      上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示 半连接,也即 有一方 要求 close 连接,但另外还告诉对方,我暂时还有点数据需要传送给你(ACK信息),稍后再关闭连接。

    3. CLOSE_WAIT
      这种状态的含义其实是表示在 等待关闭怎么理解呢
      当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果 没有 的话,那么你也就可以 close这个SOCKET,发送FIN报文给对方,也即 关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是 等待你去关闭连接。

    4. LAST_ACK
      这个状态还是比较容易好理解的,它是 被动关闭一方 在发送FIN报文后,最后等待对方的ACK报文。
      当收到ACK报文后,也即可以进入到CLOSED可用状态了。

    5. TIME_WAIT
      表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FINWAIT1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

    6. CLOSED 表示连接中断。

慢启动和 ACK

  服务器 发送数据包,当然越快越好,最好 一次性 全发出去。但是,发得 太快,就有可能 丢包带宽小路由器过热缓存溢出 等许多因素都会导致 丢包线路不好的话,发得越快,丢得越多。

  最 理想 的状态是,在线路 允许 的情况下,达到 最高速率。但是我们怎么知道,对方线路的 理想速率 是多少呢?答案就是 慢慢试

  TCP 协议为了做到 效率可靠性 的统一,设计了一个 慢启动slow start)机制。开始 的时候,发送得 较慢,然后根据 丢包 的情况,调整速率;如果 不丢包,就 加快 发送速度;如果 丢包,就 降低 发送速度。

  Linux 内核里面设定了(常量TCP_INIT_CWND),刚开始通信的时候,发送方一次性发送10个数据包,即 发送窗口 的大小为10。然后停下来,等待 接收方的确认,再 继续 发送。

  默认情况下,接收方每收到 两个 TCP 数据包,就要发送 一个 确认消息。确认 的英语是 acknowledgement,所以这个确认消息就简称 ACK

ACK 携带 两个 信息。

  1. 期待要收到下一个数据包的编号
  2. 接收方的接收窗口的剩余容量

  发送方有了这 两个 信息,再加上自己 已经发出的数据包的最新编号,就会推测出 接收方大概的接收速度,从而 降低或增加 发送速率。这被称为 发送窗口,这个窗口的大小是 可变的

(PS:每个 ACK 都带有下一个数据包的编号,以及接收窗口的剩余容量。双方都会发送 ACK。)

  注意,由于TCP通信是 双向 的,所以 双方 都需要发送 ACK。两方的 窗口 大小,很可能是 不一样 的。而且 ACK 只是很简单的几个字段,通常与 数据 合并在一个 数据包 里面发送。

(PS:上图一共 4 次通信。第一次 通信,A 主机发给B 主机的数据包编号是1,长度是100字节。因此 第二次 通信B主机的 ACK 编号是 1 + 100 = 101第三次通信 A 主机的数据包编号也是 101。同理,第二次 通信 B 主机发给 A 主机的数据包编号是1,长度是200字节,因此 第三次通信 A 主机的 ACK201,第四次通信 B 主机的数据包编号也是201。)

  即使对于带宽 很大、线路很好 的连接,TCP 也总是从10个数据包开始慢慢试,过了一段时间以后,才达到 最高的传输速率。这就是 TCP慢启动。

数据包的遗失处理

  TCP 协议可以保证 数据通信完整性,这是 怎么做到的

  前面说过,每一个数据包都带有 下一个数据包的编号。如果下一个数据包 没有收到,那么 ACK编号 就不会发生变化。

  EG,现在收到了4号包,但是没有收到5号包。ACK 就会记录,期待收到5号包。过了一段时间,5号包收到了,那么下一轮 ACK 会更新编号。如果5号包还是没收到,但是收到了6号包或7号包,那么 ACK 里面的编号不会变化,总是显示5号包。这会导致 大量重复内容ACK

  如果 发送方 发现收到 三个 连续的重复 ACK,或者 超时 了还 没有 收到任何 ACK,就会确认 丢包,即5号包遗失了,从而 再次发送 这个包。通过这种机制,TCP 保证了 不会 有数据包丢失。

(PS:Host B 没有收到100号数据包,会连续发出相同的 ACK,触发 Host A 重发100号数据包。)

TCP 探测

  明白了TCP原理,我们就可以对TCP监听的端口进行端口探测。

  其实很简单,我们构造一个第一次握手的SYN包给探测主机,如果探测主机回复我们一个SYN+ACK的回复包,我们就可以判定 此端口 为目标主机开放端口:

  1. 通过之前我们完成的ping探测主机脚本,判断目标主机是否可达

  2. 构造一系列待探测端口的SYN TCP网络数据包

TCP(dport=(int(lport),int(hport)),flags=2)
复制代码

解释一下:

lport: 起始探测端口

hport: 截止探测端口

flags:2表示SYN包,来源于SYN位置1,等于2

  1. TCP包封装到IP包中

  2. 通过sr函数将这一系列包全部发出去,并将结果保存在一个List

  3. 遍历返回的结果,如果结果中TCP包是SYN+ACK就表明 该端口开放

  连串起来:

  扫描结果:

  发现我们的虚拟主机对外开放了22端口,那么我们就可以想点歪心思啦

  关注笔者公众账号[mindev],并回复tcp,就能得到tcp扫描源码哟~~

  愿意与大家分享交流各种技术,个人公众账号[mindev],以及 知识星球[ 极客世界 ]

           欢迎订阅公众账号,日更哟~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值