[内核pwn] 环境搭建

照着师傅们的教程弄的,顺便记录一些自己遇到的问题

编译内核 or 下载内核

自己编译内核

  1. 安装所需要的依赖
1
2
sudo apt-get update
sudo apt-get install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc
  1. 下载kernel源码,这里我下载的是4.4.72版本

  2. 解压源码,然后在源码目录

1
make menuconfig
1
2
3
4
5
6
进入kernel hacking
勾选以下项目
Kernel debugging
Compile-time checks and compiler options —> Compile the kernel with debug info和Compile the kernel with frame pointers
KGDB
然后保存退出
  • 但基本上都默认保存了,所以检查一下然后退出就好了
  1. 生成kernel binary
1
make bzImage
  • 等一段时间就会出现如下信息,就意味着编译成功,然后从/arch/x86/boot/拿到bzImage,从源码根目录拿到vmlinux
1
2
3
4
Setup is 17436 bytes (padded to 17920 bytes).
System is 6797 kB
CRC c4c988d7
Kernel: arch/x86/boot/bzImage is ready (#1)
1
2
3
4
5
☁  linux-4.4.72  file vmlinux
vmlinux: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=d1f415bda5dcb18a110c0c741a0b4d69ebcee3a7, not stripped
☁ linux-4.4.72 cd arch/x86/boot
☁ boot file bzImage
bzImage: Linux kernel x86 boot executable bzImage, version 4.4.72 (hacker_mao@hacker-virtual-machine) #1 SMP Sat Jan 5 19:, RO-rootFS, swap_dev 0x6, Normal VGA

利用apt直接下载内核文件及源码

  1. 下载源码
  • 先按照版本号搜索,例如4.15.0-22版本的
1
apt search linux-headers-4.15.0-22-
  • 然后安装
1
sudo apt install linux-headers-4.15.0-22 linux-headers-4.15.0-22-generic
  • 安装成功后就会在/usr/src目录下有源码了,4.4.0-21是本机的
1
2
3
☁  boot  cd /usr/src 
☁ src ls
linux-headers-4.15.0-22 linux-headers-4.15.0-22-generic linux-headers-4.4.0-21 linux-headers-4.4.0-21-generic
  1. 下载内核文件
  • 同样先搜索各个版本的内核
1
sudo apt search linux-image-
  • 然后再下载自己所需要版本的内核
1
apt download xxxx
  • 下载下来是deb,我们解压之后在/data/boot文件夹下可以找到我们所需的内核镜像文件vmlinuz-4.15.0-22-generic,一般名字都是vmlinuz开头的

添加syscall

  • 这里我们也跟着501师傅的教程在4.4.72版本的内核复现一下添加syscall的操作
  1. 源码根目录下创建helloworld目录,并在helloworld目录下添加两个文件
1
2
3
4
☁  linux-4.4.72  mkdir helloworld
☁ linux-4.4.72 cd helloworld
☁ helloworld touch helloworld.c
☁ helloworld touch Makefile
1
2
3
4
5
6
7
//helloworld.c
#include <linux/kernel.h>

asmlinkage long sys_helloworld(void){
printk("{==kernel==} hello world\n");
return 0;
}
1
2
//Makefile
obj-y=helloworld.o
  1. 编辑源码根目录下的Makefile,添加helloworld/

  1. 编辑include/linux/syscalls.h,添加函数原型
1
asmlinkage long sys_helloworld(void);

  1. 编辑arch/x86/entry/syscalls/syscall_32.tbl和arch/x86/entry/syscalls/syscall_64.tbl,添加系统调用号
1
2
//syscall_32.tbl
1337 i386 helloworld sys_helloworld
1
2
//syscall_64.tbl
1337 common helloworld sys_helloworld

  1. 编译kernel,然后在./arch/x86/boot/下获得新bzImage
1
make bzImage
  • 编译成功
1
2
3
4
Setup is 17436 bytes (padded to 17920 bytes).
System is 6800 kB
CRC a4eb3388
Kernel: arch/x86/boot/bzImage is ready (#2)

编译busybox

  1. 官网上下载源码编译,这里我下的是busybox-1.30.0

  2. 解压源码,然后在根目录下

1
make menuconfig
  • 进Settings,勾上Build static binary (no shared libs)
  1. 编译

    1
    make install -j4
  2. 编译完成后根目录多了一个_install的目录,这就是编译的结果,然后

1
2
3
4
5
6
cd _install
mkdir proc
mkdir sys
touch init
chmod +x init
touch packet
  • 在init中写入下面内容,用于内核初始化,其中insmod用于将指定模块加载到内核中
1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/sh
echo "{==DBG==} INIT SCRIPT"
mkdir /tmp
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mount -t tmpfs none /tmp
# insmod /xxx.ko # load ko
mdev -s # We need this to find /dev/sda later
echo -e "{==DBG==} Boot took $(cut -d' ' -f1 /proc/uptime) seconds"
setsid /bin/cttyhack setuidgid 1000 /bin/sh #normal user
# exec /bin/sh #root
  • 在packet中写入,用于将FileSystem打包成映像
1
2
3
#!/bin/sh
echo "Generate rootfs.img"
find . | cpio -o --format=newc > ./rootfs.img

编译ko文件

  1. 在kernel源码目录下创建一个新的文件夹
1
2
3
4
☁  linux-4.4.72  mkdir test_ko
☁ linux-4.4.72 cd test_ko
☁ test_ko touch hello.c
☁ test_ko touch Makefile
  • 在hello.c和Makefile中写入东西,写Makefile的时候注意要使用Tab而不是空格
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//hello.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cred.h>
MODULE_LICENSE("Dual BSD/GPL");
struct cred c1;
static int hello_init(void)
{
printk("<1> Hello world!\n");
printk("size of cred : %d \n",sizeof(c1));
return 0;
}
static void hello_exit(void)
{
printk("<1> Bye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Makefile 
obj-m := hello.o

KERNELDR := /home/hacker_mao/desktop/kernel_pwn/linux-4.4.72

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
  1. 编译ko文件
1
make
  • 编译成功,会出现hello.ko文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ☁  test_ko  make
    make -C /usr/src/linux-headers-4.15.0-22-generic M=/home/hacker_mao/desktop/kernel_pwn/linux-4.4.72/test_ko modules
    make[1]: Entering directory '/usr/src/linux-headers-4.15.0-22-generic'
    CC [M] /home/hacker_mao/desktop/kernel_pwn/linux-4.4.72/test_ko/hello.o
    Building modules, stage 2.
    MODPOST 1 modules
    CC /home/hacker_mao/desktop/kernel_pwn/linux-4.4.72/test_ko/hello.mod.o
    LD [M] /home/hacker_mao/desktop/kernel_pwn/linux-4.4.72/test_ko/hello.ko
    make[1]: Leaving directory '/usr/src/linux-headers-4.15.0-22-generic'
    ☁ test_ko file hello.ko
    hello.ko: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), BuildID[sha1]=1aa1e7d97868119d05876707c472a92a877048fa, not stripped

启动系统

  1. 将编译好的hello.ko放到busybox的_install目录下

  2. 再写个demo测试前面添加的syscall,将demo也放在_install目录下

1
2
3
4
5
6
7
//gcc test.c -static -o test
#include <unistd.h>

int main(void){
syscall(1337);
return 0;
}
  1. 将FileSystem打包成映像
1
./packet
  1. 将生成的rootfs.img和编译的内核bzImage文件放在同一文件夹下,新建一个启动脚本boot.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
#! /bin/sh

qemu-system-x86_64 \
-m 128M \
-kernel ./bzImage \
-initrd ./rootfs.img \
-append "root=/dev/ram rw oops=panic panic=1 kalsr" \
-netdev user,id=t0, -device e1000,netdev=t0,id=nic0 \
-monitor /dev/null \
-smp cores=2,threads=1 \
-cpu kvm64,+smep \
#-gdb tcp::1234 \
#-S
  1. 启动内核
1
./boot.sh
  • 编译的syscall和hello.ko都生效了

  • 通过lsmod命令还可以查看已加载的modul

  1. 调试ko

  2. 这里按照gdb调试内核模块的操作来,首先下载相关文件

  3. 在自己的内核源码根目录下拷贝vmlinux文件到arbitrarily_write文件夹下

  4. 启动内核,并安装模块

1
2
3
./start.sh
./mknod.sh
su hac425

  • 为了调试内核模块,还需要加载驱动的符号文件,首先在系统里面获取驱动的加载基地址
1
2
/ $ cat /proc/modules | grep arb
arbitrarily_write 2168 0 - Live 0xffffffffa0000000 (O)
  1. 启动gdb,加载符号文件和驱动,对驱动的函数下断点
1
2
3
4
5
gdb -q ./vmlinux
pwndbg> target remote 127.0.0.1:1234
pwndbg> add-symbol-file ./arbitrarily_write.ko 0xffffffffa0000000
pwndbg> b arw_ioctl
pwndbg> c
  • 这时候在系统运行test文件
1
./test
  • 这时候gdb就会断在我们下断点的函数

参考文章: