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函数地址。