【系列分享】Linux 内核漏洞利用教程(一):环境配置

http://p0.qhimg.com/t0168303a72bd1ba0bd.jpg

作者:o0xmuhe

预估稿费:300RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


传送门

【系列分享】Linux 内核漏洞利用教程(二):两个Demo


0x00: 前言

一直想入门linux kernel exploit,但是网络上比较成熟的资料很少,只能找到一些slide和零碎的文档,对于入门选手来说真的很困难。还好在自己瞎摸索的过程中上了joker师傅的装甲车,师傅说:要有开源精神,要给大家学习的机会。

所以就有了这个系列的文章,第一篇记录是环境配置篇,包含了linux内核编译、添加系统调用并测试的过程。在这个过程中我还是遇到很多坑点的,踩了一段时间才把这些坑填好,成功搞定,希望我的经历能给大家一点帮助。


0x01: 环境说明

ubuntu 14.04 x86
qemu

使用的内核版本2.6.32.1

busybox版本1.19.4

使用busybox是因为文件添加方便.


0x02: 内核编译并测试

1. 下载内核源码

$ wget https://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.32.1.tar.gz -O linux-2.6.32.1.tar.gz
$ tar -xjvf linux-2.6.32.1.tar.gz

2. 编译过程

首先要安装一些依赖库以及qemu。

$ 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

3. 编译的时候遇到的问题以及解决方案

3.1 问题1

问题

Can't use 'defined(@array)' (Maybe you should just omit the defined()?) at kernel/timeconst.pl line 373.
/home/muhe/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/kernel/Makefile:129: recipe for target 'kernel/timeconst.h' failed
make[1]: *** [kernel/timeconst.h] Error 255
Makefile:878: recipe for target 'kernel' failed
make: *** [kernel] Error 2

解决方案: 尝试修改这个文件

    @val = @{$canned_values{$hz}};
-   if (!defined(@val)) {
+   if (!@val) {
            @val = compute_values($hz);
    }
    output($hz, @val);
--

3.2 问题2

问题描述

.... 
arch/x86/kernel/ptrace.c:1472:17: error: conflicting types for ‘syscall_trace_enter’
 asmregparm long syscall_trace_enter(struct pt_regs *regs)
                 ^
In file included from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/vm86.h:130:0,
                 from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/processor.h:10,
                 from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/thread_info.h:22,
                 from include/linux/thread_info.h:56,
                 from include/linux/preempt.h:9,
                 from include/linux/spinlock.h:50,
                 from include/linux/seqlock.h:29,
                 from include/linux/time.h:8,
                 from include/linux/timex.h:56,
                 from include/linux/sched.h:56,
                 from arch/x86/kernel/ptrace.c:11:
/home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/ptrace.h:145:13: note: previous declaration of ‘syscall_trace_enter’ was here
 extern long syscall_trace_enter(struct pt_regs *);
             ^
arch/x86/kernel/ptrace.c:1517:17: error: conflicting types for ‘syscall_trace_leave’
 asmregparm void syscall_trace_leave(struct pt_regs *regs)
                 ^
In file included from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/vm86.h:130:0,
                 from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/processor.h:10,
                 from /home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/thread_info.h:22,
                 from include/linux/thread_info.h:56,
                 from include/linux/preempt.h:9,
                 from include/linux/spinlock.h:50,
                 from include/linux/seqlock.h:29,
                 from include/linux/time.h:8,
                 from include/linux/timex.h:56,
                 from include/linux/sched.h:56,
                 from arch/x86/kernel/ptrace.c:11:
/home/muhe/linux_kernel/linux-2.6.32.1/arch/x86/include/asm/ptrace.h:146:13: note: previous declaration of ‘syscall_trace_leave’ was here
 extern void syscall_trace_leave(struct pt_regs *);
             ^
make[2]: *** [arch/x86/kernel/ptrace.o] 错误 1
make[1]: *** [arch/x86/kernel] 错误 2
make: *** [arch/x86] 错误 2

解决方案

patch patch -p1 < /tmp/1.patch
--- linux-2.6.32.59/arch/x86/include/asm/ptrace.h
+++ fix_ptrace.o_compile_error/arch/x86/include/asm/ptrace.h
@@ -130,6 +130,7 @@
ifdef KERNEL
include
+#include
struct cpuinfo_x86;
struct task_struct;
@@ -142,8 +143,8 @@
int error_code, int si_code);
void signal_fault(struct pt_regs regs, void __user frame, char *where);
-extern long syscall_trace_enter(struct pt_regs );
-extern void syscall_trace_leave(struct pt_regs );
+extern asmregparm long syscall_trace_enter(struct pt_regs );
+extern asmregparm void syscall_trace_leave(struct pt_regs );
static inline unsigned long regs_return_value(struct pt_regs *regs)
{

3.3 问题3

问题描述

gcc: error: elf_i386: 没有那个文件或目录
gcc: error: unrecognized command line option ‘-m’

解决方案

    arch/x86/vdso/Makefile
    VDSO_LDFLAGS_vdso.lds = -m elf_x86_64 -Wl,-soname=linux-vdso.so.1    -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096 把"-m elf_x86_64" 替换为 "-m64"
    VDSO_LDFLAGS_vdso32.lds = -m elf_i386 -Wl,-soname=linux-gate.so.1中的 "-m elf_i386" 替换为 "-m32"

3.4 问题4

问题描述

drivers/net/igbvf/igbvf.h15: error: duplicate member ‘page’
struct page page;
^
make[3]: ** [drivers/net/igbvf/ethtool.o] 错误 1
make[2]: [drivers/net/igbvf] 错误 2
make[1]: [drivers/net] 错误 2
make: * [drivers] 错误 2

解决方案

    //修改名字重复
    struct {
                        struct page *_page;
                        u64 page_dma;
                        unsigned int page_offset;
                };
        };
        struct page *page;

