Computer Security

#14 ret2usr 기법 본문

리눅스 커널 해킹

#14 ret2usr 기법

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

ret2usr(return to user)

 

SMEP 보호 기법이 걸려 있지 않을 경우, 커널 공간에서 유저 공간의 코드를 실행할 수 있다는 것을 이용해서 권한 상승을 일으키는 기법이다.

 

대부분의 커널 공격 테크닉의 기본이 되는 기법이므로 완벽히 이해하여 자유자재로 사용할 줄 알아야 한다.

 

 

 


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

ret2usr

start.sh : qemu script

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

exp.c : ret2usr 기법을 이용해 권한 상승을 일으키는 exploit code

 

 

 


1. start.sh 를 살펴보자.

cat start.sh

KASLR : 적용 X

SMEP : 적용 X

SMAP : 적용 X

 

 

 


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

test.c

test_init() 함수에서 misc 디바이스를 등록하여 /dev 경로에 test라는 디바이스가 생성되는 것을 알 수 있다.

 

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

 

 

 

 


3.exp.c 코드의 main() 함수 부분을 살펴보자.

/dev/test 장치를 open()

현재의 context(1)를 저장하는 backup_rv()함수를 호출한다.

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

commit_creds()함수와 prepare_kernel_cred() 함수의 주소는 KADR 우회 방식으로 구한다.

 

 

(1) : context : 레지스터의 현재의 레이아웃을 의미한다.

 

 

 


4. exp.c 코드의 backup_rv() 함수와 payload()함수 부분이다.

backup_rv(), payload()

backup_rv() 함수

 

커널 공간에서 성공적으로 권한 상승을 일으킨 뒤, 쉘을 실행하기 위해 유저 공간으로의 전환에 필요한 현재의 context를 저장하는 역할을 한다.

 

 

context를 저장하는 이유:

  • 하나의 태스크의 가상메모리는 커널공간과 유저공간으로 나눌 수 있다.                                         
  • 즉, 태스크가 실행되면서 커널공간과 유저공간 사이의 모드전환이 주기적으로 일어나게 된다.                                      
  • 이때, 각 모드간의 필요한 레지스터 값이 조금씩 다르다.
  • 예를들어, 스택을 가르키는 rsp 같은경우 커널모드일때는 커널스택, 유저모드일때는 유저스택을 가르켜야한다.

 

payload() 함수

 

권한 상승을 일으키는 커널 코드,swapgs와 iretq명령, backup_rv() 함수를 호출해 저장한 contex로 payload를 구성하는 역할을 한다.

 

 

 


5. backup_rv() 함수를 자세히 살펴보자.

backup_rv()

RIP
CS
RFLAGS
RSP
SS

커널 공간에서 유저 공간으로의 전환에 필요한 context는 표와 같이 5개의 레지스터가 필요하다.

 

RIP 레지스터를 제외한 레지스터들은 현재 상황의 레지스터들을 저장하고, RIP는 /bin/sh를 실행하는 함수인 shell()함수의 주소를 저장한다.

 

RSP를 저장하는 이유는 커널공간에서는 kernel stack을 사용하고 유저공간에서는 stack을 사용하기에, 커널 공간에서 유저 공간으로의 전환이 일어날 때 RSP를 유저 공간의 stack으로 교체해줘야만 kernel panic이 일어나지 않고 쉘이 실행 되기 때문이다.

 

 

 


6. payload() 함수를 자세히 살펴보자.

payload()

payload() 함수의 흐름

 

1. commit_creds(prepare_kernel_cred(0))    : 권한 상승을 일으키는 commit_creds 코드를 실행한다.

 

2. swapgs(1) 명령을 호출한다.

 

3. RSP를 backup_rv()함수를 이용해 저장한 context가 있는 유저 공간 stack으로 설정한다.

 

4. iretq 명령을 호출해 유저 공간으로 복귀해서 shell() 함수를 실행한다.

 


(1) swapgs : KerenelGSbase의 값을 GS.base의 값으로 교환하여 시스템 소프트웨어가 시스템 데이터 구조에 대한 포인터를 로드하는 빠른 방법을 제공한다.

 

(2)iretq : 커널 공간에서 사용자 공간으로 전환할 수 있는 명령

 

이 두명령을 실행해야 kernel panic이 일어나지 않고 성공적으로 권한 상승을 일으킬 수 있다.

 

 

 


7. ./start.sh 를 실행해보자.

./start.sh

start.sh를 실행한 후, exp를 실행한 결과이다.

 

user권한에서 root권한을 획득 한 것을 볼 수 있다.

 

 

 


전체적인 흐름

 

 

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

 

 

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

--> 유저공간 write(fd, &ptr, sizeof(ptr))   --> 커널공간 test_write() 

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

 

 

 


ret2usr 최종 정리

 

 

이 기법은 SMEP와 SMAP가 걸려있지 않은 환경에서 사용할 수 있다.

 

커널 취약점을 이용해 rip control이 가능할 때,  payload를 구성한 뒤 해당 payload로 rip를 제어하면 권한 상승이 가능하다.

 

payload는 commit_creds(pre..-> swapgs -> context 준비 -> iretq  순서대로 구성되어야 한다.

 

반응형

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

#16 cr4 overwrite 기법  (0) 2022.08.24
#15 kernel ROP 기법  (0) 2022.08.23
#13 KPTI 우회  (0) 2022.08.21
#12 SSP 우회  (0) 2022.08.20
#11 KADR 우회  (0) 2022.08.19
Comments