首页
社区
课程
招聘
[原创]【NKCTF】babyHeap-Off by one&Tcache Attack
发表于: 2023-3-26 23:14 11850

[原创]【NKCTF】babyHeap-Off by one&Tcache Attack

2023-3-26 23:14
11850

保护全开,CRUD全都提供了,但是没有明显可利用的地方,问题出在edit()函数输入内容时使用的my_read()函数,看名字都很可移:),其中根据创建时输入的chunkSize进行循环,由于逻辑错误导致会多读取一个字节,可以进行溢出

已知在向堆输入内容时可进行一个字节的溢出,所以当我们申请0x?8个字节时便可实际输入0x?9篡改下一个chunkSize,于是我们可以进行如下利用

此时chunk#1被重新至于原位且此时记录大小为0xE8,真实大小依旧为0x68,可随意篡改,泄露chunk#2,于是进行以下利用

此时我们已经实现了 libcBase LeakChunk Extends ,题目给出了的ibc版本为glibc-2.32,由于应用了Tcache,所以决定使用Tcache poisoning进行任意地址写

Tcache整体和FastBin较相似,同采取 单链表 LIFO 进行管理,也既其FreeChunk仅有fd字段,区别是在利用时,FastBin等都将对ChunkHeader进行检查,而Tcache的检查极其少,导致安全性低,其中值得注意的一项检查和一项保护措施分别如下

tcache_perthread_struct结构体中的counts字段记录了当前分类中存在多少个可分配FreeChunk

glibc-2.31后,对FastBin/Tcachefd字段进行了混淆保护,当第一个FreeChunk进入分类中时,&FreeChunk#0 >> 3将作为key被保存并写入FreeChunk#0->fd,而后该分类的每个FreeChunk->fd在存取时都将与key进行异或,所以若是我们要篡改fd字段,则需要泄露key

glibc 2.29 之前,TcacheChunk16 bytes 进行对齐,而在这之后,当申请的大小64<=size<=128,则以16 bytes进行对齐,其它情况下则以 8 bytes 进行对齐,所以若是申请的&FakeChunk不符合对齐条件,需要-=8以绕过检查

