zoukankan      html  css  js  c++  java
  • vsyscall bypass pie

    系统调用

    linux 的系统调用,是 linux 内核一种特殊的运行机制,使得用户空间的应用程序可以请求,像写入文件和打开套接字等特权级下的任务。在 linux 内核中发起一个系统调用是特别昂贵的操作,因为处理器需要中断当前正在执行的任务,切换内核模式的上下文,在系统调用处理完毕后跳转至用户空间。 vsyscall 设计用来加速系统调用的处理。

    vsyscall 介绍

    vsyscall 或 virtual system call 是一种也是最古老的一种用于加快系统调用的机制。 vsyscall 的工作原则其实十分简单。Linux 内核在用户空间映射一个包含一些变量及一些系统调用的实现的内存页。vsyscall 是由 map_vsyscall 实现的,它的源码如下:

    void __init map_vsyscall(void)
    {
    	extern char __vsyscall_page;
    	unsigned long physaddr_vsyscall = __pa_symbol(&__vsyscall_page);
    	if (vsyscall_mode != NONE) {
    		__set_fixmap(VSYSCALL_PAGE, physaddr_vsyscall,
    			     PAGE_KERNEL_VVAR);
    		set_vsyscall_pgtable_user_bits(swapper_pg_dir);
    	}
    	BUILD_BUG_ON((unsigned long)__fix_to_virt(VSYSCALL_PAGE) !=
    		     (unsigned long)VSYSCALL_ADDR);
    }
    

    我们重点关注最后一行 , BUILD_BUG_ON 宏用于检查 vsyscall 内存页的虚拟地址是否等于变量 VSYSCALL_ADDR ,而 VSYSCALL_ADDR 定义如下:

    #define VSYSCALL_ADDR (-10UL << 20) /* VSYSCALL_ADDR=0xFFFFFFFFFF600000 */
    

    也就是说 vsyscall 的内存页的位置在任何时刻都是相同的,值为 0xFFFFFFFFFF600000 。我们可以验证一下,使用命令:

    sudo cat /proc/1/maps | grep vsyscall
    

    输出结果如下:

    ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
    

    vsyscall 内存页中包含了三个系统调用:

    __vsyscall_page:
    	mov $__NR_gettimeofday, %rax
    	syscall
    	ret
    
    	.balign 1024, 0xcc
    	mov $__NR_time, %rax
    	syscall
    	ret
    
    	.balign 1024, 0xcc
    	mov $__NR_getcpu, %rax
    	syscall
    	ret
    
    • gettimeofday:获取当前时间和时区信息。
    • time:获取系统时间(秒数)。
    • getcpu:确定调用线程正在运行的 CPU 和 NUMA 节点概要。

    在 glibc 源码中找到地址定义如下:

    #define VSYSCALL_ADDR_vgettimeofday   0xffffffffff600000
    #define VSYSCALL_ADDR_vtime 	      0xffffffffff600400
    #define VSYSCALL_ADDR_vgetcpu	      0xffffffffff600800
    

    利用方式

    从上面的介绍我们可以知道,那怕程序开启了 pie , vsyscall 的地址还是不变的,而且这三个系统调用对程序运行基本没有影响,也就是说我们获得了三个已知地址的 ret gadget ,分别是 0xffffffffff600000 、 0xffffffffff600400 、 0xffffffffff600800 。我们将这个三个地址简单当成 ret 使用即可。
    比如现在栈情况如下:

    0x7fffffffda60:0xaaaaaaaaaaaaaaaa <-- rbp
    0x7fffffffda68:0xbbbbbbbbbbbbbbbb <-- ret_addr
    0x7fffffffda70:0xcccccccccccccccc
    0x7fffffffda78:0xcccccccccccccccc
    0x7fffffffda80:0xdddddddddddddddd <-- get_shell_addr
    

    我们通过栈溢出修改如下:

    0x7fffffffda60:0xaaaaaaaaaaaaaaaa <-- rbp
    0x7fffffffda68:0xffffffffff600000 <-- ret_addr
    0x7fffffffda70:0xffffffffff600000
    0x7fffffffda78:0xffffffffff600000
    0x7fffffffda80:0xdddddddddddddddd <-- get_shell_addr
    

    这样函数退出时,就类似与执行:

    ret ret --> ret ret ---> ret ret --> ret get_shell_addr 
    

    最终成功 get shell ,当然这里只是举例了比较简单的利用方式,具体姿势还要根据题目灵活运用。

    内容来源

    EX pwn 中 vsyscall 的利用(PIE)
    Linux 内核系统调用 第三节

  • 相关阅读:
    排序算法的实现
    图——广度优先遍历(邻接矩阵存储)
    最大子列和问题-4种解法
    PATB 1015. 德才论 (25)
    PATB 1018. 锤子剪刀布
    PATB 1019. 数字黑洞 (20)
    常用协议的默认端口号
    统一资源定位符URL
    hdoj1009 FatMouse' Trade——贪心算法
    hdoj2037 贪心算法——今年暑假不AC
  • 原文地址:https://www.cnblogs.com/luoleqi/p/13579478.html
Copyright © 2011-2022 走看看