网络攻击的主要内容包括系统安全攻防, 网络安全攻防, 物理攻击与社会工程学三部分: 系统安全攻防主要是利用软件安全漏洞进行攻击, 网络安全攻防利用协议栈的安全漏洞 (不局限于此), 物理攻击与社会工程学攻击主要是利用人的心理弱点, 物理设计缺陷.
1. 概述
网络攻击的主要内容包括系统安全攻防, 网络安全攻防, 物理攻击与社会工程学三部分: 系统安全攻防主要是利用软件安全漏洞进行攻击, 网络安全攻防利用协议栈的安全漏洞 (不局限于此), 物理攻击与社会工程学攻击主要是利用人的心理弱点, 物理设计缺陷.
TCP(Transmission Control Protocol) 传输控制协议是 TCP/IP 协议栈的核心协议, 它位于 IP 协议层之上, 在网络上的两台计算机之间提供可靠的, 有序的通信通道. 许多应用比如浏览器, SSH,Telnet,Email 等使用 TCP 进行通信. TCP 协议处于为应用提供主机到主机通信服务的传输层.
一般我们讲 TCP 提供可靠的有连接服务, 这个可靠包括三层含义:
数据有序传输
丢包重传机制
流量控制机制
2. TCP 协议的工作原理
我们通过一个简单的 TCP client 程序和 TCP Server 程序来展示 TCP 建立连接, 数据传输, 断开连接的过程. 以下这两个程序中, 为了能清晰说明程序的通信过程, 不做容错处理, 力求简单. 工作当中这样的程序是不能正常工作的.
2.1 TCP Client 程序
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define SER_ADDR "127.0.0.1"
- #define SER_PORT 9999
- //#define SER_ADDR "172.16.28.98"
- /* main function */
- int main(int argc, char *argv[])
- {
- /**
- * Step 1: 创建一个 socket, 指定 SOCK_STREAM 参数代表基于 TCP 协议
- * 如果是 UDP 协议, 则需要用 SOCK_DGRAM
- */
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);
- /**
- * Step 2: 设置目标主机 IP 地址和端口号
- * IP+Port, 标识网络上某个主机的通信进程
- */
- struct sockaddr_in dest;
- memset(&dest, 0, sizeof(struct sockaddr_in));
- dest.sin_family = AF_INET;
- dest.sin_addr.s_addr = inet_addr(SER_ADDR);
- dest.sin_port = htons(SER_PORT);
- /**
- * Step 3: 连接服务器
- */
- if (connect(sockfd, (struct sockaddr *)&dest,
- sizeof(struct sockaddr_in)) != 0){
- /* 此处 SYN Flood 攻击会用到 */
- fprintf(stdout, "Error for connect: %s\n", strerror(errno));
- return 1;
- }
- /**
- * Step 4: 向 Server 发送数据
- */
- char *buffer1 = "Hello Server!\n";
- char *buffer2 = "Hello Again!\n";
- write(sockfd, buffer1, strlen(buffer1));
- write(sockfd, buffer2, strlen(buffer2));
- /**
- * Step 5: 关闭连接
- */
- close(sockfd);
- return 0;
- }
一个客户端程序大概如下几个步骤:
1. 创建一个 socket, 通过指定参数 SOCK_STREAM, 来标识基于 TCP 传输, 如果需要用 UDP 协议的话, 则需要指定 SOCK_DGRAM. 特别指出, 如果需要通过原始套接字进行通讯, 需要指定 SOCK_RAW.
2. 指定地址信息, 在网络上, 我们通过 IP 地址和 Port 来标识一个特定主机上的进程, 此处填写 Server 端的 IP 地址和端号.
3. 与 Server 建立连接, TCP 是基于连接下协议, 因此在数据传输之前, 需要通过三次握手建立连接.
4. 收发数据, 一旦连接建立成功, C/S 两端就可以通过 write/send/sendto/sendmsg 发送数据, 可以通过 read/recv/recvfrom/recvmsg / 接收数据
5. 关闭连接, 当数据收发完毕后, 连接不再需要时, 可以通过 close 断开连接.
2.2 TCP Server 程序
下面我们编写一个 TCP Server 程序, 还是老规矩, 力求简单, 不做容错处理.
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #define SER_ADDR 9999
- int main(int argc, char *argv[])
- {
- int sockfd, newsockfd;
- struct sockaddr_in my_addr, client_addr;
- char buffer[100];
- /**
- * Step 1: 创建一个 socket, 指定 SOCK_STREAM 代表 TCP
- */
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- /**
- * Step 2: 绑定一个端口号
- */
- memset(&my_addr, 0, sizeof(struct sockaddr_in));
- my_addr.sin_family = AF_INET;
- my_addr.sin_port = htons(SER_ADDR);
- bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr_in));
- /**
- * Step 3: 监听连接
- */
- listen(sockfd, 5);
- fprintf(stdout, "Serve listenning....\n");
- while(1){
- /**
- * Step 4: Accept 一个连接请求
- */
- socklen_t client_len = sizeof(client_addr);
- newsockfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);
- /**
- * Step 5: 从当前连接读取数据 */
- memset(buffer, 0, sizeof(buffer));
- int len = read(newsockfd, buffer, 100);
- printf("Received %d bytes: %s", len, buffer);
- /**
- * Step 6: 关闭当前链接 */
- close(newsockfd);
- }
- /**
- * Step 7: 关闭套接字
- */
- close(sockfd);
- return 0;
- }
一个 Server 程序的大致步骤为:
1. 创建一个 socket
2. 绑定一个端口号
3. 开始监听
4. 接受一个连接请求.
5. 收发数据
6. 关闭连接请求
7. 关闭 socket
其中 4,5,6 可作为一个循环, 多次响应连接请求.
2. 3 掀开数据传输的面纱
一旦连接建立, OS 分别为 Client 端和 Server 端申请两个 Buffer, 一个是 SendBuffer, 用于发送数据, 一个是 ReceiveBuffer, 用于接收数据. TCP 协议是全双工的, 两端都可以发送和接收数据. 详细流程见下图:
这里面有几个问题, 我们此处并不会对 TCP 协议完整展开解释, 选取和我们后续课程相关重点的描述一下:
1.TCP 提供面向连接的服务, 连接的建立过程在下一章节中, 重点描述.
2. 数据有序传输: 每个数据包编个序号, 数据包到达主机可能错序, 在传输层调整顺序后上传.
3. 丢包重传机制: 引入滑动窗口机制, 窗口内的数据如果没有接到应答 ack, "超时" 进行重传.
4. 流量控制机制: 引入滑动窗口机制后, 接收端实时通知发送端当前自己接受窗口大小, 从而约束发送端的发送, 进行流量控制
2.4 TCP Header
TCP 部分一般称之为 Segment, 数据段, (补充: 在应用层 -- 消息, 传输层 -- 数据段 Segment, 网络层 -- 包 / 分组 packet, 链路层 -- 帧 frame, 物理层 bits), 对于 TCP header 的格式如上图所述, 详细解读如下:
源端口号和目的端口号, 各占 16bits
32 位序号 (seq)
32 位应答序号 (ack)
TCP header 长度: 4 位, 以 4 字节度量, 故 Header 最长为 64 字节
保留: 6 位
标志位: 6 位, 包括 SYN,FIN,ACK,RST,PSH,URG
滑动窗口: 16 位, 可用于流量控制
校验: 16 位
紧急指针: 16 位, 当 URG 标志置位时, 此指针有效, 用于带外数据
选项: 0~320bits, 以 32bits 为单位, TCP 可以通过 options 携带一些补充数据
我们讲, TCP 是面向连接的服务, 因此就存在建立连接, 断开连接等操作. 后续我们选取针对连接建立过程和连接断开过程进行攻击方面的展示.
3. TCP SYN Flood 攻击
3.1 TCP 建立连接 (三次握手)
我们说 TCP 提供面向连接的服务, 因此数据发送前需要先通过三次握手建立连接:
5. 第一次握手: 首先客户端 C(?) 主动发起连接, 发送 SYN(连接请求标志), 以及序号 SEQ=x(序号 x 随机生成) 到服务器端 S.
6. 第二次握手: 服务器端 S 接受到 SYN 后, 向客户端 C 也发送 SYN 及 ACK, 且 ack=x+1, 以及序号 Seq=y(序号 y 随机生成).
7. 第三次握手: 客户端接到 SYN 及 ACK 后, 核查 ack 是否为 x+1, 若正确, 则客户端 C 发送 ACK 且 ack=y+1, 至服务器端 S.
8. 服务器端 S 接收到 ACK, 核查 ack 是否为 y+1. 若正确, 则连接正常建立.
三方握手建立连接的过程详细见下图
3.2 SYN Flooding 攻击
在探讨 SYN 攻击之前, 我们先看看 linux 内核对 SYN 是怎么处理的: 1. Server 接收到 SYN 连接请求. 内部维护一个队列 (我们暂称之半连接队列, 半连接并不准确), 发送 ack 及 syn 给 Client 端, 等待 Client 端的 ack 应答, 接收到则完成三次握手建立连接. 如果接收不到 ack 应答, 则根据延时重传规则继续发送 ack 及 syn 给客户端.
利用上述特点. 我们构造网络包, 源地址随机构建, 意味着当 Server 接到 SYN 包时, 应答 ack 和 syn 时不会得到回应. 在这种情况下, Server 端, 内核就会维持一个很大的队列来管理这些半连接. 当半连接足够多的时候, 就会导致新来的正常连接请求得不到响应, 也就是所谓的 DOS 攻击.
详细见下图所示:
3.3 SYN Flood 攻击防护手段
tcp_max_syn_backlog: 半连接队列长度
tcp_synack_retries: syn+ack 的重传次数
tcp_syncookies : syn dookie
一般的防御措施就是就是减小 SYN+ACK 重传次数, 增加半连接队列长度, 启用 syn cookie. 不过在高强度攻击面前, 调优 tcp_syn_retries 和 tcp_max_syn_backlog 并不能解决根本问题, 更有效的防御手段是激活 tcp_syncookies, 在连接真正创建起来之前, 它并不会立刻给请求分配数据区存储连接状态, 而是通过构建一个带签名的序号来屏蔽伪造请求.
来源: http://network.51cto.com/art/201805/572094.htm