kernel_pwn

内核

内核的漏洞类型

img

init.sh中的一些命令

cpio命令:用于备份Linux文件

1
2
3
4
# cpio - copy files to and from archives
cpio {-o|--create} [-0acvABLV] [-C bytes] [-H format] [-M message] [-O [[user@]host:]archive] [-F [[user@]host:]archive] [--file=[[user@]host:]archive] [--format=format] [--message=mes‐
sage] [--null] [--reset-access-time] [--verbose] [--dot] [--append] [--block-size=blocks] [--dereference] [--io-size=bytes] [--quiet] [--force-local] [--rsh-command=command] [--help]
[--version] < name-list [> archive]

mount

1
2
# mount - mount a filesystem
mount -t type device dir

chrown

1
2
3
4
# chown - change file owner and group
# chown changes the user and/or group ownership of each given file.
chown [OPTION]... [OWNER][:[GROUP]] FILE...
chown [OPTION]... --reference=RFILE FILE...

setsid, setuidgid

1
2
3
4
5
6
# setsid [options] program [arguments]
# setsid runs a program in a new session.
setsid [options] program [arguments]

# setgid() sets the effective group ID of the calling process.
# setuid() sets the effective user ID of the calling process.

kernel编译

内核源码下载

gitlab地址:https://gitlab.freedesktop.org/seanpaul/kernel/-/tags

官网地址:https://mirrors.edge.kernel.org/pub/linux/kernel/

安装需要的库

1
apt-get install libncurses5-dev build-essential kernel-package bison flex libelf-dev

编译

下载内核后解压,进入其目录执行make menuconfig

进入 kernel hacking,勾选

  • KGDB:Kernel debugging
  • Compile-time checks and compiler options—>Compile the kernel with debug info

开始编译,指定架构

1
make ARCH=x86_64 -j4 bzImage

然后在arch/x86_64/boot下面就会生成对应的bzImage文件

调试的链接过程

bzImage是

1
../linux-4.9/scripts/extract-vmlinux bzImage > vmlinux
1
2
3
4
5
6
7
# docker容器中貌似不支持虚拟化不能用kvm,将-enable-kvm选项去掉
# 这个命令一般在题目中会被给出来
qemu-system-x86_64 -initrd rootfs.cpio -kernel bzImage -append 'console=ttyS0 root=/dev/ram oops=panic panic=1' \
-monitor /dev/null \
-m 64M \
--nographic -smp cores=1,threads=1 -cpu kvm64,+smep \
-gdb tcp::1234

-m是指定RAM大小(默认384)

-kernel 是指定的内核镜像,这里是我们编译的镜像路径,也可以是下载好的镜像,如./vmlinuz-4.15.0-22-generic

-initrd 设置刚刚利用 busybox 创建的 rootfs.img ,作为内核启动的文件系统

-append 附加选项,指定no kaslr可以关闭随机偏移

--nographicconsole=ttyS0一起使用,启动的界面就变成当前终端

-s 相当于-gdb tcp::1234的简写,可以直接通过主机的gdb远程连接

-monitor配置用户模式的网络#将监视器重定向到主机设备/dev/null

-smp 用于声明所有可能用到的cpus, i.e. sockets cores threads = maxcpus.

-cpu 设置CPU的安全选项

1
2
3
4
gdb 
->set architecture ...
->target remote :1234
->add-symbol-file ./fs/lib/modules/4.4.72/babydriver.ko 0xffffffffc00000
1
2
cat /sys/module/babydriver/sections/.text
# 得到0xffffffffc00000 这个就是babydriver驱动的.txt的起始地址

也可以通过以下命令输出所有的字符以及对应的地址

1
2
3
4
cat /proc/kallsyms 
# /proc/kallsyms 给出内核中所有 symbol 的地址
# 我们需要这个信息来写可靠的 exploit,否则需要自己去泄露这个信息。
# 在低版本的内核中所有用户都可读取其中的内容,高版本的内核中缺少权限的用户读取时会返回 0。

在调试的过程中直接修改uid可能会造成环境出现差别,所以要在文件系统的启动脚本中/etc/init.d/rsC或者/init中添加cat /proc/kallsyms > /tmp/kallsyms将符号信息保存下来,供之后 调试使用。

kernel pwn 的目标

Kernel Pwn 就是找出内核模块中的漏洞,然后写一个 C 语言程序,放入文件系统中打包,重新运行取来,此时用户一般都是普通用户,运行程序调用此模块的功能利用漏洞,从而提升权限到 root 用户,读取 flag。

LKM

动态可加载模块Loadable Kernel Module

可以往 Kernel 中接入各种 LKM,也可以卸载,常见的外设驱动就是一个 LKM

