NTP 放大攻击,原理与实践

昨天在微博上看到乌云知识库分享了一篇关于 NTP 放大攻击的文章,点进去看了一下,发现内容很有意思,漏洞本身利用的原理很简单,实现的难度也并不高,但是产生的效果却十分明显,于是决定按照文章中的方式简单复现一下攻击。

本文大部分内容来自 《NTP Amplification Discovery》 原文链接

首先在进行介绍之前,需要你具有基本的背景知识,比如什么是 DDos 攻击。

NTP 放大攻击所利用的协议是 NTP 协议,全称是 Network Time Protocol,网络时间协议。是用于在互联网中对不同主机进行时间同步的协议。其中运行某些版本NTP 的服务器默认开启了对 MONLIST 命令的支持,这条命令的作用是向请求者返回最近通过 NTP 协议与本服务器进行通信的 IP 地址列表,最多支持返回600条记录。也就是说,如果一台 NTP 服务器有超过600个 IP 地址使用过它提供的 NTP 服务,那么通过一次 MONLIST 请求,你会收到返回的包含600条记录的数据包。这应该远远大于你发送查询命令的数据包大小。

那么,都有谁可以发起 MONLIST 查询请求呢?与 DNS 协议类似,NTP 协议也是基于 UDP 实现的,并且无法识别伪造了来源的请求数据包,使得任何人都可以伪装成需要攻击的目标主机对 NTP 服务器发起查询请求,支持 MONLIST 命令的 NTP 服务器在收到攻击者的数据包后,会将结果通过 UDP 的方式发送到攻击者指定的 IP 地址,造成流量攻击。而 NTP 放大攻击的可怕之处在于,它能够将攻击流量上百倍的放大,在最理想的情况下,可以将攻击的流量放大206倍(来源:https://blog.cloudflare.com/understanding-and-mitigating-ntp-based-ddos-attacks/)。在我后面的测试中,放大的效果也很轻松的达到了100倍以上。

介绍完相关背景,下面开始记录一次简单的模拟攻击流程。

首先第一步是发现网络上开启了 NTP 协议的主机,NTP 协议使用的端口是123端口。所以可以通过扫描123端口来获得一份开启 NTP 的主机列表。使用 Masscan 工具可以快递的完成大量的端口扫描任务。

./masscan -pU:123 -oX ntp_alive.xml --rate 160000 100.0.0.0-220.0.0.0 --exclude 255.255.255.255

其中,–exclude 255.255.255.255 是在扫描量较大的时候要求强制使用的,否则无法发起扫描,会得到如下的错误提示。

FAIL: range too big, need confirmation
 [hint] to prevent acccidents, at least one --exclude must be specified
 [hint] use "--exclude 255.255.255.255" as a simple confirmatio

在进行测试的时候,不需要使用100.0.0.0-220.0.0.0这样巨大的范围,因为这样的话,你的网络流量将会变成下面这样。

masscan_traffic

在 25Mbps 的带宽下,这个扫描请求跑了近三个半小时才完成。绿色部分代表出口流量,蓝色部分代表返回流量。即便是当发出和收到的数据包大小相等的时候,在线的 NTP 主机占比也不到10%,实际上考虑到 NTP 的放大效应,粗略地估计最终捕获到的主机数量所占扫描总量的比例应该在0.1%以内。

我一共对120*2^24=2013265920台主机的123端口进行了扫描,其中包括了一些非公网的 IP 地址,大约是16777216+1048576+65536=17891328。所以一共是1995374592+1(127.0.0.1)个IP。最终得到的扫描结果去重后是263493,大概占到了0.013%。

下一步是删选出可以接受 MONLIST 命令的主机,使用开头链接里博客给出的代码

from scapy.all import *
import thread
#Raw packet data used to request Monlist from NTP server
rawData = "\x17\x00\x03\x2a" + "\x00" * 61
#File containing all IP addresses with NTP port open.
logfile = open('uniq_ntp.txt', 'r')
#Output file used to store all monlist enabled servers
outputFile = open('monlistServers.txt', 'a')
def sniffer():
    #Sniffs incomming network traffic on UDP port 48769, all packets meeting thease requirements run through the analyser function.
    sniffedPacket = sniff(filter="udp port 48769 and dst net your_server_ip", store=0, prn=analyser)

def analyser(packet):
    #If the server responds to the GET_MONLIST command.
    if len(packet) > 200:
        if packet.haslayer(IP):
            print packet.getlayer(IP).src
            #Outputs the IP address to a log file.
            outputFile.write(packet.getlayer(IP).src + '\n')

thread.start_new_thread(sniffer, ())

for address in logfile:
    #Creates a UDP packet with NTP port 123 as the destination and the MON_GETLIST payload.
    send(IP(dst=address)/UDP(sport=48769, dport=123)/Raw(load=rawData))
print 'End'

需要将代码部分中 your_server_ip 替换成进行测试的主机 IP,这样才能接受到返回的 MONLIST 数据包并对主机是否可用进行判断。最终这263493主机中,筛选出了5121台主机。比 Cloudfare 遭受攻击时使用的4529台服务器还要多。

其实在这里是可以进行优化的,因为每台 NTP 主机所在的网络环境和繁忙程度不同,可能一部分 NTP 主机只有为数不多的主机与其进行通信,这样他返回的数据包的大小并没有比请求的数据包高出太多,会拉低整体的放大效果。因此可以稍微修改 analyser 函数,将放大倍数较小的主机抛弃,或者依据返回的大小安排不同的权重,从而达到在有限带宽下获得最大放大倍数的效果。

接下来我们已经有了一份可以利用的 IP 列表并进行测试了。这里我拿自己的 VPS 进行了简单的测试,测试的方法和数据可能不是十分准确,但是应该足以说明这种攻击方式的实现难度和效果了。

iptraf

通过图中的数字可以看到 48769测试端口收到的流量是102050K,发出的流量是1253082,简单的计算后81.44倍的放大效果。实际上放大倍数比这个值要大,因为数据包发出后服务器收到再返回会有一定的延迟,所以实际最后收到的流量应该大于当前实时看到的数字。在我上午进行的测试中,最后的放大倍数在110左右。在没经过任何优化的情况下取得这样的效果,还是很可怕的。具体的攻击测试脚本就不放出来了。以免造成了不希望看到的结果,如果需要测试的话可以在前面的扫描脚本上进行简单的修改就能够得到一个威力强大的攻击工具。如果部署在一定数量的服务器上同时运行,会产生很可观的攻击流量。

相关参考:

https://blog.cloudflare.com/understanding-and-mitigating-ntp-based-ddos-attacks/

https://blog.cloudflare.com/technical-details-behind-a-400gbps-ntp-amplification-ddos-attack/

 

Ubuntu下Shadowsocks服务器搭建与各平台客户端的使用。

0x00 Wiki for Shadowsocks

关于服务端的搭建与客户端的选择,你都可以在 Github 上的 Shadowsocks Wiki 找到相关的内容。这里基本有你想要的一切。

0x01 Setup Shadowsocks on Server

apt-get install python-gevent python-pip

接下来用 pip 安装 Shadowsocks

pip install shadowsocks

安装完成后有两种方式启动 Shadowsocks 的服务,一种是在命令行中指定参数运行,另一种是将配置信息写在文件中,运行时读取配置文件。

以后一种为例,在/etc/下建立文件 shadowsocks.json,并写入以下内容

{
    "server":"0.0.0.0",
    "port_password":{
        "1111":"password1",
        "2222":"password2"
    },
    "timeout":300,
    "method":"aes-256-cfb"
}

这里 port_password 下面可以包含多个端口和密码的组合,方便开启多个端口进行分享。配置好端口和密码之后然后在命令行运行ssserver -c /etc/shadowsocks/config.json -d start即可,关于参数的具体含义可以使用 ssserver – h 查看。-d的作用是在后台运行。如果想要调试或者查看信息可以直接运行

ssserver -c /etc/shadowsocks/config.json

之后可以在终端中看到链接情况。

0x02 Enjoy

Windows

在 Wiki 的 Ports and Clients 中,可以看到在不同的平台下的客户端支持情况。以Windows 为例,下载并运行运行 Shadowsocks.exe 后,在我的电脑上报错提示端口冲突,可能是默认的1080端口被占用导致的,只需在服务器的设置中指派一个空闲代理端口(如1099)即可。

在软件的右键菜单中,有给局域网共享代理的选项,开启此选项后可以给同局域网的其他主机共享连接,从而无须重复运行多个客户端。比如当你使用 VMWare 运行虚拟机时,不需要考虑虚拟机操作系统,可以直接使用主机提供的代理,仅需在配置时正确填写即可。

配置好后,可以安装 Chrome 的扩展 SwitchySharp,进行简单配置如下:

情景模式中选择手动配置,socks v5 代理地址填127.0.0.1 或 运行客户端的主机 ip (如192.168.5.1),端口填写刚才设置的代理端口,如未修改默认为1080。

切换规则中勾选在线规则列表和 AutoProxy 兼容列表,规则列表 URL 可以使用这个地址

http://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt

同时在最右侧的情景模式选择刚刚填写好的配置。

按照如上设置,梯子应该可以使用了。在 Chrome 工具栏里点 SwitchySharp 图标并选择自动切换模式,访问 Google 测试一下,应该可以无障碍浏览了。

OS X

Mac下面的客户端可以有多个选择,最直接的可以使用 ShadowsocksX 项目提供的客户端,迫于官方的压力,原作者 @clowwindy 已宣布停止维护,最后版本的客户端可以在 Github 上面下载到。只需要简单的配置服务器、端口、加密方式和密码就可以直接使用了。

OS X 的客户端提供了 PAC 代理和全局代理两种方式,常见的墙外网站已经包含在了 PAC 当中,如果需要自定添加地址到代理列表里,可以编辑用户自定规则。格式的话参考 gfwllist 文件即可,具体的语法规则参考 https://adblockplus.org/filters 上面的说明。我简单举几个例子供大家参考,以便快速添加自定规则。

plugins.jetbrains.com
106.185.1.1
https://www.gerritcodereview.com
*.stackoverflow.com

当然,匹配规则支持正则表达式,不过由于性能原因,应该尽可能少地在 PAC 中使用正则表达式。语法规则应该是使用 Perl 的格式,即 /pattern/ 类型的正则表达式,下面是一个例子,用来匹配 https://edge*.digicert.com (*代表任意数字)的结果

/https:\/\/edge[0-9]+\.digicert\.com/

目前依旧在积极开发的还有 shadowsocks-libev 这个项目,配置文件的方法和服务端类似,给一个例子做参考吧。

{
    "local_address":"0.0.0.0",
    "server":"1.2.3.4",
    "server_port":443,
    "local_port":10010,
    "password":"yourpassword",
    "timeout":300,
    "method":"aes-256-cfb"
}

iOS

iOS 平台可以直接在 App Store 中搜索 ShadowSocks 下载,不过 Shadowsocks 在手机上的应用价值不如 VPN 提供全局代理方便,所以感兴趣的自己下一个玩就好了。

iOS 9 开始,Apple 提供了新的公共 API,预计 iOS 上会涌现出不少类似的应用,不妨关注一下。

无线热点登陆认证原理探究

在使用需要认证的公共热点时,通常我们都需要在一个 web 页面完成认证后才可以访问外网。这个从技术实现上来说也许不难获得思路,通过劫持请求重定向到认证页面应该就可以实现。但是在使用 CMCC 一类的运营商热点时,连接到热点后没有进行任何操作就可以在设备上调用浏览器弹出认证页面,或者在 iOS 设备上打开一个专门的特殊窗口来要求认证,这一点是怎么实现的还没有想通。而且既然可以在 iOS 设备中调出专门的窗口来认证,应该使用了某些被广泛支持的协议标准来做这件事。那么现在心里的两个疑问就是:
1.一个通常的认证流程是什么样的,从设备接入到认证前后发生了哪些动作?
2.如何实现设备接入后弹出自定义的窗口,可不可以作为攻击者利用这一点搞一些事情?

这篇日志我就想记录我寻找这两个问题答案的过程。

首先来解决第一个问题。

第一步需要了解这套强制认证的技术叫什么,经过初步的搜索得到的答案是这种认证方式叫做 Portal 认证。根据 Wikipedia 上对 Captive Portal 技术的简单介绍,这套流程的基本思路和本文开头的猜测基本相同。具体的实施方式有不止一种,例如:

  • Redirection by HTTP(HTTP 重定向)
  • ICMP Redirect (ICMP 重定向)
  • Redirection by DNS (DNS 重定向)

在设备第一次接入到无线网络的时候,会通过 DHCP 服务获取到一个 IP 地址,可能是公有地址也可能是私有地址。不论分配到的 IP 地址属于公有或者私有,此时客户端都只具备访问指定站点的权限,需要在 Portal 页面上完成认证(确认接受使用条款,验证账号密码等),在 Portal 页面完成认证后,这个系统通常会立即执行一系列的动作包括,重新分配地址(在原本是私有地址时可选),更新这台设备对应的权限控制列表(ACL)中的信息解除访问限制,并开始计时/计费等动作。

当用户需要停止使用这类网络时,通常在 Portal 页面上会提供主动断开的按钮,或者服务器会在一段时间没有检测到该客户端网络流量时将你的设备做下线处理,以保证计费准确并及时释放占用的 IP 地址。

以上就是一台新设备接入到需要 Portal 认证的网络环境中在获得访问互联网权限之前的大致步骤。那在一台设备完成介入后,以后再访问这类网络是否可以不必进行重复的认证就直接接入网络呢?答案当然是肯定的,否则在网络条件不好时无穷无尽的输入账号密码登陆不用强调也知道是一件折磨死人的事情,而这个问题的解决使用了叫做无感知认证的解决方案。关于无感知认证在参考资料中给出了常见的认证方式下实现无感知认证的方案。无感知认证的思想总结起来就是在完成了首次认证后再次接入时会利用已保存的信息进行后台验证,不需要用户主动输入口令进行登陆。不过这并不是我想了解的重点,于是不再继续深入了解。

当时产生这个疑问的时候,是用 iPhone 连接到热点后,系统会弹出一个类似浏览器的窗口,那么这个窗口是怎么弹出来的,触发的条件又是什么呢。根据前面收集到的信息,我使用 Captive Portal iOS 作为关键字在谷歌进行搜索,找到了一些相关的信息,大致介绍了这里面相关的细节。iOS设备所弹出的认证窗口是由 Apple Captive Network Assistant(CNA) 来完成的。大体的流程是当你首先链接到 Wi-Fi 网络后,iOS 设备会从它所属的众多域名下的特定域名选择一个或多个进行访问,例如

  • www.appleiphonecell.com
  • captive.apple.com
  • captive.apple.com
  • www.apple.com
  • www.itools.info
  • www.ibook.info
  • www.airport.us
  • www.thinkdifferent.us

当域名能够被解析且对特定的网页访问得到正确的响应(通常是 Success )时,CNA 会判定设备成功的链接到了互联网,比如这个页面,。如果域名被解析但访问特定页面返回的内容不是预期的结果,那么 CNA 会认为设备所处网络存在Captive Portal,因此会调用一个专门的类似浏览器的页面来发起网络请求并触发 Portal 页面,来引导用户进行登录或者认证。

根据参考资料3中的链接,在 iOS 6 及更早的版本中,一个典型的 CNA 测试请求如下:

GET /library/test/success.html

HTTP/1.0

Host: www.apple.com

User-Agent:CaptiveNetworkSupport/1.0 wispr

Connection: close

 

可以看到,在请求特定的页面时,CNA 也使用了一个自定的 User-Agent,这有助于我们识别iOS设备的 Captive Portal 探测请求并选择性的绕过,来实现自定的认证方式。也就是开头提到的第二个问题。

在提供的参考资料3中的几个链接,可以很好的帮助理解 Captive Portal 的相关概念和在 iOS 下 CNA 的大概工作机制,鉴于版本更迭中具体的实现策略可能不时更改,而且我手头暂时也没有进一步研究利用的精力,所以第二个问题就不再做深入的研究了,待日后再次遇到相关的问题后再更新本文。

参考资料

1.Portal Authentication Technology White Paper.PDF 

2.无感知认证方案技术介绍

3.Helpful Links

http://stackoverflow.com/questions/19055502/facebook-com-and-the-ios7-captive-portal-detection

http://stackoverflow.com/questions/18891706/ios7-and-captive-portals-changes-to-apple-request-url

https://supportforums.cisco.com/document/11934456/captive-portal-ios7-public

http://stackoverflow.com/questions/3615147/how-to-create-wifi-popup-login-page

http://blog.tanaza.com/blog/bid/318805/iOS-7-and-captive-portal-a-guide-to-captive-portal-requirements