DNS-Amplification на практике, часть 0


Старая добрая тема DNS-усиления (она же DNS-Amplification). Те, кто знает, что это такое, могут пропустить абзац или вообще не читать тут ничего.

Принцип атаки крайне прост. Каждый НС сервер ответит любому, кто его (ДНС) о чем-то спросит. Если в пакете запроса стоит IP-адрес того, кто, как нам думается, наиболее удостоен ответа, ДНС-сервер отправит пакет именно ему.

Весь смысл усиления - добиться как можно большего отношения размера отправленного ДНС-серверу пакета к размеру пакета, которым ДНС сервер ответит. Самый избитый метод на этом поприще - попросить ДНС резолвнуть домен "." - в ответ на это ДНС пришлет массивный пакет, в котором, как правило, несколько записей корневых ДНС. Выглядит этот ответ примерно так :

length 540
    209.160.35.110.domain   46.4.179.132.50723: [udp sum ok] 3- q: AAAA? . 0/13/14 
    ns: 
     . NS f.root-servers.net., 
     . NS g.root-servers.net., 
     . NS h.root-servers.net., 
     . NS i.root-servers.net., 
     . NS j.root-servers.net., 
     . NS k.root-servers.net., 
     . NS l.root-servers.net., 
     . NS m.root-servers.net., 
     . NS a.root-servers.net., 
     . NS b.root-servers.net., 
     . NS c.root-servers.net., 
     . NS d.root-servers.net., 
     . NS e.root-servers.net. 
 ar: 
  a.root-servers.net. A 198.41.0.4, 
  a.root-servers.net. AAAA 2001:503:ba3e::2:30, 
  b.root-servers.net. A 192.228.79.201, 
  c.root-servers.net. A 192.33.4.12, 
  d.root-servers.net. A 199.7.91.13, 
  d.root-servers.net. AAAA 2001:500:2d::d, 
  e.root-servers.net. A 192.203.230.10, 
  f.root-servers.net. A 192.5.5.241, 
  f.root-servers.net. AAAA 2001:500:2f::f, 
  g.root-servers.net. A 192.112.36.4, 
  h.root-servers.net. A 128.63.2.53, 
  h.root-servers.net. AAAA 2001:500:1::803f:235, 
  i.root-servers.net. A 192.36.148.17, 
  i.root-servers.net. AAAA 2001:7fe::53 

 (512)

Как видно, ответ аж 500+ байт. Это очень много, учитывая, что запрос составлял всего 17 (без учета IP\UDP хедеров). Для нормальной атаки необходимо набрать таких серверов, ответ от которых на наш запрос будет как можно более большим. Таких ДНС-серверов не так много как кажется, так как атака стара как мир и есть множество готовых решений для предотвращения подобной эксплуатации, о которых будет написано в конце.


Поиск уязвимых серверов

Для этих целей понадобится сервер с хорошим каналом и лояльное отношение хостера к хорошему потоку исходящего UDP. Производить сканирование лучше всего с помощью быстрого кода, написанного на нативных языках. Я использовал NodeJS, так как лаги резолва покрывают временные затраты на выполнение кода. Если не будет хватать производительности, ноду можно запустить в режиме cluster с использованием over9000 чайлдов. Идея простая - надо слать пакеты и смотреть ответы. Пакеты можно слать как на рандомный хост

var ip = [rand(255), rand(255), rand(255), rand(255)].join('.')
sendPacket(ip);

так и выбирая хосты более обдуманно. Для более быстрого сканирования, целесообразно заиметь как можно более широкий список уже существующих ДНС серверов. Для этого необходим список совершенно любых доменов, главное чтоб их было много (больше 1-2млн как минимум). Из списка берется один домен, для него запрашивается запись NS. В ответе на NS будет список ДНС серверов, обслуживающих этот домен. Вот их и добавляем в список тестируемых.

var dns = require('dns');
dns.resolveNs('google.com', function (err, addresses) {
 if (!err){
  addresses.forEach(function (a) {
   sendPacket(a);
  });
 }
});

Теперь самое интересное - отправляем ДНС пакет. На деле все проще чем кажется. Статичный пакет ДНС-запроса, который будет отправляться по UDP нужному серверу:

var ns_packet = new Buffer([
    //---- стандартный заголовок -----
    0x00, 0x03, // Айди запроса 
    0x01, 0x00, // Флаги
    0x00, 0x01, // Число запросов : 1
    0x00, 0x00, // Число ответов : 0 
    0x00, 0x00, // Число авторизованных записей
    0x00, 0x00, // Число дополнительных записей
    //---- тело запроса -----
    // запись АААА для "."
    0x00, 0x00,
    0x1C, 0x00, 
    0x01
]);

Код отправки:

var client = dgram.createSocket('udp4');
 
function sendPacket(ip){
 client.send(ns_packet, 0, ns_packet.length, 53, ip, function(err, bytes) {
  console.log('sent %d bytes', bytes);
 });
}
 
client.on('error', function(msg, rinfo){
 console.log('client error');
});
 
client.on('message', function(msg, rinfo){
 if(msg.length > 0){
  console.log("%d bytes from %s:%d", 
   msg.length, rinfo.address, rinfo.port);
 }
});

Таким образом, удалось собрать более 100к серверов, отвечающих пакетами в полкилобайта, за несколько часов.


Часть 1 >>>


______________________________
MZh
2013

Inception E-Zine