Reference

http://fanrong1992.github.io/2016/11/09/Return-to-dl-resolve/

Program

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    char buf[1024];
    while(1)
    {
        memset(buf, '\0', 1024);
        read(0, buf, 1024);
        printf(buf);
        fflush(stdout);
    }
    return 0;
}

一个简单的printf格式化字符串漏洞程序

重定位

  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8048330
 0x0000000d (FINI)                       0x80485b4
 0x00000019 (INIT_ARRAY)                 0x8049f08
 0x0000001b (INIT_ARRAYSZ)               4 (bytes)
 0x0000001a (FINI_ARRAY)                 0x8049f0c
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x6ffffef5 (GNU_HASH)                   0x80481ac
 0x00000005 (STRTAB)                     0x8048260
 0x00000006 (SYMTAB)                     0x80481d0
 0x0000000a (STRSZ)                      102 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x804a000
 0x00000002 (PLTRELSZ)                   40 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8048308
 0x00000011 (REL)                        0x80482f8
 0x00000012 (RELSZ)                      16 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x80482d8
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x80482c6
 0x00000000 (NULL)                       0x0

JMPREL

.rel.plt是elf文件中用来进行函数重定位的区段,而.rel.dyn则是变量重定位

read -S core

  [ 9] .rel.dyn          REL             080482f8 0002f8 000010 08   A  5   0  4
  [10] .rel.plt          REL             08048308 000308 000028 08  AI  5  24  4

然后看下symbol具体的信息,第一个就对应got表地址,第二个是type和index组合

pwndbg> dd 0x8048308
08048308     0804a00c 00000107 0804a010 00000207

RLF32_R_SYM(r_info)=0x107>>8=1

ELF32_R_TYPE(r_info)=0x107*0xff=0x07

在执行read函数的时候

<read@plt>                  jmp    dword ptr [_GLOBAL_OFFSET_TABLE_+12] <0x804a00c>

程序通过调用plt中的jmp跳转到.got.plt对应地址

.got.plt就存放真实的read函数地址

SYMTAB

.dynamic中SYMTAB存放相关符号表信息,STRTAB则对应了函数名,由上面计算到的sym对应的index为1,那么SYMTAB[1]就对应了read函数的符号信息,实际上对应文件中偏移就是如下

   [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
   [ 5] .dynsym           DYNSYM          080481d0 0001d0 000090 10   A  6   1  4

可以看到SYMENT为16bytes,那么SYMTAB[1]实际上就是SYMTAB+16

pwndbg> dd 0x80481d0+16
080481e0     0000002f 00000000 00000000 00000012

由Sym的定义

// Symbol table entries for ELF32.
struct Elf32_Sym {
   Elf32_Word    st_name;  // Symbol name (index into string table)
   Elf32_Addr    st_value; // Value or address associated with the symbol
   Elf32_Word    st_size;  // Size of the symbol
   unsigned char st_info;  // Symbol's type and binding attributes
   unsigned char st_other; // Must be zero; reserved
   Elf32_Half    st_shndx; // Which section (header table index) it's defined in
   }

第一个(4bytes)就是该sym对应函数名(变量名)在STRTAB中的偏移(0x2f)

pwndbg> x/s 0x8048260+0x2f
0x804828f:    "read"

流程

总结一下查找函数的流程。以read为例。

首先程序通过.rel.plt找到条目的位置,每个条目大小为RELENT,其中条目包含两个信息,一个是got表位置,另外一个是index和type,通过index到SYMTAB中进行查找,即dynsym[1],查找到其Elf32_Sym 对应地址后根据其结构定位其名称的偏移st_name,然后STRTAB+st_name对应了其函数名,比较函数名是不是read,然后进行表填充

利用

由于解析的过程是要通过rel.plt,所以dl-resolve的思路就是伪造rel.plt中的rel_offset,然后伪造后面一系列解析需要的信息,从而解析出system函数地址。

results matching ""

    No results matching ""