# KASLR ( Kernel Address Space Layout Randomization )
2013년, 커널 영역에 대한 주솟값 랜덤 매핑이 적용되었다
때문에 Kernel exploit을 하기위해서는 유저모드의 libc base를 leak하는 것과 같이
kernel base 의 address leak이 필요하게 되었다
위 예시와 같이 커널 함수들의 주소는 앞 4byte가 0xffffffff로 패딩되어있으며
하위 3byte를 수정함으로써 커널 함수들을 사용할 수 있다
* 커널에는 모듈도 지원을 하는데 이 모듈들의 주솟값도 얻게되면 디버깅이 가능하다
당연하게도 단순히 주솟값을 랜덤화한 것뿐이기 때문에
exploit이 힘들어질지는 몰라도 leak된다면 kernel exploit이 가능해진다
# KAISER( Kernel Address Isolation to have Side-channels Efficiently Removed )
meltdown 취약점이 발생하기 이전에 고안된 커널보호기법으로
kernel 모드와 user 모드 시 접근가능한 영역을 다르게함으로써
사용자가 user 모드에서의 커널메모리 접근을 하지못하도록 한다
Even with Meltdown, KAISER can avoid having any kernel pointers on memory locations that are mapped in the user space which would leak information about the randomized offsets. This would require trampoline locations for every kernel pointer, .e., the interrupt handler would not call into kernel code directly, but through a trampoline function. The trampoline function must only be mapped in the kernel. It must be randomized with a different offset than the remaining kernel. Consequently, an attacker can only leak pointers to the trampoline code, but not the randomized offsets of the remaining kernel.
- https://meltdownattack.com/meltdown.pdf
커널 영역의 메모리를 페이징하는 시점에 어떠한 작업을 하는 것으로 보인다
나는 KPTI에 더 관심있으니 넘어가도록 하겠다
? ? ?
# KPTI ( Kernel page-table isolation )
널려진 자료에서는 KPTI 기술이 KAISER를 기반으로 구현되었다고 설명되어있다
왼쪽이 KASLR 보호기법 당시 모드별 접근 가능한 영역이고
오른쪽이 KPIT로 인해 user 모드 시 접근 가능한 kernel space이 대폭줄어든 모습이다
개발&보안 관측에서 이론적인 내용은 세상에 많이 있기 때문에
내가 직접 경험한 exploit 관점 KPTI의 행동을 설명해보면 다음과 같다
먼저 유저영역에서 커널 영역의 모듈을 호출했다고 가정하자
해당 모듈을 사용하기 위해 유저 모드에서 커널 모드로 진입하여
커널 영역의 모듈을 실행해줄 것이다
이후 해당 모듈의 실행이 종료되고 나서는
다시 커널 모드에서 유저 모드로 되돌리는 작업이 발생한다
이 때 KPTI가 적용되어있는 커널의 경우에 한해서
되돌아오는 부분에서 break point를 걸고 디버깅을 해보면
아래 그림과 같은 과정을 진행하게 된다
cr3 레지스터의 값을 or 연산을 통해 수정하는 것이 보이는데
이 작업을 통해서 유저 영역과 커널 영역의 메모리를 구분하는 것으로 보인다
즉, KPTI 가 적용되어있을 경우에
cr3 레지스터의 값을 수정함으로써
유저 모드에서 접근가능한 메모리의 범위와
커널 모드에서 접근가능한 메모리의 범위를 다르게해서
유저 모드로 커널 영역의 메모리를 접근할 수 없도록 한다
그렇다면 cr3 레지스터는 무엇일까? 하고 알아보면
cr3 레지스터는 페이징 테이블을 관리하는 레지스터이다
또한 위 사진에서 or cr3, 0x1000 연산을 하는 것이 보이는데
해당 연산을 통해 상위레벨 구조의 주솟값이 변경된다
만약 커널 ROP를 진행할 경우에 PTI가 적용되어있다면
해당 과정을 통해 커널 모드에서 유저 모드로 무사히 넘어올 수 있도록
cr3레지스터값을 어떻게든 수정해야만 할 것이다
만약 적용되어있지 않다면 유저모드로 돌아온 시점에서도
커널 영역의 메모리를 참조하여 ROP와 같은 것을 진행할 수 있을 것이다
* 추가 내용
PTI가 적용되어있는 CPU는 KVM 이다
QEMU와 KVM은 둘 다 CPU의 한 종류이지만
기술적으로 다른 기능을 내포하는 부분이 있기때문에 분류되어진다
cat /proc/cpuinfo 를 통해서 cpu의 상태를 볼 수 있는데
이 때 flags 값들을 보면 두 cpu가 상이하다는 것을 볼 수 있고
KVM cpu에만 PTI 가 적용된 것을 볼 수 있다
* qemu는 에뮬레이터이고 , kvm은 하드웨어의 가상화이다
# Exploit Aspect
위 KPTI 분석을 통해서 Kernel Stack ROP를 진행하게 된다면
cr3 레지스터의 값을 조작함으로써
커널 PGD에서 유저 PGD로 전환해야함을 인지했다
(* PGD: Page Global Directory)
그러나 이 cr3레지스터 값을 변조해주는 것만으로는 손쉽게 ROP할 수 없다
그 이유는 추가적인 PTI가 제공하는 보호기법이 존재하기 때문인데
바로 커널모드에서 유저영역의 코드를 실행할 수 없다는 것이다
이 보호기법은 다른 커널 보호기법 중 하나인 SMEP의 역할과 동일하다
SMEP은 Superviser Mode Execution Protection의 약자로
커널모드에서 유저영역 메모리 코드에 실행권한을 삭제한다
때문에 커널모드에서 유저영역 메모리 자체가 매핑이 되어있더라도
해당 메모리에 적힌 코드를 실행할 수는 없는 것이다
결과적으로 커널모드에서 유저영역의 코드에 존재하는 ROP gadget을 사용하여 exploit 하는 것은 불가능하다
그렇다면 커널모드에서 다시 유저모드로 돌아온 상태에서
ROP를 진행해야하는 것일까라고 생각해보면
ROP payload자체는 커널영역에 적힌 값들이므로
당연하게도 유저모드로 돌아간 시점에서 커널 스택 주솟값을 참조하기 때문에
pop과 같은 gadget을 진행하면 segment fault가 발생할 것이다
그럼 Exploit 할 수 없는 것일까?
'System Hacking > Study Notes' 카테고리의 다른 글
Seccomp (0) | 2020.09.18 |
---|---|
Unsorted bin attack (0) | 2020.09.18 |
[ Kernel ] Meltdown 취약점 (0) | 2020.08.16 |
어셈블리어 구구단 만들기 (3) | 2020.07.01 |
개인 서버 만들기 (with. GCP) (0) | 2020.06.16 |