4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / remote_key_agreement.c C
/*
 * remote_key_agreement.c - Remote Key Agreement via Network Timing
 * 
 * Uses CVE-2023-1206 (IPv6 hash collision) timing side-channel for
 * establishing a shared secret between two hosts over the network.
 * 
 * Protocol:
 * 1. Both parties connect to each other (or a shared server)
 * 2. For each key bit, both parties simultaneously:
 *    - Generate a random bit locally
 *    - If bit=1: flood the network (cause congestion)
 *    - If bit=0: stay quiet
 * 3. Both measure network latency during each round
 * 4. High latency = at least one party sent 1
 * 5. Low latency = both sent 0
 * 6. Use XOR of measurements as shared entropy
 * 
 * This creates shared randomness because:
 * - If both flood: high latency (both know: likely both sent 1)
 * - If one floods: medium latency (uncertainty)
 * - If neither floods: low latency (both know: both sent 0)
 * 
 * The timing measurements become correlated, providing shared entropy
 * even without revealing individual bits directly.
 *
 * Alternative protocol (more robust):
 * - Use NIST randomness beacon as seed
 * - Use network RTT measurements to derive additional entropy
 * - Combine with Diffie-Hellman for hybrid approach
 *
 * Author: Vlad (PwnCTF Research)
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <poll.h>

#define KEY_BITS 256
#define KEY_BYTES (KEY_BITS / 8)
#define SYNC_PORT 31337
#define DATA_PORT 31338
#define FLOOD_PORT 31339

#define ROUND_DURATION_MS 100
#define FLOOD_PACKETS 1000
#define LATENCY_THRESHOLD_US 500  /* Threshold for detecting flood */

static volatile int running = 1;

void signal_handler(int sig) { (void)sig; running = 0; }

static inline uint64_t get_us(void)
{
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
}

/*
 * ============================================================================
 * Network Timing Measurement
 * ============================================================================
 */

/* Measure RTT to remote host */
uint64_t measure_rtt(int sock)
{
    uint8_t ping = 0xAA;
    uint8_t pong;
    
    uint64_t t1 = get_us();
    
    if (send(sock, &ping, 1, 0) != 1) return 0;
    if (recv(sock, &pong, 1, 0) != 1) return 0;
    
    uint64_t t2 = get_us();
    return t2 - t1;
}

/* Send flood packets to cause congestion */
void send_flood(const char *target_ip, int duration_ms)
{
    int sock = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sock < 0) {
        sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (sock < 0) return;
        
        struct sockaddr_in addr = {
            .sin_family = AF_INET,
            .sin_port = htons(FLOOD_PORT),
        };
        inet_pton(AF_INET, target_ip, &addr.sin_addr);
        
        uint8_t junk[64];
        memset(junk, 0x41, sizeof(junk));
        
        uint64_t end_time = get_us() + duration_ms * 1000;
        while (get_us() < end_time) {
            for (int i = 0; i < FLOOD_PACKETS; i++) {
                sendto(sock, junk, sizeof(junk), 0, 
                       (struct sockaddr *)&addr, sizeof(addr));
            }
        }
        close(sock);
        return;
    }
    
    /* IPv6 flood for CVE-2023-1206 */
    struct sockaddr_in6 addr6 = {
        .sin6_family = AF_INET6,
        .sin6_port = htons(FLOOD_PORT),
    };
    inet_pton(AF_INET6, target_ip, &addr6.sin6_addr);
    
    uint8_t junk[64];
    memset(junk, 0x41, sizeof(junk));
    
    uint64_t end_time = get_us() + duration_ms * 1000;
    while (get_us() < end_time) {
        for (int i = 0; i < FLOOD_PACKETS; i++) {
            sendto(sock, junk, sizeof(junk), 0,
                   (struct sockaddr *)&addr6, sizeof(addr6));
        }
    }
    
    close(sock);
}

/*
 * ============================================================================
 * Key Agreement Protocol
 * ============================================================================
 */

typedef struct {
    int ctrl_sock;           /* Control socket for coordination */
    char peer_ip[64];        /* Peer IP address */
    uint8_t key[KEY_BYTES];  /* Derived key */
    int is_initiator;        /* Are we the initiator? */
} key_ctx_t;

/* Synchronize with peer for next round */
int sync_round(key_ctx_t *ctx, int round)
{
    uint32_t msg = htonl(round);
    uint32_t peer_round;
    
    if (ctx->is_initiator) {
        send(ctx->ctrl_sock, &msg, sizeof(msg), 0);
        recv(ctx->ctrl_sock, &peer_round, sizeof(peer_round), 0);
    } else {
        recv(ctx->ctrl_sock, &peer_round, sizeof(peer_round), 0);
        send(ctx->ctrl_sock, &msg, sizeof(msg), 0);
    }
    
    return (ntohl(peer_round) == (uint32_t)round) ? 0 : -1;
}