0x03:增加syscall

增加syscall的方式和之前文章写的差不多,只是这次内核版本更低,所以更简单一点。我这里添加了两个系统调用进去。

1. 在syscall table中添加信息

文件 arch/x86/kernel/syscall_table_32.S中添加自己的调用

    .long sys_muhe_test
    .long sys_hello

2. 定义syscall的宏

文件arch/x86/include/asm/unistd_32.h中添加

#define __NR_hello 337
#define __NR_muhe_test    338
#ifdef __KERNEL__
#define NR_syscalls 339

要注意NR_syscalls要修改成现有的调用数目,比如原来有0~336一共337个调用,现在增加了两个,那就改成339。

3. 添加函数定义

文件include/linux/syscalls.h

asmlinkage long sys_muhe_test(int arg0);
asmlinkage long sys_hello(void);

4. 编写syscall代码

新建目录放自定义syscall的代码

# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/muhe_test [2:43:06] 
$ cat muhe_test.c
#include <linux/kernel.h>
asmlinkage long sys_muhe_test(int arg0){
    printk("I am syscall");
    printk("syscall arg %d",arg0);
    return ((long)arg0);
}
asmlinkage long sys_hello(void){
    printk("hello my kernel worldn");
    return 0;
}
# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/muhe_test [2:43:12] 
$ cat Makefile
obj-y := muhe_test.o

5. 修改Makefile

# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1 [2:44:59] 
$ cat Makefile| grep muhe
core-y        += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ muhe_test/

6. 编译

make -j2

我虚拟机分配了两个核,所以使用-j2 这样能稍微快一点。


0x04: busybox编译配置

1. 编译步骤

$ make menuconfig
$ make
$ make install

2. 遇到的问题

2.1 问题一以及解决方案

错误

    loginutils/passwd.c:188:12: error: ‘RLIMIT_FSIZE’ undeclared (first use in this function)
  setrlimit(RLIMIT_FSIZE, &rlimit_fsize);

解决

$  vim include/libbb.h
$  add a line #include <sys/resource.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/socket.h>

2.2 问题二以及解决方案

错误

    linux/ext2_fs.h: 没有那个文件或目录

解决

    Linux System Utilities --->
        [ ] mkfs_ext2 
        [ ] mkfs_vfat

3. 编译完成之后如下配置

1. 方案1

$ cd _install
$ mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}
$ cat init
#!/bin/sh
echo "INIT SCRIPT"
mount -t proc none /proc
mount -t sysfs none /sys
mount -t debugfs none /sys/kernel/debug
mkdir /tmp
mount -t tmpfs none /tmp
mdev -s # We need this to find /dev/sda later
echo -e "nBoot took $(cut -d' ' -f1 /proc/uptime) secondsn"
exec /bin/sh
$ chmod +x init
$ find . -print0 
    | cpio --null -ov --format=newc 
    | gzip -9 > /tmp/initramfs-busybox-x86.cpio.gz
$ qemu-system-i386 -kernel arch/i386/boot/bzImage -initrd /tmp/initramfs-busybox-x86.cpio.gz

http://p7.qhimg.com/t019a80254b84d91e87.png

2. 方案2

后面为了方便,使用了另一种方式:

目录结构和之前差不多,添加inittab文件:

$ cat etc/inittab 
::sysinit:/etc/init.d/rcS
::askfirst:/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init

添加rcS文件

$ cat etc/init.d/rcS 
#!/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 -
$ chmod +x ./etc/init.d/rcS

配置下dev目录

mkdir dev
sudo mknod dev/ttyAMA0 c 204 64
sudo mknod dev/null c 1 3
sudo mknod dev/console c 5 1
$ find . | cpio -o --format=newc > ../rootfs.img
$ qemu-system-i386 -kernel arch/i386/boot/bzImage -initrd ../busybox-1.19.4/rootfs.img -append "root=/dev/ram rdinit=/sbin/init"

http://p0.qhimg.com/t016ecb6e221f21933d.png


0x05: 测试系统调用

# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1 [2:45:04] 
$ cd muhe_test_syscall_lib 
# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/muhe_test_syscall_lib [2:51:48] 
$ cat muhe_test_syscall_lib.c
#include <stdio.h>
#include <linux/unistd.h>
#include <sys/syscall.h>
int main(int argc,char **argv)
{
        printf("n Diving to kernel levelnn");
        syscall(337,1337);
        return 0;
}
# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/muhe_test_syscall_lib [2:51:51] 
$ gcc muhe_test_syscall_lib.c -o muhe -static

一定要静态链接,因为你进busybox链接库那些是没有的。

# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1/muhe_test_syscall_lib [2:52:20] 
$ cp muhe_test_syscall_lib/muhe ../busybox-1.19.4/_install/usr/muhe

这里要注意,每次拷贝新文件到busybox的文件系统中去,都要执行find . | cpio -o –format=newc > ../rootfs.img去生成新的rootfs。

然后qemu起系统

# muhe @ ubuntu in ~/linux_kernel/linux-2.6.32.1/linux-2.6.32.1 [2:53:33] 
$  qemu-system-i386 -kernel arch/i386/boot/bzImage -initrd ../busybox-1.19.4/rootfs.img -append "root=/dev/ram rdinit=/sbin/init"

http://p2.qhimg.com/t019e8e38063f5ebdac.png


0x06:引用与参考

adding-hello-world-system-call-to-linux

Adding a new system call to the Linux kernel

Adding a system call in X86 QEMU Environment

Create a simple file system

Setup for linux kernel dev using qemu

root-file-system-for-embedded-system

传送门


【系列分享】Linux 内核漏洞利用教程(二):两个Demo

(完)