tcache_perthread_struct->counts=0时,则会直接跳过Tcache从而去别处分配

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int choice; // [rsp+0h] [rbp-10h]
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v6; // [rsp+8h] [rbp-8h]
 
  v6 = __readfsqword(0x28u);
  init(argc, argv, envp);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      read(0, buf, 4uLL);
      choice = strtol(buf, 0LL, 10);
      if ( choice <= 5 && choice > 0 )
        break;
      puts("Index error.");
    }
    if ( choice == 5 )
      break;
    switch ( choice )
    {
      case 1:
        add();
        break;
      case 2:
        delete();
        break;
      case 3:
        edit();
        break;
      default:
        show();
        break;
    }
  }
  puts("Goodbye and welcome to use it next time.");
  return 0;
}
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int choice; // [rsp+0h] [rbp-10h]
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v6; // [rsp+8h] [rbp-8h]
 
  v6 = __readfsqword(0x28u);
  init(argc, argv, envp);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      read(0, buf, 4uLL);
      choice = strtol(buf, 0LL, 10);
      if ( choice <= 5 && choice > 0 )
        break;
      puts("Index error.");
    }
    if ( choice == 5 )
      break;
    switch ( choice )
    {
      case 1:
        add();
        break;
      case 2:
        delete();
        break;
      case 3:
        edit();
        break;
      default:
        show();
        break;
    }
  }
  puts("Goodbye and welcome to use it next time.");
  return 0;
}
unsigned __int64 add()
{
  signed int index; // [rsp+Ch] [rbp-14h]
  int size; // [rsp+10h] [rbp-10h]
  char buf[4]; // [rsp+14h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]
 
  v4 = __readfsqword(0x28u);
  printf("Enter the index: ");
  read(0, buf, 4uLL);
  index = strtol(buf, 0LL, 10);
  if ( (unsigned int)index > 0xF )
  {
    puts("Up to 16 nkctf notes can be created.");
  }
  else if ( note_array[index] )
  {
    puts("Sorry, this nkctf note has already been used.");
  }
  else
  {
    printf("Enter the Size: ");
    read(0, buf, 4uLL);
    size = strtol(buf, 0LL, 10);
    if ( size <= 256 )
    {
      note_size[index] = size;
      note_array[index] = malloc(note_size[index]);
      if ( !note_array[index] || !note_size[index] )
        my_error("malloc()", 0xFFFFFFFFLL);
    }
    else
    {
      puts("This nkctf note is too big.");
    }
  }
  return __readfsqword(0x28u) ^ v4;
}
unsigned __int64 add()
{
  signed int index; // [rsp+Ch] [rbp-14h]
  int size; // [rsp+10h] [rbp-10h]
  char buf[4]; // [rsp+14h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]
 
  v4 = __readfsqword(0x28u);
  printf("Enter the index: ");
  read(0, buf, 4uLL);
  index = strtol(buf, 0LL, 10);
  if ( (unsigned int)index > 0xF )
  {
    puts("Up to 16 nkctf notes can be created.");
  }
  else if ( note_array[index] )
  {
    puts("Sorry, this nkctf note has already been used.");
  }
  else
  {
    printf("Enter the Size: ");
    read(0, buf, 4uLL);
    size = strtol(buf, 0LL, 10);
    if ( size <= 256 )
    {
      note_size[index] = size;
      note_array[index] = malloc(note_size[index]);
      if ( !note_array[index] || !note_size[index] )
        my_error("malloc()", 0xFFFFFFFFLL);
    }
    else
    {
      puts("This nkctf note is too big.");
    }
  }
  return __readfsqword(0x28u) ^ v4;
}
unsigned __int64 delete()
{
  unsigned int v1; // [rsp+0h] [rbp-10h]
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]
 
  v3 = __readfsqword(0x28u);
  printf("Enter the index: ");
  read(0, buf, 4uLL);
  v1 = strtol(buf, 0LL, 10);
  if ( v1 > 0xF )
  {
    puts("Index error.");
  }
  else if ( note_array[v1] )
  {
    free((void *)note_array[v1]);
    note_array[v1] = 0LL;
    note_size[v1] = 0;
    if ( note_array[v1] || note_size[v1] )
      my_error("free()", 4294967294LL);
  }
  else
  {
    puts("The nkctf note to be freed does not exist.");
  }
  return __readfsqword(0x28u) ^ v3;
}
unsigned __int64 delete()
{
  unsigned int v1; // [rsp+0h] [rbp-10h]
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]
 
  v3 = __readfsqword(0x28u);
  printf("Enter the index: ");
  read(0, buf, 4uLL);
  v1 = strtol(buf, 0LL, 10);
  if ( v1 > 0xF )
  {
    puts("Index error.");
  }
  else if ( note_array[v1] )
  {
    free((void *)note_array[v1]);
    note_array[v1] = 0LL;
    note_size[v1] = 0;
    if ( note_array[v1] || note_size[v1] )
      my_error("free()", 4294967294LL);
  }
  else
  {
    puts("The nkctf note to be freed does not exist.");
  }
  return __readfsqword(0x28u) ^ v3;
}
unsigned __int64 edit()
{
  unsigned int v1; // [rsp+0h] [rbp-10h]
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]
 
  v3 = __readfsqword(0x28u);
  printf("Enter the index: ");
  read(0, buf, 4uLL);
  v1 = strtol(buf, 0LL, 10);
  if ( v1 > 0xF )
  {
    puts("Index error.");
  }
  else if ( note_array[v1] )
  {
    printf("Enter the content: ");
    my_read(note_array[v1], (unsigned int)note_size[v1]);
  }
  else
  {
    puts("The nkctf note to be filled was not created.");
  }
  return __readfsqword(0x28u) ^ v3;
}
unsigned __int64 edit()
{
  unsigned int v1; // [rsp+0h] [rbp-10h]
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]
 
  v3 = __readfsqword(0x28u);
  printf("Enter the index: ");
  read(0, buf, 4uLL);
  v1 = strtol(buf, 0LL, 10);
  if ( v1 > 0xF )
  {
    puts("Index error.");
  }
  else if ( note_array[v1] )
  {
    printf("Enter the content: ");
    my_read(note_array[v1], (unsigned int)note_size[v1]);
  }
  else
  {
    puts("The nkctf note to be filled was not created.");
  }
  return __readfsqword(0x28u) ^ v3;
}
unsigned __int64 show()
{
  unsigned int v1; // [rsp+0h] [rbp-10h]
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]
 
  v3 = __readfsqword(0x28u);
  printf("Enter the index: ");
  read(0, buf, 4uLL);
  v1 = strtol(buf, 0LL, 10);
  if ( v1 > 0xF )
  {
    puts("Index error.");
  }
  else if ( note_array[v1] )
  {
    puts((const char *)note_array[v1]);
  }
  else
  {
    puts("The nkctf note to be printed was not created.");
  }
  return __readfsqword(0x28u) ^ v3;
}
unsigned __int64 show()
{
  unsigned int v1; // [rsp+0h] [rbp-10h]
  char buf[4]; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]
 
  v3 = __readfsqword(0x28u);
  printf("Enter the index: ");
  read(0, buf, 4uLL);
  v1 = strtol(buf, 0LL, 10);
  if ( v1 > 0xF )
  {
    puts("Index error.");
  }
  else if ( note_array[v1] )
  {
    puts((const char *)note_array[v1]);
  }
  else
  {
    puts("The nkctf note to be printed was not created.");
  }
  return __readfsqword(0x28u) ^ v3;
}
__int64 __fastcall my_read(__int64 a1, int a2)
{
  unsigned int v3; // [rsp+10h] [rbp-10h]
  int i; // [rsp+14h] [rbp-Ch]
 
  for ( i = 0; i <= a2; ++i )//逻辑错误导致1个字节的溢出
  {
    v3 = read(0, (void *)(i + a1), 1uLL);
    if ( *(_BYTE *)(i + a1) == 10 )
      break;
  }
  return v3;
}
__int64 __fastcall my_read(__int64 a1, int a2)
{
  unsigned int v3; // [rsp+10h] [rbp-10h]
  int i; // [rsp+14h] [rbp-Ch]
 
  for ( i = 0; i <= a2; ++i )//逻辑错误导致1个字节的溢出
  {
    v3 = read(0, (void *)(i + a1), 1uLL);
    if ( *(_BYTE *)(i + a1) == 10 )
      break;
  }
  return v3;
}
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2023-3-26 23:16 被LeaMov编辑 ,原因: 错别字
上传的附件:
收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//