/* Exchange our random bit (encrypted with temporary value) */
int exchange_commitment(key_ctx_t *ctx, int our_bit, int *peer_bit)
{
    /* 
     * Commitment scheme:
     * 1. Generate random nonce
     * 2. Send hash(bit || nonce)
     * 3. After both committed, reveal bit and nonce
     * 4. Verify commitments
     * 
     * Simplified for demo: just exchange bits directly
     * (In real implementation, use proper commitment)
     */
    uint8_t msg = our_bit ? 1 : 0;
    uint8_t peer_msg;
    
    if (ctx->is_initiator) {
        send(ctx->ctrl_sock, &msg, 1, 0);
        recv(ctx->ctrl_sock, &peer_msg, 1, 0);
    } else {
        recv(ctx->ctrl_sock, &peer_msg, 1, 0);
        send(ctx->ctrl_sock, &msg, 1, 0);
    }
    
    *peer_bit = peer_msg ? 1 : 0;
    return 0;
}

/* Single round of key agreement */
int key_round(key_ctx_t *ctx, int bit_index, int verbose)
{
    /* Generate random bit */
    int our_bit = rand() & 1;
    
    /* Sync with peer */
    if (sync_round(ctx, bit_index) < 0) {
        fprintf(stderr, "Sync failed at bit %d\n", bit_index);
        return -1;
    }
    
    /* Measure baseline RTT */
    uint64_t baseline = measure_rtt(ctx->ctrl_sock);
    
    /* Both parties: if bit=1, flood; if bit=0, quiet */
    pthread_t flood_thread;
    int flooding = 0;
    
    if (our_bit) {
        flooding = 1;
        /* Start flood in background */
        pthread_create(&flood_thread, NULL, 
            (void *(*)(void *))send_flood, 
            (void *)ctx->peer_ip);
    }
    
    /* Wait for round duration and measure latency */
    usleep(ROUND_DURATION_MS * 1000 / 2);
    uint64_t latency = measure_rtt(ctx->ctrl_sock);
    usleep(ROUND_DURATION_MS * 1000 / 2);
    
    if (flooding) {
        pthread_cancel(flood_thread);
        pthread_join(flood_thread, NULL);
    }
    
    /* Exchange bits (in real protocol, use commitment scheme) */
    int peer_bit;
    exchange_commitment(ctx, our_bit, &peer_bit);
    
    /* 
     * Key derivation:
     * - XOR of both bits gives unpredictable result
     * - Latency measurement provides additional entropy
     * - Combined: key_bit = (our_bit XOR peer_bit) XOR (latency > threshold)
     */
    int latency_bit = (latency > baseline + LATENCY_THRESHOLD_US) ? 1 : 0;
    int key_bit = our_bit ^ peer_bit ^ latency_bit;
    
    /* Store key bit */
    int byte_idx = bit_index / 8;
    int bit_pos = bit_index % 8;
    if (key_bit) {
        ctx->key[byte_idx] |= (1 << bit_pos);
    }
    
    if (verbose) {
        printf("[Round %3d] our=%d peer=%d latency=%lu (base=%lu) -> key_bit=%d\n",
               bit_index, our_bit, peer_bit, latency, baseline, key_bit);
    }
    
    return 0;
}

/*
 * ============================================================================
 * Network Setup
 * ============================================================================
 */

/* Connect to peer (initiator mode) */
int connect_to_peer(const char *host, int port)
{
    struct addrinfo hints = {
        .ai_family = AF_UNSPEC,
        .ai_socktype = SOCK_STREAM,
    };
    struct addrinfo *result;
    char port_str[16];
    snprintf(port_str, sizeof(port_str), "%d", port);
    
    if (getaddrinfo(host, port_str, &hints, &result) != 0) {
        perror("getaddrinfo");
        return -1;
    }
    
    int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (sock < 0) {
        perror("socket");
        freeaddrinfo(result);
        return -1;
    }
    
    /* Set TCP_NODELAY for accurate timing */
    int flag = 1;
    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
    
    if (connect(sock, result->ai_addr, result->ai_addrlen) < 0) {
        perror("connect");
        close(sock);
        freeaddrinfo(result);
        return -1;
    }
    
    freeaddrinfo(result);
    return sock;
}