LKM 文件与用户态的可执行文件一样,在 Linux 中就是 ELF 文件,可以利用 IDA 进行分析

LKM 是单独编译的,但是不能单独运行,他只能作为 OS Kernel 的一部分

1
2
3
4
5
6
7
insmod # 接入指定模块
rmmod # 删除置顶模块
lsmod # 列出指定模块

# insmod - Simple program to insert a module into the Linux Kernel
# insmod is a trivial program to insert a module into the kernel.
insmod [filename] [module options...]

ioctl

ioctl 是设备驱动程序中对设备的 I/O 通道进行管理的函数

1
2
3
4
5
6
/*
fd 是文件标示符
cmd 用户对于设备的控制命令
... 和cmd的意义有关
*/
int ioctl(int fd, ind cmd, …);

如果一个 LKM 中提供了 iotcl 功能,并且实现了对应指令的操作,那么在用户态中,通过这个驱动程序,我们可以调用 ioctl 来直接调用模块中的操作。

内核态的保护模式

常见的保护机制

  • KPTI:Kernel PageTable Isolation,内核页表隔离
  • KASLR:Kernel Address space layout randomization,内核地址空间布局随机化
  • SMEP:Supervisor Mode Execution Prevention,管理模式执行保护
  • SMAP:Supervisor Mode Access Prevention,管理模式访问保护
  • Stack Protector:Stack Protector又名canary,stack cookie
  • kptr_restrict:允许查看内核函数地址
  • dmesg_restrict:允许查看printk函数输出,用dmesg命令来查看
  • MMAP_MIN_ADDR:不允许申请NULL地址 mmap(0,....)

KASLRStack Protector与用户态下的ASLRcanary保护机制相似。

KASLR

相当于alsr开启后地址空间进行随机化布局。

内核地址空间布局随机化,并不默认开启,需要在内核命令行中添加指定指令。

qemu 增加启动参数 -append “kaslr” 即可开启

Stack Protector

相当于开启了canary,对栈上的地址进行保护。

MMAP_MIN_ADDR

保护机制不允许程序分配低内存地址

SMEP

管理模式执行保护,保护内核是其不允许执行用户空间代码。

操作系统是通过 CR4 寄存器的第 20 位的值来判断 SMEP 是否开启,1 开启,0 关闭,检查 SMEP 是否开启,可通过 mov 指令给 CR4 寄存器赋值从而达到关闭 SMEP 的目的,相关的 mov 指令可以通过 ropper,ROPgadget 等工具查找。

img

SMAP

管理模式访问保护,禁止内核访问用户空间的数据

提权

  1. 内核态调用commit_creds(prepare_kernel_cred(0)),返回用户态执行shell

    1
    commit_creds(prepare_kernel_cred(0)
  2. 修改cred结构体

    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
    struct cred {
    atomic_t usage;
    #ifdef CONFIG_DEBUG_CREDENTIALS
    atomic_t subscribers; /* number of processes subscribed */
    void *put_addr;
    unsigned magic;
    #define CRED_MAGIC 0x43736564
    #define CRED_MAGIC_DEAD 0x44656144
    #endif
    kuid_t uid; /* real UID of the task */
    kgid_t gid; /* real GID of the task */
    kuid_t suid; /* saved UID of the task */
    kgid_t sgid; /* saved GID of the task */
    kuid_t euid; /* effective UID of the task */
    kgid_t egid; /* effective GID of the task */
    kuid_t fsuid; /* UID for VFS ops */
    kgid_t fsgid; /* GID for VFS ops */
    unsigned securebits; /* SUID-less security management */
    kernel_cap_t cap_inheritable; /* caps our children can inherit */
    kernel_cap_t cap_permitted; /* caps we're permitted */
    kernel_cap_t cap_effective; /* caps we can actually use */
    kernel_cap_t cap_bset; /* capability bounding set */
    kernel_cap_t cap_ambient; /* Ambient capability set */
    #ifdef CONFIG_KEYS
    unsigned char jit_keyring; /* default keyring to attach requested
    * keys to */
    struct key __rcu *session_keyring; /* keyring inherited over fork */
    struct key *process_keyring; /* keyring private to this process */
    struct key *thread_keyring; /* keyring private to this thread */
    struct key *request_key_auth; /* assumed request_key authority */
    #endif
    #ifdef CONFIG_SECURITY
    void *security; /* subjective LSM security */
    #endif
    struct user_struct *user; /* real user ID subscription */
    struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
    struct group_info *group_info; /* supplementary groups for euid/fsgid */
    struct rcu_head rcu; /* RCU deletion hook */
    };