
TCP-сканер портов, асинхронно отправляющий SYN-пакеты, сканирует весь Интернет менее чем за 5 минут.
Это сканер портов масштаба Интернета. Он может просканировать весь Интернет менее чем за 5 минут, передавая 10 миллионов пакетов в секунду с одной машины.
Его использование (параметры, вывод) похоже на nmap, самый известный сканер портов. Если сомневаетесь, попробуйте одну из тех функций — поддерживаются функции, предназначенные для массового сканирования множества машин, а углублённое сканирование отдельных машин — нет.
Внутренне он использует асинхронную передачу, как сканеры портов scanrand, unicornscan и ZMap. Он более гибок, допуская произвольные диапазоны портов и адресов.
ПРИМЕЧАНИЕ: masscan использует свой собственный специализированный стек TCP/IP. Всё, кроме простого сканирования портов, может вызвать конфликт с локальным стеком TCP/IP. Это означает, что вам нужно использовать опцию --src-ip для работы с другого IP-адреса или использовать --src-port для настройки исходных портов masscan, а затем также настроить внутренний брандмауэр (например, pf или iptables) для блокировки этих портов от остальной операционной системы.
Этот инструмент бесплатен, но подумайте о пожертвовании на его развитие: Bitcoin-кошелёк: 1MASSCANaHUiyTtR3bJ2sLGuMw5kDBaj4T
На Debian/Ubuntu это выглядит примерно так. На самом деле у него нет зависимостей, кроме компилятора C (например, gcc или clang).
sudo apt-get --assume-yes install git make gcc
git clone https://github.com/robertdavidgraham/masscan
cd masscan
make
Это помещает программу в подкаталог masscan/bin.
Чтобы установить её (в Linux), выполните:
make install
Исходный код состоит из множества маленьких файлов, поэтому сборка проходит значительно быстрее при многопоточной сборке. На Raspberry Pi для этого требуется более 2 ГБ (и это приводит к сбоям), поэтому вы можете использовать меньшее число, например, -j4, а не все возможные потоки.
make -j
Хотя Linux является основной целевой платформой, код хорошо работает и на многих других системах (Windows, macOS и т.д.). Дополнительная информация о сборке:
makemakegmakecc src/*.c -o bin/masscanНа macOS двоичные файлы x86, похоже, работают так же быстро под эмуляцией ARM.
Использование похоже на nmap. Для сканирования сегмента сети на наличие некоторых портов:
# masscan -p80,8000-8100 10.0.0.0/8 2603:3001:2d00:da00::/112
Это:
10.x.x.x и подсети 2603:3001:2d00:da00::x<stdout>, который можно перенаправить в файлЧтобы увидеть полный список параметров, используйте функцию --echo. Она выводит текущую конфигурацию и завершает работу. Этот вывод можно использовать как входные данные для программы:
# masscan -p80,8000-8100 10.0.0.0/8 2603:3001:2d00:da00::/112 --echo > xxx.conf
# masscan -c xxx.conf --rate 1000
Masscan может не только определять, открыты ли порты. Он также может завершить TCP-соединение и взаимодействовать с приложением на этом порту, чтобы получить простую информацию — «баннер».
Masscan поддерживает проверку баннеров по следующим протоколам:
Проблема в том, что masscan содержит собственный стек TCP/IP, отдельный от системы, на которой он запущен. Когда локальная система получает SYN-ACK от исследуемой цели, она отвечает RST-пакетом, который разрывает соединение до того, как masscan сможет захватить баннер.
Самый простой способ предотвратить это — выделить masscan отдельный IP-адрес. Это будет выглядеть как один из следующих примеров:
# masscan 10.0.0.0/8 -p80 --banners --source-ip 192.168.1.200
# masscan 2a00:1450:4007:810::/112 -p80 --banners --source-ip 2603:3001:2d00:da00:91d7:b54:b498:859d
Выбранный адрес должен находиться в локальной подсети и не использоваться другой системой. Masscan предупредит вас, если вы ошиблись, но вы можете нарушить связь другой машины на несколько минут, так что будьте осторожны.
В некоторых случаях, например, в WiFi, это невозможно. В таких случаях вы можете заблокировать порт, который использует masscan, с помощью брандмауэра. Это предотвращает обработку пакета локальным стеком TCP/IP, но masscan всё равно видит его, поскольку он обходит локальный стек. В Linux это будет выглядеть так:
# iptables -A INPUT -p tcp --dport 61000 -j DROP
# masscan 10.0.0.0/8 -p80 --banners --source-port 61000
Вероятно, вам следует выбирать порты, которые не конфликтуют с портами, которые Linux может иначе выбрать для исходных портов. Вы можете увидеть диапазон, используемый Linux, и перенастроить его, просмотрев файл:
/proc/sys/net/ipv4/ip_local_port_range
В последней версии Kali Linux (август 2018) этот диапазон — 32768–60999, поэтому вам следует выбирать порты ниже 32768 или 61000 и выше.
Правило iptables действует только до перезагрузки. Вам нужно узнать, как сохранить конфигурацию в зависимости от вашего дистрибутива, например, с помощью iptables-save и/или iptables-persistent.
На Mac OS X и BSD есть похожие шаги. Чтобы узнать диапазоны, которых следует избегать, используйте команду, подобную следующей:
# sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
На FreeBSD и старых MacOS используйте команду ipfw:
# sudo ipfw add 1 deny tcp from any to any 40000 in
# masscan 10.0.0.0/8 -p80 --banners --source-port 40000
На новых MacOS и OpenBSD используйте утилиту фильтрации пакетов pf.
Отредактируйте файл /etc/pf.conf, добавив строку, подобную следующей:
block in proto tcp from any to any port 40000:40015
Затем для включения брандмауэра выполните команду:
# pfctl -E
Если брандмауэр уже запущен, перезагрузите правила (или перезагрузите систему) с помощью следующей команды:
# pfctl -f /etc/pf.conf
Windows не отвечает RST-пакетами, поэтому ни одна из этих техник не требуется. Однако masscan всё равно лучше всего работает со своим собственным IP-адресом, поэтому по возможности запускайте его так, даже если это строго не требуется.
То же самое необходимо и для других проверок, таких как проверка --heartbleed, которая является лишь формой проверки баннера.
Хотя программа полезна для небольших внутренних сетей, она в первую очередь предназначена для работы со всем Интернетом. Это может выглядеть примерно так:
# masscan 0.0.0.0/0 -p0-65535
Сканирование всего Интернета — плохая идея. Во-первых, некоторые части Интернета плохо реагируют на сканирование. Во-вторых, некоторые сайты отслеживают сканирования и заносят вас в чёрный список, что может привести к блокировке полезных частей Интернета. Поэтому необходимо исключить множество диапазонов. Для занесения в чёрный список или исключения диапазонов используйте следующий синтаксис:
# masscan 0.0.0.0/0 -p0-65535 --excludefile exclude.txt
Это просто выводит результаты в командную строку. Вероятно, вы захотите сохранить их в файл. Поэтому вам нужно нечто вроде:
# masscan 0.0.0.0/0 -p0-65535 -oX scan.xml
Это сохраняет результаты в XML-файл, позволяя легко импортировать результаты в базу данных или что-то подобное.
Но это выполняется со скоростью по умолчанию 100 пакетов/сек, что займёт вечность для сканирования Интернета. Вам нужно ускорить процесс следующим образом:
# masscan 0.0.0.0/0 -p0-65535 --max-rate 100000
Это увеличивает скорость до 100 000 пакетов/сек, что позволит просканировать весь Интернет (за исключением исключений) примерно за 10 часов на порт (или 655 360 часов при сканировании всех портов).
Обратите внимание, что все эти параметры совместимы с nmap. Кроме того, для вас автоматически устанавливаются «невидимые» опции, совместимые с nmap: -sS -Pn -n --randomize-hosts --send-eth. Аналогично, формат XML-файла вдохновлён nmap. Конечно, есть много различий, поскольку асинхронная природа программы приводит к принципиально иному подходу к задаче.
Вышеприведённая командная строка немного громоздка. Вместо того чтобы размещать всё в командной строке, параметры можно сохранить в файл. Вышеуказанные настройки будут выглядеть так:
# My Scan
rate = 100000.00
output-format = xml
output-status = all
output-filename = scan.xml
ports = 0-65535
range = 0.0.0.0-255.255.255.255
excludefile = exclude.txt
Чтобы использовать этот конфигурационный файл, примените параметр -c:
# masscan -c myscan.conf
Это также упрощает повторное сканирование.
По умолчанию masscan сначала загружает конфигурационный файл /etc/masscan/masscan.conf. Любые последующие параметры конфигурации переопределяют значения в этом файле конфигурации по умолчанию. Именно там я размещаю параметр «excludefile», чтобы никогда не забыть его. Он работает автоматически.
По умолчанию masscan создаёт довольно большие текстовые файлы, но их легко преобразовать в любой другой формат. Поддерживаются пять форматов вывода:
xml: просто используйте параметр -oX <filename>.
Или используйте параметры --output-format xml и --output-filename <filename>.
binary: это встроенный формат masscan. Он создаёт гораздо меньшие файлы, так что при сканировании Интернета мой диск не переполняется. Однако их нужно анализировать. Параметр командной строки --readscan позволяет читать бинарные файлы сканирования. Использование --readscan с опцией -oX создаст XML-версию файла результатов.
grepable: это реализация вывода -oG Nmap, которую можно легко анализировать с помощью инструментов командной строки. Просто используйте параметр -oG <filename>. Или используйте параметры --output-format grepable и --output-filename <filename>.
json: сохраняет результаты в формате JSON. Просто используйте параметр -oJ <filename>. Или используйте параметры и .
Где это разумно, были предприняты все усилия, чтобы программа была знакома пользователям nmap, хотя она принципиально отличается. Masscan оптимизирован для широкого сканирования большого количества машин, тогда как nmap предназначен для интенсивного сканирования одной машины или небольшого диапазона.
Два важных отличия:
-p <ports>nmap (например, 10.0.0-255.0-255).Можно считать, что у masscan постоянно включены следующие настройки:
-sS: выполняет только SYN-сканирование (в настоящее время; в будущем изменится)-Pn: не пингует хосты первой, что является основой асинхронной работы-n: не выполняется разрешение DNS--randomize-hosts: сканирование полностью рандомизировано, всегда, это нельзя изменить--send-eth: отправляет с использованием сырых libpcapЕсли вы хотите получить список дополнительных совместимых с nmap настроек, используйте следующую команду:
# masscan --nmap
Эта программа выдает пакеты очень быстро. В Windows или на виртуальных машинах она может достигать 300 000 пакетов/сек. В Linux (без виртуализации) она будет выдавать 1,6 миллиона пакетов/сек. Этого достаточно, чтобы «расплавить» большинство сетей.
Обратите внимание, что она расплавит только вашу собственную сеть. IP-адреса целей рандомизируются, так что она не должна перегружать удалённые сети.
По умолчанию скорость установлена на 100 пакетов/сек. Чтобы увеличить скорость до миллиона, используйте что-то вроде --rate 1000000.
При сканировании IPv4-Интернета вы будете сканировать множество подсетей, поэтому, хотя исходящий трафик велик, каждая целевая подсеть будет получать маленькую скорость входящих пакетов.
Однако при сканировании IPv6 вы, как правило, сосредотачиваетесь на одной целевой подсети с миллиардами адресов. Таким образом, ваше поведение по умолчанию перегрузит целевую сеть. Сети часто выходят из строя под нагрузкой, которую может генерировать masscan.
В этом разделе описываются основные вопросы проектирования программы.
Файл main.c содержит функцию main(), как и следовало ожидать. Он также содержит функции transmit_thread() и receive_thread(). Эти функции были намеренно упрощены и снабжены подробными комментариями, чтобы вы могли понять структуру программы, просто проходя пошагово каждую из них.
Это асинхронная конструкция. Другими словами, это для nmap то же, что веб-сервер nginx для Apache. Он имеет отдельные потоки передачи и приёма, которые в значительной степени независимы друг от друга. Это та же конструкция, что и в scanrand, unicornscan и ZMap.
Из-за асинхронности он работает на максимальной скорости, допускаемой базовой передачей пакетов.
Ключевое различие между Masscan и другими сканерами — это способ рандомизации целей.
Основной принцип — иметь одну переменную индекса, которая начинается с нуля и увеличивается на единицу для каждого зонда. В коде C это выражается как:
for (i = 0; i < range; i++) {
scan(i);
}
Нам нужно преобразовать индекс в IP-адрес. Допустим, вы хотите отсканировать все «частные» IP-адреса. Это будет таблица диапазонов, например:
192.168.0.0/16
10.0.0.0/8
172.16.0.0/12
В этом примере первые 64k индексов добавляются к 192.168.x.x для формирования целевого адреса. Затем следующие 16 миллионов добавляются к 10.x.x.x. Оставшиеся индексы в диапазоне применяются к 172.16.x.x.
В этом примере у нас всего три диапазона. При сканировании всего Интернета на практике у нас более 100 диапазонов. Это связано с тем, что приходится заносить в чёрный список или исключать множество подсетей. Это дробит желаемый диапазон на сотни меньших диапазонов.
Это приводит к одному из самых медленных участков кода. Мы передаём 10 миллионов пакетов в секунду и должны преобразовывать переменную индекса в IP-адрес для каждого зонда. Мы решаем эту задачу, выполняя «бинарный поиск» в небольшом объёме памяти. При такой скорости пакетов эффективность кэширования начинает доминировать над эффективностью алгоритмов. Существует множество более эффективных техник в теории, но все они требуют такого большого объёма памяти, что на практике оказываются медленнее.
Мы называем функцию, которая преобразует индекс в IP-адрес, функцией pick(). В работе это выглядит так:
for (i = 0; i < range; i++) {
ip = pick(addresses, i);
scan(ip);
}
Masscan поддерживает не только диапазоны IP-адресов, но и диапазоны портов. Это означает, что мы должны извлекать из переменной индекса как IP-адрес, так и порт. Это достаточно просто:
range = ip_count * port_count;
for (i = 0; i < range; i++) {
ip = pick(addresses, i / port_count);
port = pick(ports, i % port_count);
scan(ip, port);
}
Это приводит к ещё одной дорогой части кода. Инструкции деления/остатка занимают около 90 тактов, или 30 наносекунд, на процессорах x86. При передаче со скоростью 10 миллионов пакетов/сек у нас есть всего 100 наносекунд на пакет. Я не вижу способа оптимизировать это лучше. К счастью, две такие операции могут выполняться одновременно, так что выполнение двух таких операций, как показано выше, стоит не дороже, чем одной.
На самом деле есть несколько простых оптимизаций для вышеуказанных проблем производительности, но все они полагаются на i++, то есть на то, что переменная индекса увеличивается на единицу в процессе сканирования. На самом деле нам нужно рандомизировать эту переменную. Нам нужно рандомизировать порядок сканирования IP-адресов, иначе мы перегрузим целевые сети, которые не рассчитаны на такую скорость. Нам нужно равномерно распределить трафик по цели.
Способ рандомизации заключается в шифровании переменной индекса. По определению, шифрование является случайным и создаёт взаимно однозначное соответствие между исходной переменной индекса и выходными данными. Это означает, что хотя мы линейно проходим по диапазону, выходные IP-адреса полностью случайны. В коде это выглядит так:
range = ip_count * port_count;
for (i = 0; i < range; i++) {
x = encrypt(i);
ip = pick(addresses, x / port_count);
port = pick(ports, x % port_count);
scan(ip, port);
}
Это также имеет существенную стоимость. Поскольку диапазон имеет непредсказуемый размер, а не является ровной степенью двойки, мы не можем использовать дешёвые бинарные методы, такие как AND (&) и XOR (^). Вместо этого мы вынуждены использовать дорогие операции, такие как ОСТАТОК (%). В моих текущих тестах шифрование переменной занимает 40 наносекунд.
Эта архитектура позволяет реализовать множество интересных возможностей. Например, она поддерживает «осколки» (shards). Вы можете настроить 5 машин, каждая из которых выполняет пятую часть сканирования, или range / shard_count. Осколками могут быть несколько машин, или просто несколько сетевых адаптеров на одной машине, или даже (если хотите) несколько исходных IP-адресов на одном сетевом адаптере.
Или вы можете использовать «зерно» (seed) или «ключ» для функции шифрования, чтобы при каждом сканировании получать разный порядок, например, x = encrypt(seed, i).
Мы также можем приостановить сканирование, выйдя из программы, просто запомнив текущее значение i, и перезапустить его позже. Я часто делаю так при разработке. Я вижу, что при сканировании Интернета что-то пошло не так, нажимаю <ctrl-c>, чтобы остановить сканирование, и перезапускаю его после исправления ошибки.
Ещё одна возможность — повторные передачи/ретрайми. Пакеты иногда теряются в Интернете, поэтому можно отправлять два пакета друг за другом. Однако если пакет теряется, может потеряться и следующий за ним. Поэтому копию нужно отправлять с интервалом около 1 секунды. Это просто. У нас уже есть переменная 'rate', которая является скоростью передачи пакетов в секунду, поэтому функция повторной передачи — просто использовать i + rate в качестве индекса. Когда-нибудь я проведу исследование Интернета, чтобы различать «подряд», «1 секунда», «10 секунд» и «1 минута» таким образом и посмотреть, есть ли разница в том, что теряется.
Асинхронная техника известна как решение «проблемы C10k». Masscan designed for the next level of scalability, the "C10M problem".
The C10M solution is to bypass the kernel. There are three primary kernel bypasses in Masscan:
Masscan can use the PF_RING DNA driver. This driver DMAs packets directly from user-mode memory to the network driver with zero kernel involvement. That allows software, even with a slow CPU, to transmit packets at the maximum rate the hardware allows. If you put 8 10-gbps network cards in a computer, this means it could transmit at 100-million packets/second.
Masscan has its own built-in TCP stack for grabbing banners from TCP connections. This means it can easily support 10 million concurrent TCP connections, assuming of course that the computer has enough memory.
Masscan has no "mutex". Modern mutexes (aka. futexes) are mostly user-mode, but they have two problems. The first problem is that they cause cache-lines to bounce quickly back-and-forth between CPUs. The second is that when there is contention, they'll do a system call into the kernel, which kills performance. A mutex on the fast path of a program severely limits scalability. Instead, Masscan uses "rings" to synchronize things, such as when the user-mode TCP stack in the receive thread needs to transmit a packet without interfering with the transmit thread.
Масштабируемость C10M
Решение C10M заключается в обходе ядра. В Masscan есть три основных способа обхода ядра:
Masscan может использовать драйвер PF_RING DNA. Этот драйвер передаёт пакеты напрямую из памяти пользовательского режима в сетевой драйвер без участия ядра. Это позволяет программному обеспечению, даже на медленном ЦП, передавать пакеты с максимальной скоростью, которую допускает оборудование. Если установить в компьютер 8 сетевых карт по 10 Гбит/с, это означает, что можно передавать 100 миллионов пакетов в секунду.
У Masscan есть собственный встроенный стек TCP/IP для захвата баннеров из TCP-соединений. Это означает, что он может легко поддерживать 10 миллионов одновременных TCP-соединений, при условии, конечно, что в компьютере достаточно памяти.
В Masscan нет «мьютексов». Современные мьютексы (они же futex) в основном работают в пользовательском режиме, но у них есть две проблемы. Первая проблема — они вызывают быстрое переключение строк кэша между ЦП. Вторая — при конкуренции они выполняют системный вызов к ядру, что убивает производительность. Мьютекс на быстром пути программы серьёзно ограничивает масштабируемость. Вместо этого Masscan использует «кольца» для синхронизации, например, когда пользовательский стек TCP/IP в потоке приёма должен передать пакет, не мешая потоку передачи.
на стандартном C (C90). Поэтому он компилируется в Visual Studio с помощью компилятора Microsoft, компилятора Clang/LLVM на Mac OS X и GCC на Linux.
Windows и Mac не оптимизированы для передачи пакетов и выдают только около 300 000 пакетов в секунду, тогда как Linux может обрабатывать 1 500 000 пакетов/сек. Это, вероятно, быстрее, чем вам нужно.
За обнаружение уязвимостей предлагается вознаграждение. Дополнительную информацию см. в файле VULNINFO.md.
В этом проекте используются безопасные функции, такие как safe_strcpy(), вместо небезопасных функций,
например strcpy().
Проект имеет автоматические модульные регрессионные тесты (make regress).
Приложено много усилий, чтобы ввод/вывод был похож на nmap, с которым
знаком (или должен быть знаком) каждый, кто занимается сканированием портов.
Masscan поддерживает IPv6, но специального режима нет — оба поддерживаются
одновременно. (Опции -6 не существует — она всегда доступна).
В любом примере использования masscan просто подставьте IPv6-адрес туда, где вы видите IPv4-адрес. Вы можете включать IPv4 и IPv6 адреса одновременно в одно сканирование. Вывод включает соответствующий адрес на том же месте, без специальных пометок.
Помните, что адресное пространство IPv6 действительно огромно. Вероятно, вам не захочется сканировать большие диапазоны, за исключением, возможно, первых 64k адресов подсети, назначенных через DHCPv6.
Вместо этого вам, вероятно, захочется сканировать большие списки адресов, хранящиеся
в файле (--include-file filename.txt), полученные из других источников.
Как и везде, этот файл может содержать списки как IPv4, так и IPv6 адресов.
Тестовый файл, который я использую, содержит 8 миллионов адресов. Файлы такого размера требуют
нескольких дополнительных секунд для чтения при запуске (masscan сортирует адреса и удаляет
дубликаты перед сканированием).
Помните, что masscan содержит собственный сетевой стек. Таким образом, локальная машина, с которой вы запускаете masscan, не должна быть включена поддержка IPv6 — хотя локальная сеть должна уметь маршрутизировать IPv6-пакеты.
Чтобы превысить 2 миллиона пакетов в секунду, вам понадобится сетевой адаптер Intel 10 Гбит/с и специальный драйвер, известный как "PF_RING ZC" от ntop. Masscan не нужно пересобирать для использования PF_RING. Для использования PF_RING вам нужно собрать следующие компоненты:
libpfring.so (установлен в /usr/lib/libpfring.so)pf_ring.ko (их драйвер ядра)ixgbe.ko (их версия драйвера Intel 10 Гбит/с Ethernet)Вам не нужно собирать их версию libpcap.so.
Когда Masscan обнаруживает, что адаптер называется как zc:enp1s0 вместо
enp1s0, он автоматически переключается в режим PF_RING ZC.
Более подробное обсуждение можно найти в PoC||GTFO 0x15.
Проект содержит встроенный модульный тест:
$ make test
bin/masscan --selftest
selftest: success!
Это тестирует множество сложных участков кода. Вам следует выполнить это после сборки.
Для тестирования производительности выполните что-то вроде следующего на одноразовый адрес, чтобы не перегружать ваш локальный маршрутизатор:
$ bin/masscan 0.0.0.0/4 -p80 --rate 100000000 --router-mac 66-55-44-33-22-11
Подставной --router-mac удерживает пакеты в локальном сегменте сети, чтобы
они не уходили в Интернет.
Вы также можете протестировать в «автономном» режиме, который показывает, насколько быстро программа работает без накладных расходов на передачу:
$ bin/masscan 0.0.0.0/4 -p80 --rate 100000000 --offline
Этот второй тест примерно показывает, насколько быстро программа работала бы, если бы использовала PF_RING, у которого почти нулевые накладные расходы.
Кстати, алгоритм рандомизации активно использует «целочисленную арифметику»,
хронически медленную операцию на процессорах. Современные процессоры удвоили скорость
выполнения этих вычислений, что делает masscan значительно быстрее.
Этот инструмент создан Робертом Грэмом (Robert Graham): email: [email protected] twitter: @ErrataRob
Copyright (c) 2013 Robert David Graham
Эта программа является свободным программным обеспечением: вы можете распространять и/или изменять её в соответствии с условиями GNU Affero General Public License, опубликованной Free Software Foundation, версия 3 этой Лицензии.
Эта программа распространяется в надежде, что она будет полезна, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ; даже без подразумеваемой гарантии КОММЕРЧЕСКОЙ ЦЕННОСТИ или ПРИГОДНОСТИ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ. См. GNU Affero General Public License для получения более подробной информации.
Вы должны были получить копию GNU Affero General Public License вместе с этой программой. Если нет, см. https://www.gnu.org/licenses/.
--output-format json--output-filename <filename>list: это простой список с одной парой хост-порт на строку. Просто используйте параметр -oL <filename>. Или используйте параметры --output-format list и --output-filename <filename>. Формат:
<port state> <protocol> <port number> <IP address> <POSIX timestamp>
open tcp 80 XXX.XXX.XXX.XXX 1390380064