/* Listen for peer (responder mode) */
int accept_peer(int port)
{
    int listen_sock = socket(AF_INET6, SOCK_STREAM, 0);
    if (listen_sock < 0) {
        listen_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (listen_sock < 0) {
            perror("socket");
            return -1;
        }
        
        struct sockaddr_in addr = {
            .sin_family = AF_INET,
            .sin_port = htons(port),
            .sin_addr.s_addr = INADDR_ANY,
        };
        
        int opt = 1;
        setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        
        if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
            perror("bind");
            close(listen_sock);
            return -1;
        }
    } else {
        struct sockaddr_in6 addr6 = {
            .sin6_family = AF_INET6,
            .sin6_port = htons(port),
            .sin6_addr = in6addr_any,
        };
        
        int opt = 1;
        setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
        setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
        
        if (bind(listen_sock, (struct sockaddr *)&addr6, sizeof(addr6)) < 0) {
            perror("bind");
            close(listen_sock);
            return -1;
        }
    }
    
    listen(listen_sock, 1);
    printf("[Server] Listening on port %d...\n", port);
    
    struct sockaddr_storage peer_addr;
    socklen_t peer_len = sizeof(peer_addr);
    int sock = accept(listen_sock, (struct sockaddr *)&peer_addr, &peer_len);
    
    close(listen_sock);
    
    if (sock < 0) {
        perror("accept");
        return -1;
    }
    
    /* Set TCP_NODELAY */
    int flag = 1;
    setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
    
    printf("[Server] Peer connected\n");
    return sock;
}

/*
 * ============================================================================
 * Main
 * ============================================================================
 */

void print_key(const uint8_t *key, int bytes)
{
    printf("Key (%d bits): ", bytes * 8);
    for (int i = 0; i < bytes; i++) {
        printf("%02x", key[i]);
    }
    printf("\n");
}

void print_usage(const char *prog)
{
    printf("Usage: %s [options]\n", prog);
    printf("\nRemote Key Agreement via Network Timing Side-Channel\n");
    printf("\nOptions:\n");
    printf("  -c HOST     Connect to HOST (initiator mode)\n");
    printf("  -l          Listen for connection (responder mode)\n");
    printf("  -p PORT     Port number (default: %d)\n", SYNC_PORT);
    printf("  -b BITS     Key bits (default: %d)\n", KEY_BITS);
    printf("  -v          Verbose output\n");
    printf("  -h          Show help\n");
    printf("\nExample:\n");
    printf("  Host A: %s -l -v\n", prog);
    printf("  Host B: %s -c <host_a_ip> -v\n", prog);
}

int main(int argc, char *argv[])
{
    int mode = 0;  /* 0=help, 1=initiator, 2=responder */
    char *peer_host = NULL;
    int port = SYNC_PORT;
    int num_bits = KEY_BITS;
    int verbose = 0;
    int opt;
    
    while ((opt = getopt(argc, argv, "c:lp:b:vh")) != -1) {
        switch (opt) {
        case 'c':
            mode = 1;
            peer_host = optarg;
            break;
        case 'l':
            mode = 2;
            break;
        case 'p':
            port = atoi(optarg);
            break;
        case 'b':
            num_bits = atoi(optarg);
            break;
        case 'v':
            verbose = 1;
            break;
        case 'h':
        default:
            print_usage(argv[0]);
            return 0;
        }
    }
    
    printf("╔════════════════════════════════════════════════════════════════╗\n");
    printf("║  Remote Key Agreement via Network Timing                       ║\n");
    printf("║  Uses CVE-2023-1206 timing side-channel                        ║\n");
    printf("╚════════════════════════════════════════════════════════════════╝\n\n");
    
    if (mode == 0) {
        print_usage(argv[0]);
        return 0;
    }
    
    signal(SIGINT, signal_handler);
    srand(time(NULL) ^ getpid());
    
    key_ctx_t ctx;
    memset(&ctx, 0, sizeof(ctx));
    
    /* Establish connection */
    if (mode == 1) {
        printf("[Init] Connecting to %s:%d...\n", peer_host, port);
        ctx.ctrl_sock = connect_to_peer(peer_host, port);
        ctx.is_initiator = 1;
        strncpy(ctx.peer_ip, peer_host, sizeof(ctx.peer_ip) - 1);
    } else {
        ctx.ctrl_sock = accept_peer(port);
        ctx.is_initiator = 0;
        /* Get peer IP from socket */
        struct sockaddr_storage peer;
        socklen_t len = sizeof(peer);
        getpeername(ctx.ctrl_sock, (struct sockaddr *)&peer, &len);
        if (peer.ss_family == AF_INET) {
            inet_ntop(AF_INET, &((struct sockaddr_in *)&peer)->sin_addr,
                      ctx.peer_ip, sizeof(ctx.peer_ip));
        } else {
            inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&peer)->sin6_addr,
                      ctx.peer_ip, sizeof(ctx.peer_ip));
        }
    }
    
    if (ctx.ctrl_sock < 0) {
        fprintf(stderr, "Failed to establish connection\n");
        return 1;
    }
    
    printf("[Setup] Connected! Starting key agreement (%d bits)...\n\n", num_bits);
    
    /* Run key agreement rounds */
    for (int bit = 0; bit < num_bits && running; bit++) {
        if (key_round(&ctx, bit, verbose) < 0) {
            break;
        }
        
        if (!verbose && bit % 32 == 31) {
            printf("[Progress] %d/%d bits derived\n", bit + 1, num_bits);
        }
    }
    
    printf("\n[Complete] Key agreement finished!\n");
    print_key(ctx.key, num_bits / 8);
    
    close(ctx.ctrl_sock);
    return 0;
}