company_banner

Ядерный шелл поверх ICMP



    TL;DR: пишу модуль ядра, который будет читать команды из пейлоада ICMP и выполнять их на сервере даже в том случае, если у вас упал SSH. Для самых нетерпеливых весь код на github.

    Осторожно! Опытные программисты на C рискуют разрыдаться кровавыми слезами! Я могу ошибаться даже в терминологии, но любая критика приветствуется. Пост рассчитан на тех, кто имеет самое приблизительное представление о программировании на C и хочет заглянуть во внутренности Linux.

    В комментариях к моей первой статье упомянули SoftEther VPN, который умеет мимикрировать под некоторые «обычные» протоколы, в частности, HTTPS, ICMP и даже DNS. Я представляю себе работу только первого из них, так как хорошо знаком с HTTP(S), а туннелирование поверх ICMP и DNS пришлось изучать.

    image

    Да, я в 2020 году узнал, что в ICMP-пакеты можно вставить произвольный пейлоад. Но лучше поздно, чем никогда! И раз уж с этим можно что-то сделать, значит нужно делать. Так как в своей повседневности чаще всего я пользуюсь командной строкой, в том числе через SSH, идея ICMP-шелла пришла мне в голову первым делом. А чтобы собрать полное буллщит-бинго, решил писать в виде модуля Linux на языке, о котором я имею лишь приблизительное представление. Такой шелл не будет виден в списке процессов, можно загрузить его в ядро и он не будет лежать на файловой системе, вы не увидите ничего подозрительного в списке прослушиваемых портов. По своим возможностям это полноценный руткит, но я надеюсь доработать и использовать его в качестве шелла последней надежды, когда Load Average слишком высокий для того, чтобы зайти по SSH и выполнить хотя бы echo i > /proc/sysrq-trigger, чтобы восстановить доступ без перезагрузки.

    Берём текстовый редактор, базовые скиллы программирования на Python и C, гугл и виртуалку которую не жалко пустить под нож если всё поломается (опционально — локальный VirtualBox/KVM/etc) и погнали!

    Клиентская часть


    Мне казалось, что для клиентской части придётся писать скрипт строк эдак на 80, но нашлись добрые люди, которые сделали за меня всю работу. Код оказался неожиданно простым, умещается в 10 значащих строк:

    import sys
    from scapy.all import sr1, IP, ICMP
    
    if len(sys.argv) < 3:
        print('Usage: {} IP "command"'.format(sys.argv[0]))
        exit(0)
    
    p = sr1(IP(dst=sys.argv[1])/ICMP()/"run:{}".format(sys.argv[2]))
    if p:
        p.show()

    Скрипт принимает два аргумента, адресом и пейлоад. Перед отправкой пейлоад предваряется ключом run:, он нам понадобится чтобы исключить пакеты со случайным пейлоадом.

    Ядро требует привилегий для того чтобы крафтить пакеты, поэтому скрипт придётся запускать с правами суперпользователя. Не забудьте дать права на выполнение и установить сам scapy. В Debian есть пакет, называется python3-scapy. Теперь можно проверять, как это всё работает.

    Запуск и вывод команды
    morq@laptop:~/icmpshell$ sudo ./send.py 45.11.26.232 "Hello, world!"
    Begin emission:
    .Finished sending 1 packets.
    *
    Received 2 packets, got 1 answers, remaining 0 packets
    ###[ IP ]###
    version = 4
    ihl = 5
    tos = 0x0
    len = 45
    id = 17218
    flags =
    frag = 0
    ttl = 58
    proto = icmp
    chksum = 0x3403
    src = 45.11.26.232
    dst = 192.168.0.240
    \options \
    ###[ ICMP ]###
    type = echo-reply
    code = 0
    chksum = 0xde03
    id = 0x0
    seq = 0x0
    ###[ Raw ]###
    load = 'run:Hello, world!


    Так это выглядит в сниффере
    morq@laptop:~/icmpshell$ sudo tshark -i wlp1s0 -O icmp -f "icmp and host 45.11.26.232"
    Running as user "root" and group "root". This could be dangerous.
    Capturing on 'wlp1s0'
    Frame 1: 59 bytes on wire (472 bits), 59 bytes captured (472 bits) on interface wlp1s0, id 0
    Internet Protocol Version 4, Src: 192.168.0.240, Dst: 45.11.26.232
    Internet Control Message Protocol
    Type: 8 (Echo (ping) request)
    Code: 0
    Checksum: 0xd603 [correct]
    [Checksum Status: Good]
    Identifier (BE): 0 (0x0000)
    Identifier (LE): 0 (0x0000)
    Sequence number (BE): 0 (0x0000)
    Sequence number (LE): 0 (0x0000)
    Data (17 bytes)

    0000 72 75 6e 3a 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 run:Hello, world
    0010 21 !
    Data: 72756e3a48656c6c6f2c20776f726c6421
    [Length: 17]

    Frame 2: 59 bytes on wire (472 bits), 59 bytes captured (472 bits) on interface wlp1s0, id 0
    Internet Protocol Version 4, Src: 45.11.26.232, Dst: 192.168.0.240
    Internet Control Message Protocol
    Type: 0 (Echo (ping) reply)
    Code: 0
    Checksum: 0xde03 [correct]
    [Checksum Status: Good]
    Identifier (BE): 0 (0x0000)
    Identifier (LE): 0 (0x0000)
    Sequence number (BE): 0 (0x0000)
    Sequence number (LE): 0 (0x0000)
    [Request frame: 1]
    [Response time: 19.094 ms]
    Data (17 bytes)

    0000 72 75 6e 3a 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 run:Hello, world
    0010 21 !
    Data: 72756e3a48656c6c6f2c20776f726c6421
    [Length: 17]

    ^C2 packets captured


    Пейлоад в пакете с ответом не меняется.

    Модуль ядра


    Для сборки в виртуалке с Debian понадобятся как минимум make и linux-headers-amd64, остальное подтянется в виде зависимостей. В статье код целиком приводить не буду, вы его можете склонировать на гитхабе.

    Настройка хука


    Для начала нам понадобятся две функции для того, чтобы загрузить модуль и чтобы его выгрузить. Функция для выгрузки не обязательна, но тогда и rmmod выполнить не получится, модуль выгрузится только при выключении.

    #include <linux/module.h>
    #include <linux/netfilter_ipv4.h>
    
    static struct nf_hook_ops nfho;
    
    static int __init startup(void)
    {
      nfho.hook = icmp_cmd_executor;
      nfho.hooknum = NF_INET_PRE_ROUTING;
      nfho.pf = PF_INET;
      nfho.priority = NF_IP_PRI_FIRST;
      nf_register_net_hook(&init_net, &nfho);
      return 0;
    }
    
    static void __exit cleanup(void)
    {
      nf_unregister_net_hook(&init_net, &nfho);
    }
    
    MODULE_LICENSE("GPL");
    module_init(startup);
    module_exit(cleanup);

    Что здесь происходит:

    1. Подтягиваются два заголовочных файла для манипуляций собственно с модулем и с нетфильтром.
    2. Все операции проходят через нетфильтр, в нём можно задавать хуки. Для этого нужно заявить структуру, в которой хук будет настраиваться. Самое важное — указать функцию, которая будет выполняться в качестве хука: nfho.hook = icmp_cmd_executor; до самой функции я ещё доберусь.
      Затем я задал момент обработки пакета: NF_INET_PRE_ROUTING указывает обрабатывать пакет, когда он только появился в ядре. Можно использовать NF_INET_POST_ROUTING для обработки пакета на выходе из ядра.
      Вешаю фильтр на IPv4: nfho.pf = PF_INET;.
      Назначаю своему хуку наивысшей приоритет: nfho.priority = NF_IP_PRI_FIRST;
      И регистрирую структуру данных как собственно хук: nf_register_net_hook(&init_net, &nfho);
    3. В завершающей функции хук удаляется.
    4. Лицензия обозначена явно, чтобы компилятор не ругался.
    5. Функции module_init() и module_exit() задают другие функции в качестве инициализирующей и завершающей работу модуля.

    Извлечение пейлоада


    Теперь нужно извлечь пейлоад, это оказалось самой сложной задачей. В ядре нет встроенных функций для работы с пейлоадом, можно только парсить заголовки более высокоуровневых протоколов.

    #include <linux/ip.h>
    #include <linux/icmp.h>
    
    #define MAX_CMD_LEN 1976
    
    char cmd_string[MAX_CMD_LEN];
    
    struct work_struct my_work;
    
    DECLARE_WORK(my_work, work_handler);
    
    static unsigned int icmp_cmd_executor(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
    {
      struct iphdr *iph;
      struct icmphdr *icmph;
    
      unsigned char *user_data;
      unsigned char *tail;
      unsigned char *i;
      int j = 0;
    
      iph = ip_hdr(skb);
      icmph = icmp_hdr(skb);
    
      if (iph->protocol != IPPROTO_ICMP) {
        return NF_ACCEPT;
      }
      if (icmph->type != ICMP_ECHO) {
        return NF_ACCEPT;
      }
    
      user_data = (unsigned char *)((unsigned char *)icmph + (sizeof(icmph)));
      tail = skb_tail_pointer(skb);
    
      j = 0;
      for (i = user_data; i != tail; ++i) {
        char c = *(char *)i;
    
        cmd_string[j] = c;
    
        j++;
    
        if (c == '\0')
          break;
    
        if (j == MAX_CMD_LEN) {
          cmd_string[j] = '\0';
          break;
        }
    
      }
    
      if (strncmp(cmd_string, "run:", 4) != 0) {
        return NF_ACCEPT;
      } else {
        for (j = 0; j <= sizeof(cmd_string)/sizeof(cmd_string[0])-4; j++) {
          cmd_string[j] = cmd_string[j+4];
          if (cmd_string[j] == '\0')
    	break;
        }
      }
    
      schedule_work(&my_work);
    
      return NF_ACCEPT;
    }

    Что происходит:

    1. Пришлось подключить дополнительные заголовочные файлы, на этот раз для манипуляция с IP- и ICMP-хедерами.
    2. Задаю максимальную длину строки: #define MAX_CMD_LEN 1976. Почему именно такую? Потому что на большую компилятор ругается! Мне уже подсказали, что надо разбираться со стеком и кучей, когда-нибудь я обязательно это сделаю и может даже поправлю код. Сходу задаю строку, в которой будет лежать команда: char cmd_string[MAX_CMD_LEN];. Она должна быть видима во всех функциях, об этом подробней расскажу в пункте 9.
    3. Теперь надо инициализировать (struct work_struct my_work;) структуру и связать её с ещё одной функцией (DECLARE_WORK(my_work, work_handler);). О том, зачем это нужно, я также расскажу в девятом пункте.
    4. Теперь объявляю функцию, которая и будет хуком. Тип и принимаемые аргументы диктуются нетфильтром, нас интересует только skb. Это буфер сокета, фундаментальная структура данных, которая содержит все доступные сведения о пакете.
    5. Для работы функции понадобится две структуры, и несколько переменных, в том числе два итератора.

        struct iphdr *iph;
        struct icmphdr *icmph;
      
        unsigned char *user_data;
        unsigned char *tail;
        unsigned char *i;
        int j = 0;
    6. Можно приступить к логике. Для работы модуля не нужны никакие пакеты кроме ICMP Echo, поэтому парсим буфер встроенными функциями и выкидываем все не ICMP- и не Echo-пакеты. Возврат NF_ACCEPT означает принятие пакета, но можете и дропнуть пакеты, вернув NF_DROP.

        iph = ip_hdr(skb);
        icmph = icmp_hdr(skb);
      
        if (iph->protocol != IPPROTO_ICMP) {
          return NF_ACCEPT;
        }
        if (icmph->type != ICMP_ECHO) {
          return NF_ACCEPT;
        }


      Я не проверял, что произойдёт без проверки заголовков IP. Моё минимальное знание C подсказывает: без дополнительных проверок обязательно произойдёт что-нибудь ужасное. Я буду рад, если вы меня в этом разубедите!
    7. Теперь, когда пакет точно нужного типа, можно извлекать данные. Без встроенной функции приходится сначала получать указатель на начало пейлода. Делается это через одно место, нужно взять указатель на начало заголовка ICMP и передвинуть его на размер этого заголовка. Для всего используется структура icmph: user_data = (unsigned char *)((unsigned char *)icmph + (sizeof(icmph)));
      Конец заголовка должен совпадать с концом полезной нагрузки в skb, поэтому получаем его ядерными средствами из соответствующей структуры: tail = skb_tail_pointer(skb);.

      image

      Картинку утащил отсюда, можете почитать подробней про буфер сокета.
    8. Получив указатели на начало и конец, можно скопировать данные в строку cmd_string, проверить её на наличие префикса run: и, либо выкинуть пакет в случае его отсутствия, либо снова перезаписать строку, удалив этот префикс.
    9. Ну всё, теперь можно вызвать ещё один хендлер: schedule_work(&my_work);. Так как в такой вызов передать параметр не получится, строка с командой и должна быть глобальной. schedule_work() поместит функцию ассоциированную с переданной структурой в общую очередь планировщика задач и завершится, позволив не ждать завершения команды. Это нужно потому что хук должен быть очень быстрым. Иначе у вас, на выбор, ничего не запустится или вы получите kernel panic. Промедление смерти подобно!
    10. Всё, можно принимать пакет соответствующим возвратом.

    Вызов программы в юзерспейсе


    Эта функция самая понятная. Название её было задано в DECLARE_WORK(), тип и принимаемые аргументы не интересны. Берём строку с командой и передаём её целиком шеллу. Пусть он сам разбирается с парсингом, поиском бинарей и со всем остальным.

    static void work_handler(struct work_struct * work)
    {
      static char *argv[] = {"/bin/sh", "-c", cmd_string, NULL};
      static char *envp[] = {"PATH=/bin:/sbin", NULL};
    
      call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
    }

    1. Задаём аргументы в массив строк argv[]. Предположу, что все знают, что программы на самом деле выполняются именно так, а не сплошной строкой с пробелами.
    2. Задаём переменные окружения. Я вставил только PATH с минимальным набором путей, рассчитывая что у всех уже объединены /bin с /usr/bin и /sbin с /usr/sbin. Прочие пути довольно редко имеют значение на практике.
    3. Готово, выполняем! Функция ядра call_usermodehelper() принимает на вход. путь к бинарю, массив аргументов, массив переменных окружения. Здесь я тоже предполагаю, что все понимают смысл передачи пути к исполняемому файлу отдельным аргументом, но можете спросить. Последний аргумент указывает, ждать ли завершения процесса (UMH_WAIT_PROC), запуска процесса (UMH_WAIT_EXEC) или не ждать вообще (UMH_NO_WAIT). Есть ещё UMH_KILLABLE, я не стал разбираться в этом.

    Сборка


    Сборка ядерных модулей выполняется через ядерный же make-фреймворк. Вызывается make внутри специальной директории привязанной к версии ядра (определяется тут: KERNELDIR:=/lib/modules/$(shell uname -r)/build), а местонахождение модуля передаётся переменной M в аргументах. В таргетах icmpshell.ko и clean целиком используется этот фреймворк. В obj-m указывается объектный файл, который будет переделан в модуль. Синтаксис, которые переделывает main.o в icmpshell.o (icmpshell-objs = main.o) для меня выглядит не очень логичным, но пусть так и будет.

    KERNELDIR:=/lib/modules/$(shell uname -r)/build

    obj-m = icmpshell.o
    icmpshell-objs = main.o

    all: icmpshell.ko

    icmpshell.ko: main.c
    make -C $(KERNELDIR) M=$(PWD) modules

    clean:
    make -C $(KERNELDIR) M=$(PWD) clean


    Собираем: make. Загружаем: insmod icmpshell.ko. Готово, можно проверять: sudo ./send.py 45.11.26.232 "date > /tmp/test". Если у вас на машине появился файл /tmp/test и в нём лежит дата отправки запроса, значит вы сделали всё правильно и я сделал всё правильно.

    Заключение


    Мой первый опыт ядерной разработки оказался гораздо более простым, чем я ожидал. Даже не имея опыта разработки на C, ориентируясь на подсказки компилятора и выдачу гугла, я смог написать рабочий модуль и почувствовать себя кернел хакером, а заодно и скрипт-кидди. Кроме этого я зашёл на канал Kernel Newbies, где мне подсказали использовать schedule_work() вместо вызова call_usermodehelper() внутри самого хука и пристыдили, справедливо заподозрив скам. Сотня строк кода мне стоила где-то недели разработки в свободное время. Удачный опыт, разрушивший мой личный миф о непосильной сложности системной разработки.

    Если кто-то согласится выполнить код-ревью на гитхабе, я буду признателен. Я почти уверен, что допустил много глупых ошибок, особенно, в работе со строками.

    RUVDS.com
    VDS/VPS-хостинг. Скидка 10% по коду HABR

    Комментарии 57

      +1

      Любопытно. Для каких-то локальных сетей удобнее разослать пачку ICMP без установки полноценного TCP-коннекта. Но выглядит жутко опасно, если такое вдруг начнет торчать в интернет.

        0

        Из структуры с IP-хедером можно взять source, но чтобы он не был захардкожен надо дополнительно заморочиться, потребуется больше времени и разработки. Это пара строк дополнительного кода, но решение не идеальное. Хочу найти какие-нибудь примитивы, которые позволят обеспечить симметричное шифрование вместо авторизации по источнику и префикса с паролем, надеюсь, кто-нибудь подскажет.

          +2
          Из структуры с IP-хедером можно взять source

          Но ведь такой пакет легко подделать

            –1

            Да, верно. Такая безопасность будет работать только через неясность. Я полагаю, что можно заменить прероутинг на построутинг и дать доступ только локальным IP-адресам. Возможно в этом случае до модуля не доберутся "марсианские" пакеты и подделка не будет работать.

            +7
            Я, пожалуй, подскажу небольшой чеклист, по которому следует проверить то, что Вам предложат для шифрования.
            1. Что будет, если злоумышленник подслушает зашифрованный пакет и через некоторое время пошлёт его повторно? Не выполнится ли команда повторно?
            2. Предположим, злоумышленник поменял порядок прохождения пакетов. К чему это приведёт?
            3. Предположим, злоумышленник перехватил все пакеты и не пустил ни один из них к серверу. А потом через несколько дней сам начал отправлять перехваченные пакеты на сервер. Что будет?
            4. Предположим, злоумышленник подслушал два зашифрованных пакета и произвёл над ними, например, операцию XOR. Не получит ли он в результате XOR от посланных на сервер команд?
            5. Предположим, злоумышенник некоторое время слушал трафик, а потом устроил маски-шоу и получил доступ к серверу и хранящемуся на нём паролю. Не сможет ли он после этого расшифровать отправленные на сервер команды?
            6. Предположим, злоумышленник знает, какую команду Вы хотите отправить. Он перехватил пакет. Не получится ли у него изменить несколько байтов в зашифрованном пакете и заставить сервер выполнить _другую_ команду?
            7. Не получится ли у злоумышленника подобрать пакет, который пройдёт проверку, измеряя время от получения ICMP-пакета до отправки ответа на него? Если пакет, не прошедший проверку, отвергается, то следует рассмотреть случай, когда злоумышленник отправляет _два_ пакета, первый из которых содержит payload, требующий проверки, а второй является обычным ping'ом.
              0

              Спасибо, полезно. Я успел обеспокоиться только о получении ключа на основании предсказуемого содержимого пейлода. К мусору надо ещё время добавить и проверять, чтобы исключить реплей-атаку.

            0
            Это разные протоколы. ICMP\IGMP служат для выяснения, жив ли хост и для маршрута. TCP — надёэная пересылка больших объёмов данных. Кроме того, это протоколы разных уровней OSI
              0

              Одного, поэтому ICMP-пакет и не требует тройного рукопожатия. Но для модуля ядра это не должно быть проблемой, TCP-пакет можно принять и обработать в любом виде, без установки полноценного соединения.

                0
                В теории одного, согласно Wiki. Практически — разного, поскольку TCP connection relative, ICMP connectionless
                  0
                  Я ошибся, отвечая Вам. Семейство протоколов — одно AF_INET. Но уровни OSI РАЗНЫЕ. ICMP относится к сетевому уровню, TCP к транспортному. Вспомним прототип системного вызова socket(family, type, proto). family в нашем случае равно AF_INET. При создании socket ядро ищет по этой константе каталог протоколов. Остальные константы служат для уточнения поиска. Подробнее можно почитать здесь sock-raw.org/papers/sock_raw
                    0

                    IP — сетевой уровень. TCP, UDP, ICMP работают поверх IP — на транспортном уровне.

            +1
            >Иначе у вас, на выбор, ничего не запустится или вы получите kernel panic.

            От чего это зависит?
              +1

              Если в хуке вызывать юзерспейс-программу не ожидая её выполнения, с аргументом UMH_NO_WAIT, то ничего не произойдёт. Думаю, что хук завершается вместе со всеми потомками. Если вызывать с UMH_WAIT_EXEC или UMH_WAIT_PROC, то ядро крашится.

              0
              А команда выполнится, если пакет дропнуть в конце хука?
                0

                Должна. Фунцкия с запуском будет уже поставлена в очередь планировщика, но клиентская часть точно зависнет, потом учто ожидает ответного пакета.

                +3
                MAX_CMD_LEN 1976 — каков максимальный размер ICMP payload? Подозреваю, что немного меньше.
                Было бы интересно увидеть реализацию отправки «ответа» в icmp reply.
                Реализовать полноценное шифрование в данной схеме — задача куда более сложная. Но почему бы и нет?
                И, самое главное, в боевых условиях проверяли? Будет ли оно работать когда уже свалился shh? Подозреваю, что вызов /bin/sh не пройдет.
                  0
                  каков максимальный размер ICMP payload?

                  Сам ICMP-пакет по размерам не ограничен, но есть ограничения на IP-пакет, 65535 байт. Из этого вычитаем 20 байт на заголовок IP-пакета и 8 байт на заголовок ICMP-пакета, получается намного больше, чем моё ограничение.


                  Было бы интересно увидеть реализацию отправки «ответа» в icmp reply.

                  Думаю, что это технически невозможно. Как раз потому что ядро крашится, если ждать даже начала выполнения команды.


                  Будет ли оно работать когда уже свалился shh?

                  Конкретно это не проверял, /bin/sh никак не зависит от SSH-сервера. Кроме того, SSH работает по TCP, а в моём модуле TCP никак не затрагивается.

                    0
                    Сам ICMP-пакет по размерам не ограничен, но есть ограничения на IP-пакет, 65535 байт.

                    Насколько я понимаю, это не совсем так. Есть ограничение на ethernet пакет, там что-то порядка 1500 байт. IP поверх него умеет фрагметировать/собирать пакеты большего размера, но врят ли это коснется вашего icmp кетчера.
                    Думаю, что это технически невозможно. Как раз потому что ядро крашится, если ждать даже начала выполнения команды.

                    Сохраните вывод команды в некий буфер и при следующем icmp echo отправьте его назад.
                    Конкретно это не проверял, /bin/sh никак не зависит от SSH-сервера.

                    Когда у меня на виртуалке заканчивается оперативная память вместе с буфером подкачки, то /bin/sh уже не открывается. Подозреваю, что где то тут же сдохнет и ssh. Ваша идея призвана бороться с подобными случаями. Она с ними справляется? Просто подключая вызов внешней утилиты вы убиваете саму идею обработки команды в ядре. Имеет ли это хоть какой нибудь смысл? С тем же успехом можно было написать свой простейший sshtelnet в user space и использовать его в крайних случаях. Отказоустойчивость, вероятно, оказалась бы на том же уровне.
                      0
                      Есть ограничение на ethernet пакет

                      Да, MTU, про него не думал. Если IP-пакет собирается ядром прежде чем попадёт в нетфильтр, то всё будет хорошо. Возможно, зависит от приоритета, надо проверять.


                      при следующем icmp echo отправьте его назад

                      Думаю, можно дропнуть этот пакет и потом скрафтить следующий, с соответствующими заголовками, чтобы клиент принял его за родной. Это усложнит сам модуль и потребует непредсказуемого с моим уровнем знаний времени на разработку. Идея всё же хорошая, спасибо.


                      Когда у меня на виртуалке заканчивается оперативная память вместе с буфером подкачки, то /bin/sh уже не открывается.

                      Подозреваю, что в этом случае и сама команда рискует не выполниться. Команда ведь всё же не в ядре выполняется, ядро запускает процесс в юзерспейсе. Пока я имел в виду только высокий LA, на случай нехватки памяти, проще будет добавить ключ sysrq:, который будет в обход юзерспейса триггерить нужный обработчик. Там есть и вызов OOM и убийство всех процессов кроме инита.

                        0

                        Телнету надо максимальный приоритет выставить найсом и ионайсом, тогда будет работать.

                    0

                    Все модули должны лежать в /lib/modules, не такой уж незаметный бэкдор.

                      0

                      Проверил файндом, ничего не нашлось. Там лежат модули из пакета и из DKMS, а загружаемое простым insmod остаётся только в памяти. Заметно будет в выводе lsmod.

                      0

                      Постойте, а разве более-менее сетевая flow-based железка по дороге не заменит содержимое нагрузки на random?

                        0

                        У меня до виртуалки ничего не подменялось. Есть сведения о железках, которые точно так делают?

                          0
                          Провел эксперимент:
                          ping ngs.ru -s 1000

                          Посмотрел wireshark'ом что уходит и что приходит. Полное совпадение.
                            0

                            Большинство мэ анализирует содержимое нагрузки в том числе и icmp. Так что есть вероятность, что в корпоративной сети не взлетит.
                            Так же всякие системы DDos очень нервно могут реагировать на содержимое ICMP и начать дропать трафик. Понятно, что все зависит от настроек…

                              0

                              Боюсь, что и провайдеры со своими DPI и прочими погремушками могут манипулировать icmp-ями в полный рост.


                              И вообще, тестирование сетевых штук на виртуалке, установленной на локалхосте, не покажет все богатство красок. Тот же STP на порте, например.

                                0

                                Я тестировал на удалённой. Повезло, но могло быть интересней.

                                +2

                                Никакие файрволлы не должны манипулировать содержимым пакетов, разве что только если нужен NAT — но и то с ограниченным числом протоколов.


                                Суть ICMP echo как раз в том что отправитель получит ровно то что отправил, иначе это сломает много тулзов. Не пустить пакет — другое дело, но менять содержимое — никак.


                                А вообще идея не нова — уже был модуль ping-sysrq, да и похожие типа icmp shell.

                                +2

                                Не специалист в ядерной разработке, но не будет ли тут гонки при работе с cmd_string? Чтение и запись туда ведь никак не синхронизируется, на первый взгляд.

                                  0

                                  Не понимаю проблему, я даже в C не специалист. cmd_string может перезаписываться разными ICMP-пакетами?

                                    +2

                                    Да, речь в том числе и про это.
                                    Может произойти следующая ситуация:
                                    Пришел 1й icmp-пакет, в cmd_string записалось, скажем, echo hello world и в планировщик поставилась задача.
                                    В момент обработки 2го пакета в cmd_string начало записываться, например killall ssh и "в это же время" планировщик начал выполнять задачу на запуск команды, где из cmd_string началось считывание. И считаться может, к примеру echoall ssh (т.е. совсем не то, что ожидалось изначально).
                                    Похоже на то, что тут нужна некая очередь команд, взятие и добавление элемента в которую будет происходить атомарно.

                                      0
                                      Да вполне возможно что это и произойдет, особенно если послать 2-3 пакета сразу может сегфолтнуться
                                        0

                                        В случае с ядром будет сходу kernel panic.

                                        0

                                        Интересно, спасибо. Подумаю над этим, когда чуть лучше изучу язык.

                                          0

                                          Тут не столько язык, сколько проблема конкурентного доступа к памяти. И всплывает она во многих языках.

                                            +1
                                            Никогда ничего не писал для ядра линукса, но предположу, что в структуре work_struct должно быть поле типа void*, в которое можно положить произвольные данные. Ещё не видел API на C, в котором бы принимался колбэк без подобного параметра. Единственное неудобство: придётся динамически выделять (а потом освобождать) память.
                                              0

                                              Интересно. Мне всегда казалось, что void говорит о том, что никаких данных не подразумевается.

                                                0
                                                Просто void — да. Но void* — это указатель без конкретного типа. Любая функция, которая принимает void*, принимает любой указатель, хоть int*, хоть char*, хоть массив из структур.
                                      0
                                      Хакеры оценят. Интересно мой код патченого su для андроида уже помечается как вирус?
                                        0
                                        Вспомнился ICMP Proxy, который в 2002(!) году победил в конкурсе на лучшую программу в журнале «Хакер». А я тогда занял второе место, хнык…
                                          +1
                                          Хабр торт!
                                          The code made me cry.
                                            0
                                            справедливо заподозрив скам

                                            Что это?

                                              0

                                              Всяческое мошенничество. Меня заподозрили в том, что я пишу бэкдор для вредоносной деятельности.

                                              +1
                                              Любой фрагментированный ICMP echo, и получаем kernel panic или перезапись памяти ядра.
                                              Не помешает изучить зачем нужны и как работают skb_header_pointer(), skb_copy_bits() и skb_may_pull().
                                              Déjà Vu: lwn.net/Articles/225946
                                                0

                                                Обязательно, спасибо! Про фрагментированные echo-пакеты я даже не знал.

                                                  0
                                                  Как понять фрагментированный ICMP echo?
                                                    0
                                                    IP протокол поддерживает фрагментацию пакетов. Т.е. один пакет размером до 64к будет разбит и собран автоматически на кусочки по 1500 байт (примерный максимум для ethernet). ICMP реализован поверх IP, следовательно и ICMP пакет может быть разбит на несколько ethernet пакетов (если не влезет в один).
                                                      0
                                                      Почему

                                                      «Любой фрагментированный ICMP echo, и получаем kernel panic или перезапись памяти ядра.»


                                                      ?
                                                        0
                                                        Думаю, об этом лучше почитать в указанном выше Déjà Vu
                                                    0
                                                    Почему

                                                    «Любой фрагментированный ICMP echo, и получаем kernel panic или перезапись памяти ядра.»


                                                    ?
                                                    +2
                                                    А почему бы просто не закрыть это асинхронной криптографией? Заодно и шифрование протокола будет бесплатно.
                                                      0

                                                      Кажется, что синхронная будет проще. Если есть примитивы для асинхронной, то можно и так. Мне кажется, есть возможность для атаки, команды отправляемые на сервер в критичной ситуации однообразны, мало энтропии в полезных данных, поэтому в идеале надо ещё и мусорную нагрузку добавлять.

                                                        0

                                                        Вероятно, вы все таки имели ввиду асимметричное шифрование, а не асинхронное?

                                                          0

                                                          Точно, как-то я и не заметил подмены.

                                                            0
                                                            Точно, простите. Слишком много питонячьего бэкенда было у меня в тот день…

                                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                        Самое читаемое