Intro

커널 영역도 유저 영역처럼 보호기법이 존재한다. 많이 어려울줄 알았는데, 조금 찾아보니 다행히 대동소이한 것 같아서 핵심만 요약해보려 한다. 아직 보호기법에 따라 사용해야하는 공격기법은 공부하지 않았기 때문에, 보호기법에 중점을 두고 작성했다.

Checksec

/proc/cpuinfo &/etc/default/grub 파일 확인

cat /proc/cpuinfo | grep flags

위 명령어로 smep, smap 확인하고, /etc/default/grub에서 kaslr 확인

qemu 스크립트 확인

qemu 스크립트에 아래 옵션들로 확인 가능하다.

-append "••• nokaslr •••"
-cpu smep,smap

KASLR

유저 영역 보호기법인 ASLR 앞에 Kernel이 붙은 보호기법이다. 말 그대로 커널 영역 주소 랜덤화다.

커널이 실행되기 전 부트로더에서 세팅된다.

적용 여부는 qemu 스크립트에 옵션으로 준다. 아래 스크립트의 nokaslr 부분이다.

 
#!/bin/sh
qemu-system-x86_64 \
    -m 64M \
    -nographic \
    -kernel bzImage \
    -append "console=ttyS0 loglevel=3 oops=panic panic=-1 nopti nokaslr" \
    -no-reboot \
    -cpu qemu64 \
    -smp 1 \
    -monitor /dev/null \
    -initrd debugfs.cpio \
    -net nic,model=virtio \
    -net user

우회 방법은 ASLR과 마찬가지로 임의의 주소를 leak해서 offset을 계산해서 해결 가능하다.
/proc/kallsyms 파일에 커널의 모든 심볼 주소를 담고 있어서 이를 토대로 계산해주면 된다(libc 처럼).

해당 파일 내에 base address는 _text 심볼의 주소다.

SMEP

커널 영역에서 유저 영역의 실행 권한을 제한

ring 0 권한일 때 ring 3와 관련된 코드를 실행할 수 없게 하는 기법인데, NX Bit 보호기법과 비슷하다.

cr4 레지스터에 SMEP를 제어하는 비트가 있는데, 이 비트를 0으로 만들면 해제가 가능하다.

커널 영역 ROP로 우회가 가능하며, gadget은 vmlinux 이미지에서 구할 수 있다.

pwndbg> i r
rax            0xffffffff81b2c390  -2118990960
rbx            0x0                 0
rcx            0x0                 0
rdx            0x232               562
rsi            0x83                131
rdi            0x0                 0
rbp            0x0                 0x0
rsp            0xffffffff82403eb0  0xffffffff82403eb0
•••
cr0            0x80050033          [ PG AM WP NE ET MP PE ]
cr2            0x24fdc78           38788216
cr3            0x1d92e000          [ PDBR=121134 PCID=0 ]
cr4            0x1006f0            [ SMEP OSXMMEXCPT OSFXSR PGE MCE PAE PSE ]
•••

추가로 커널 영역에서 유저 영역의 실행 권한을 제한하기 때문에 페이로드를 커널 영역에 넣어놓고 스택 피봇팅을 이용한 방법으로도 우회가 가능하다.

커널 영역에서 특정 주소에 접근할 수 있을 때, 유저 영역에서 mmap을 이용해 해당 영역을 할당하고, 페이로드를 해당 영역에 작성해둔 다음에 return 해버리면 된다.

SMAP

SMEP + Read/Write 권한도 제한.

이 보호기법 또한 cr4 레지스터에서 제어하며, 이를 0으로 만들면 해제할 수 있다.

SMEP처럼 stack based BOF가 발생하면 단순하게 ROP로 우회 가능하다. 하지만 이런 경우는 굳이 SMAP를 설정하지 않기 때문에 거의 보기 힘들다.

다른 우회 방법으로는 slab 객체 크기의 커널 힙 할당 및 UAF가 가능한 경우에는 fork() 호출 시 커널 힙에 할당 되는 cred 구조체의 멤버를 수정하여 권한 상승을 할 수 있다.

해당 기법을 이용한 문제 풀이

KADR

일반 유저가 커널의 심볼을 볼 수 없도록 제한.

/ $ cat /proc/kallsyms | grep text
0000000000000000 T _stext
0000000000000000 T _text

rootfs 파일의 init 파일 안에 설정되어 있다.

echo 2 > /proc/sys/kernel/kptr_restrict

2 : 모두에게 표시 안함
1 : 권한 있는 유저에게만 표시
0 : 모두에게 표시

커널 힙과 관련된 취약점의 경우 user 권한과 root 권한일때의 heap 할당 방식이 다르기 때문에 offset 계산 후 user 권한으로 다시 디버깅을 해야한다.

SSP

Canary와 동일

Leak으로 우회하면 된다.

KPTI

커널 영역 > 유저 영역 간 전환이 발생할 때, 각자의 페이지 테이블을 사용한다. 이 때 최소한의 커널 주소만 포함되도록 하는 보호기법이다.

qemu 스크립트에서 -cpu kvm64가 적용되어 있는 경우에만 해당한다.

앞에 나온 다른 보호기법에서는 ROP 페이로드를 작성할 시 swapgsiretq 가젯을 사용하는데, 이 보호기법을 우회할 때는 swapgs_restore_regs_and_return_to_user_mod 함수를 추가한다.

해당 함수는 커널 영역에서 유저 영역으로 전환될 때 호출되는 함수인데, 페이지 테이블을 분리한다. 함수 내에서 페이지 테이블을 분리하고 나면, swapgs, iretq를 알아서 호출한다.

Reference