4

【Pwn 笔记】Linux Kernel 调试文件总结

 2 years ago
source link: https://binlep.github.io/2020/03/12/%E3%80%90Pwn%20%E7%AC%94%E8%AE%B0%E3%80%91Linux%20Kernel%20%E8%B0%83%E8%AF%95%E6%96%87%E4%BB%B6%E6%80%BB%E7%BB%93/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【Pwn 笔记】Linux Kernel 调试文件总结

用于上传文件的脚本就放到最后啦

baby.ko

漏洞模块,要拖到 IDA 看的,要打的题就是它

bzImage

vmlinux

vmlinux 是 ELF 文件,未压缩的内核,即编译出来的最原始的文件

vmlinux 用于 kernel-debug,产生 system.map 符号表,不能用于直接加载,不可以作为启动内核,只是启动过程中的中间媒体

vmlinuz

vmlinuz 是可引导的、压缩的内核,vm 代表 Virtual Memory

Linux 支持虚拟内存,不像老的操作系统比如 DOS 有 640KB 内存的限制,因为 Linux 能够使用硬盘空间作为虚拟内存,所以得名 vm

vmlinuz 是可执行的 Linux 内核,位于/boot/vmlinuz。它一般是一个软链接,但是已经丢失了调试信息等数据,不可用于调试

这就是为什么 perf 和 systemtap 等内核级别的调试软件安装的时候,需要重新编译内核的原因

同理,解压缩 vmlinuz 是不能得到 vmlinux 的,相对于 vmlinux,它增加了解压缩和 boot 的部分

zImage

zImage 是 vmlinuz 经过 gzip 压缩后的文件,适用于小内核(512KB 以内),加载到内存的开始 640KB 处

bzImage

bzImage 是 vmlinuz 经过 gzip 压缩后的文件,适用于大内核,为什么会发明 bzImage 这种内核镜像呢?

随着 Linux 内核的成熟,Linux 内核的大小逐渐增大,超过了一些体系结构的限制,导致存储压缩内核的空间受到限制

bzImage 这种格式就是为了克服这种限制,它通过把 kernel 分解到不相邻的内存区域来达到这一个目的

bzImage 包含以下目标文件:bootsect.o + setup.o + misc.o + piggy.o

bzImage 的解剖图:

extract-vmlinux

主要是在题目没有提供 vmlinux 用以调试内核及查看 rop 的时候,利用 extract-vmlinux 命令可以从 bzImage 中提取 vmlinux

extract-vmlinux 存在于在搭建 Kernel 环境时编译 linux 内核的文件夹里,我的路径是/linux-4.20/scripts/extract-vmlinux

ropper

ropper 提取 rop 的速度比 ROPgadget 要快很多,但是安装属实困难,最主要的问题出在安装 filebytes 上面

安装这个库的时候包名都变成了 UNKNOWN,之后了解到是因为没更新 setuptools,用命令更新一下:

sudo pip install setuptools --upgrade
ERROR: launchpadlib 1.10.3 requires testresources, which is not installed.

再安装即可

sudo pip install launchpadlib
sudo pip install filebytes

之后按照 github 上面写的安装 ropper 的步骤来就完事了,提取 rop 的方法如下:

ropper -f vmlinux --nocolor > gadget.txt

initramfs.cpio

cpio 文件的压缩和解包

这个一定要在解压出来的文件目录内运行,不然最后出来的镜像文件是不完整的

find . | cpio -o --format=newc > initramfs.cpio

将 cpio 文件内的文件全部解压到当前目录,最好一开始建个文件夹,将这些文件都存在单独的文件夹里

sudo cpio -idmv < initramfs.cpio

这个右键归档管理器貌似也能解压,可能是因为我装了 7z

这个文件一般都在解包后文件夹的/etc/init.d里,记载着内核启动时的参数,一般 rcS 文件内会写有如下参数:

#!/bin/sh
mount -t devtmpfs none /dev
mount -t proc proc /proc

insmod /home/pwn/baby.ko
chmod 644 /dev/baby
echo 1 > /proc/sys/kernel/dmesg_restrict
echo 1 > /proc/sys/kernel/kptr_restrict

cd /home/pwn
setsid cttyhack setuidgid 1000 sh

umount /proc
poweroff -f

这里调试的时候需要把 1000 改成 0000 来获得 root 权限,改成 0 会崩,一定要 4 个 0

下面就简单讲解一下这些参数:

mount -t devtmpfs none /dev

挂载 devtmpfs 类型的文件系统,设备名设置为 none,挂载目录为 /dev

devtmpfs 的功用是在 Linux 核心启动早期建立一个初步的 /dev,令一般启动程序不用等待 udev,缩短 GNU/Linux 的开机时间。

