kernel
环境搭建
在Linux Kernel Archive下载对应版本的内核源码 wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.11.tar.xz
tar -xvf linux-5.11.tar.xz
cd linux-5.11.1/
make menuconfigmake -j$(nproc) bzImage
arch/x86/boot/目录下提取到bzImage,为压缩后的内核文件 再下载busybox构建文件系统,在busybox.net下载版本 wget https://busybox.net/downloads/busybox-1.33.0.tar.bz2
tar -jxvf busybox-1.33.0.tar.bz2
cd busybox-1.33.0/
make menuconfigSettings —> Build static file (no shared libs) make install
cd _install
mkdir -pv {bin,tmp,sbin,etc,proc,sys,home,lib64,lib/x86_64-linux-gnu,usr/{bin,sbin}}
touch etc/inittab
mkdir etc/init.d
touch etc/init.d/rcS
chmod +x ./etc/init.d/rcSgedit etc/inittab ::sysinit:/etc/init.d/rcS
::askfirst:/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/initetc/init.d/rcS sudo cat <<EOF > etc/init.d/rcS
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs devtmpfs /dev
mount -t tmpfs tmpfs /tmp
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo -e "\nBoot took $(cut -d' ' -f1 /proc/uptime) seconds\n"
setsid cttyhack setuidgid 1000 sh
poweroff -d 0 -f
EOFecho "root:x:0:0:root:/root:/bin/sh" > etc/passwd
echo "ctf:x:1000:1000:ctf:/home/ctf:/bin/sh" >> etc/passwd
echo "root:x:0:" > etc/group
echo "ctf:x:1000:" >> etc/group
echo "none /dev/pts devpts gid=5,mode=620 0 0" > etc/fstabfind . | cpio -o --format=newc > ../rootfs.cpio
cpio -idv < ./rootfs.cpio
bzImage和rootfs.cpio放到同一个目录下,然后编写sh脚本 #!/bin/sh
qemu-system-x86_64 \
-m 128M \
-kernel ./bzImage \
-initrd ./rootfs.cpio \
-monitor /dev/null \
-append "root=/dev/ram rdinit=/sbin/init console=ttyS0 oops=panic panic=1 loglevel=3 quiet kaslr" \
-cpu kvm64,+smep \
-smp cores=2,threads=1 \
-nographic \
-s
-m:虚拟机内存大小-kernel:内存镜像路径-initrd:磁盘镜像路径 -append:附加参数选项nokalsr:关闭内核地址随机化,方便我们进行调试rdinit:指定初始启动进程,/sbin/init进程会默认以/etc/init.d/rcS作为启动脚本loglevel=3&quiet:不输出logconsole=ttyS0:指定终端为/dev/ttyS0,这样一启动就能进入终端界面-monitor:将监视器重定向到主机设备/dev/null,这里重定向至null主要是防止CTF中被人给偷了qemu拿flag-cpu:设置CPU安全选项,在这里开启了smep保护(smep保护就不能采用ret2usr手法了)-s:相当于-gdb tcp::1234的简写(也可以直接这么写),后续我们可以通过gdb连接本地端口进行调试
机制
KASLR
和普通用户态的ASLR差不多,都是基地址+偏移
在未开启 KASLR 保护机制时 * 内核代码段的基址为 0xffffffff81000000 * 直接映射区域的基址为 0xffff888000000000
FGKASLR
KASLR的plus版本,以函数粒度重新排布内核代码 原来不同的函数会在.text一个节上,现在不同的函数在不同的节上
ksymtab
kernel_symbol结构体其记录了函数的偏移、函数名的偏移以及命名空间的偏移 在使用fgkalsr编译后函数重定向通过此结构体 struct kernel_symbol {
int value_offset; // 函数的偏移量
int name_offset; // 符号名称的偏移量
int namespace_offset; // 符号命名空间的偏移量
};kernel_symbol结构体存储的偏移就能找到具体函数的内存地址 比如 cat /proc/kallsyms | grep commit_creds
ksymtab的偏移 __start___ksymtab和__stop___ksymtab 被记录在each_symbol_section函数中 只需要 cat /proc/kallsyms | grep each_symbols_section
> addr_A
x/10i arrd_A
> ...
> mov rbx,addr_B
> ...
x/10gx addr_B
> addr_C
x/10wx addr_C
> neg_offset
x/10i addr_B + neg_offset - 0x100000000
> addr_offset_function
SMAP/SMEP
指管理模式访问保护和管理模式执行保护 用来防止内核态访问/执行用户态数据,完全将内核空间与用户空间隔离 绕过的两种方式: 篡改CR4寄存器->ret2usr:CR4寄存器的第20位标识SMEP开关(0关,1开),利用kernel ROP篡改CR4,然后完成ret2usr。 不过现在都是KPTI的内核,内核页面的用户地址没有执行权限,ret2usr已经过时
ret2dir:简单说,把用户地址的数据映射到内核地址空间上。利用内核线性映射区对物理空间地址的完整映射,可以找到用户空间的数据,但是地址在内核空间上,利用内核地址访问用户的数据
KPTI
指内核页表隔离,内核空间与用户空间使用两组不同的页表集






