일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- pwncollege
- css
- 컴퓨터구조
- 리눅스
- write up
- 리눅스커널
- Bandit
- 커널
- 시스템 프로그래밍
- 시스템프로그래밍
- 워게임
- kernel
- 리눅스 커널
- pwn.college
- 시스템
- radare2
- px4
- Pwnable.kr
- 드론
- C++
- 시그널
- 취약점
- 어셈블리어
- 리버싱
- 프로그래밍
- Leviathan
- wargame
- 알고리즘
- C언어
- 시스템해킹
- Today
- Total
Computer Security
#14 ret2usr 기법 본문
ret2usr(return to user)
SMEP 보호 기법이 걸려 있지 않을 경우, 커널 공간에서 유저 공간의 코드를 실행할 수 있다는 것을 이용해서 권한 상승을 일으키는 기법이다.
대부분의 커널 공격 테크닉의 기본이 되는 기법이므로 완벽히 이해하여 자유자재로 사용할 줄 알아야 한다.
우리는 아래의 파일들로 실습 할 것이다.
start.sh : qemu script
test.c : 간단한 취약점이 터지는 디바이스 드라이버 예제
exp.c : ret2usr 기법을 이용해 권한 상승을 일으키는 exploit code
1. start.sh 를 살펴보자.
KASLR : 적용 X
SMEP : 적용 X
SMAP : 적용 X
2. 커널의 흐름을 원하는 대로 조작할 수 있는 디바이스 드라이버 예제인 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() 함수
커널 공간에서 성공적으로 권한 상승을 일으킨 뒤, 쉘을 실행하기 위해 유저 공간으로의 전환에 필요한 현재의 context를 저장하는 역할을 한다.
context를 저장하는 이유:
- 하나의 태스크의 가상메모리는 커널공간과 유저공간으로 나눌 수 있다.
- 즉, 태스크가 실행되면서 커널공간과 유저공간 사이의 모드전환이 주기적으로 일어나게 된다.
- 이때, 각 모드간의 필요한 레지스터 값이 조금씩 다르다.
- 예를들어, 스택을 가르키는 rsp 같은경우 커널모드일때는 커널스택, 유저모드일때는 유저스택을 가르켜야한다.
payload() 함수
권한 상승을 일으키는 커널 코드,swapgs와 iretq명령, backup_rv() 함수를 호출해 저장한 contex로 payload를 구성하는 역할을 한다.
5. 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() 함수의 흐름
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를 실행한 후, 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 |