Computer Security

#8 KASLR 우회 본문

리눅스 커널 해킹

#8 KASLR 우회

쿠리 Kuri 2022. 8. 16. 18:30
반응형

KASLR(Kernel Address Space Layout Randomization)

 

커널이 무작위 주소에 매핑되도록 만들어, 커널 익스플로잇을 어렵게 하는 보호기법이다.

유저 공간의 ASLR(1) 과 비슷한 개념이다.

주로 커널이 실행되기 전 단계인 부트로더에서 셋팅된다.

우회하기 위해 kernel memory address leak 과정이 필요하다.

 

 

 

(1) ASLR(address Space Layout Randomization)

스택, 힙, 라이브러리 등의 주소를 랜덤한 영역에 배치하여, 공격에 필요한 target address를 예측하기 어렵게 만든다. 프로그램이 실행될 때마다 각 주소들이 변경된다.

 

(2)  kernel memory address leak

커널의 취약점을 이용해서, 커널의 메모리 주소를 유저공가능로 유출 하는 것

 


 

 

commit_creds(prepare_kernel_cred(0))

 

prepare_kernel_cred(0) 은 root credentials 를 준비하는 역할

commit_creds() 는 현재 태스크의 credentials 를 인자로 받은 credentials로 갱신하는 역할

즉, 현재 태스크의 권한을 root로 바꾸는 코드

커널 공격자들은 대부분 이 코드를 실행하는 것을 공격 목표로 한다.

 

 


 

우리는 아래의 파일들로 실습 할 것이다.

kaslr_bypass

start.sh : KASLR 을 적용한 qemu script

test.c : 간단한 취약점이 터지는 디바이스 드라이버 예제

exp.c : KASLR을 우회하여 권한 상승을 일으키는 exploit code

leak.c : 커널 메모리 주소를 leak 하는 code

 


1. start.sh 를 읽어서 걸려있는 보호 기법들을 확인 해보자.

cat start.sh

-append "kaslr" 옵션을 이용해서 KASLR 보호 기법을 적용한 qemu script 인것을 알 수 있다.

 

 

 


2. 커널의 흐름을 원하는 대로 조작할 수 있는 디바이스 드라이버 예제인 test.c 를 살펴보자.

test.c

test_read( )함수에서 copy_to_user 함수를 이용해 printk() 함수의 주소를 유저 공간으로 전달하는 것을 볼 수 있다.

 

test_write() 함수에서 copy_from_user 함수를 이용해 유저로부터 주소를 전달 받아, 그 주소를 실행하는 것을 볼 수 있다.

 

 

 


3. leak.c 코드를 살펴보자.

leak.c

 

커널 메모리 주소를 leak 하는 코드이다.

 

read() 함수를 이용해 test 드라이버로부터 printk() 함수의 주소를 전달 받은 뒤 출력한다.

 

 

 


4. ./start.sh 를 두 번 실행 시킨뒤, ./leak 파일을 각각 실행 해보자.

 

첫번째.

1

두번째

2

 

첫 번째와 두 번째의 실행에서 printk() 함수의 주소가 각각 0xffffffffbacbedb9 와 0xffffffffb70bedb9 로 서로 다른 것을 알 수있다.

 

즉, 커널이 부팅 될 때마다 주소가 바뀐 다는 부분을 알 수 있었다.

 

 

 


5. test 드라이버에서 터지는 취약점을 이용해 권한 상승을 일으키는 exploit code 인 exp.c 코드를 분석 해보자.

exp.c

write() 함수를 이용해 test 드라이버에 payload(1) 함수의 주소를 전달한다.

 

전체적인 흐름은 ret2usr 라는 기법을 이용한다.

 

여기서 중요한 점은, leak한 printk()주소를 이용해 kernel base를 구한 뒤, 이 kernel base에 필요한 커널 함수들의 offset을 더해 exploit에 이용하는 것이다.

이렇게 하면, 커널이 부팅될 때마다, 커널함수의 주소가 랜덤하게 바뀌더라도 원하는 커널함수를 호출할 수 있게 된다.

 

 

 

 

(1)  payload함수:  commit_ceds(prepare_kernel_cred(0)) 코드를 실행한 후, /bin/sh 를 실행하는 역할이다.


offset 주소 구하는 방법

/proc/kallsyms 파일은 커널의 모든 심볼의 주소를 담고 있는 파일이다.

 

KADR 보호기법에 의해 root 권한으로만 주소를 확인할 수 있다.

 

offset 계산과정 예시

 

<offset 계산 코드>

offset 계산 코드

<커널 내부적으로 확정된 예시 주소>

printk = 0xffffffff810bedb9

commit_creds = 0xffffffff8108e9f0

prepare_kernel_cred = 0xffffffff8108ec20

 

 

1. kernel base = 0xffffffff810bedb9 - 0xbedb9 = 0xffffffff81000000

 

2. commit_creds = 0xffffffff81000000 + 0x8e9f0 = 0xffffffff8108e9f0

 

3. prepare_kernel_cred = 0xffffffff81000000 + 0x8ec20 = 0xffffffff8108ec20

 

0xbedb9 , 0x8e9f0 , 0x8ec20 같은 offset은 커널이 재부팅 되어도 바뀌지 않는다.

 

 

 


6. start.sh 를 실행한뒤,  ./exp 를 실행해서 root권한을 획득 해보자.

./start.sh

 

KASLR 보호기법을 적용한 start.sh 를 실행한 후, exp를 실행한 결과

 

test드라이버의 취약점을 이용해 user 권한에서 root권한을 획들 할 수 있었다!

 

 

 


전체적인 흐름

 

 

 

Kernel address leak 과정

 

 

커널공간 : test.c     ,      유저공간 : exp.c

 

 

커널 공간 misc_register(&test_driver)   -->  유저공간 fd=open("/dev/test", O_RDWR)  --> 유저공간 read(fd,&leak,0)

--> 커널공간 test_read()  --> 커널공간 void*ptr = &printk --> 커널공간 copy_to_user(buf,&ptr,...)


권한 상승 과정

 

커널공간 : test.c      ,       유저공간 : exp.c

 

 

커널공간 copy_to_user(buf,&ptr,...)  -->  유저공간 offset 계산 --> 유저공간 write(fd,&ptr,sizeof(ptr))

--> 커널공간 test_wirte()  --> 커널공간 copy_from_user(&fp_expc,...)  --> 커널공간 fp_exec()

 

 

 


KASLR 우회 방법 총정리

 

다양한 information leak 취약점을 이용해 커널 코드 주소를 leak 한다.

 

leak 한 주소와 offset 을 기반으로 exploit에 사용할 커널 함수들의 주소를 구한다.

 

구한 커널 함수 주소를 이용해 exploit을 작성한다.

 

 

반응형

'리눅스 커널 해킹' 카테고리의 다른 글

#10 SMAP 우회  (0) 2022.08.18
#9 SMEP 우회  (0) 2022.08.17
#7 리눅스 커널 보호 기법  (0) 2022.08.15
#6 리눅스 CTF에서의 환경 셋팅  (0) 2022.08.14
#5 리눅스 qemu&gdb 셋팅  (0) 2022.08.13
Comments