mount -t proc proc /proc

挂载 proc 类型的文件系统,设备名设置为 proc,挂载目录为 /proc

insmod /home/pwn/baby.ko

加载存在漏洞的题目模块到内核中

chmod 644 /dev/baby

这块还不懂,但是不设置内核会崩,改的是一个字符设备文件的权限

echo 1 > /proc/sys/kernel/dmesg_restrict

这个参数是不让非 root 用户读取 dmesg 的输出信息的

设置为 0 就可以输出 dmesg 的信息了,但是没必要,改成 root 用户就好了

echo 1 > /proc/sys/kernel/kptr_restrict

有 0、1、2 三个值可以选择,这里的 1 具体我也不懂,不过 1 和 0 都是能打印内核符号表的

如果是 2 的话,打印的符号表地址就都是 0,相当于不让你读取了,一般读取方法如下:

grep prepare_kernel_cred /proc/kallsyms

cd /home/pwn

漏洞模块在这,没啥好说的

setsid cttyhack setuidgid 1000 sh

创建一个子进程,并使子进程成为新会话的首进程,成为新进程组的组长进程

用户组设置为 1000,登录 shell 设置为 sh

这个 cttyhack 我不知道啥意思,不过写上就完了

umount /proc

卸除挂载的 /proc 文件夹

poweroff -f

不写起不了程序,暂且不知道是退出啥终端

startvm.sh

起程序的脚本,一般大致如下:

#!/bin/bash

stty intr ^]
cd `dirname $0`
timeout --foreground 600 qemu-system-x86_64\
-m 64M \
-nographic \
-kernel bzImage \
-append 'console=ttyS0 loglevel=3 oops=panic panic=1 nokaslr' \
-monitor /dev/null \
-initrd initramfs.cpio \
-smp cores=1,threads=1 \
-cpu qemu64 2>/dev/null

调试的时候需要在后面加一个 -s 用来开放本地的 1234 号端口,用 gdb-multiarch 监听 1234 端口即可

如果 1234 号端口用不了,也可以换成 -gdb tcp::端口号,改完大致如下:

#!/bin/bash

stty intr ^]
cd `dirname $0`
timeout --foreground 600 qemu-system-x86_64\
-m 64M \
-nographic \
-kernel bzImage \
-append 'console=ttyS0 loglevel=3 oops=panic panic=1 nokaslr' \
-monitor /dev/null \
-initrd initramfs.cpio \
-smp cores=1,threads=1 \
-cpu qemu64 2>/dev/null \
-gdb tcp::2134

exp 上传脚本

这里用的是 sixstars 战队中一位师傅的脚本,采用 base64 来进行文件编码传输

我改了改脚本,写 exp 的时候需要在当前目录下新建一个 poc 文件夹,把 c 文件和 c 程序都放在那里

这样看起来不乱,程序的名字设置成 exp 就好

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import os

# context.log_level = 'debug'
cmd = '$ '


def exploit(r):
r.sendlineafter(cmd, 'stty -echo')
os.system('musl-gcc -static -O2 ./poc/exp.c -o ./poc/exp')
os.system('gzip -c ./poc/exp > ./poc/exp.gz')
r.sendlineafter(cmd, 'cat <<EOF > exp.gz.b64')
r.sendline((read('./poc/exp.gz')).encode('base64'))
r.sendline('EOF')
r.sendlineafter(cmd, 'base64 -d exp.gz.b64 > exp.gz')
r.sendlineafter(cmd, 'gunzip ./exp.gz')
r.sendlineafter(cmd, 'chmod +x ./exp')
r.sendlineafter(cmd, './exp')
r.interactive()


p = process('./startvm.sh', shell=True)
# p = remote('', )

exploit(p)

root 用户

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import os

# context.log_level = 'debug'
cmd = '# '


def exploit(r):
r.sendlineafter(cmd, 'stty -echo')
os.system('musl-gcc -static -O2 ./poc/exp.c -o ./poc/exp')
os.system('gzip -c ./poc/exp > ./poc/exp.gz')
r.sendlineafter(cmd, 'cat <<EOF > exp.gz.b64')
r.sendline((read('./poc/exp.gz')).encode('base64'))
r.sendline('EOF')
r.sendlineafter(cmd, 'base64 -d exp.gz.b64 > exp.gz')
r.sendlineafter(cmd, 'gunzip ./exp.gz')
r.sendlineafter(cmd, 'chmod +x ./exp')
r.sendlineafter(cmd, './exp')
r.interactive()


p = process('./startvm.sh', shell=True)
# p = remote('', )

exploit(p)

https://www.cnblogs.com/javawebsoa/archive/2013/07/24/3212564.html
https://blog.csdn.net/tiantao2012/article/details/78864757

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK