Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

Refreshments

​ 4th Dec 2024

​ Prepared By: 131LL

​ Challenge Author(s): 131LL

​ Difficulty: Hard

​ Classification: Official

Synopsis

Refreshments is a hard difficulty challenge that features:

  • off-by-one heap overflow
  • Leak unsorted bin addresses
  • Aggregate chunks
  • Fake vtable object
  • Remainder the "tamper" chunk
  • FSOP
  • House of Orange in glibc 2.23

Description

On a blazing summer day in the Shroom Kingdom, beat the heat with our refreshing juice – it's as bright as a Sun Flower! In our Toadstool Stand, you're not just limited to one sip. Try a mix of free samples, or channel your inner alchemist like Toadsworth by blending it with other delightful beverages. The adventure for your taste buds awaits – power up and enjoy! ✨

Skills Required

  • Advanced Heap internal, glibc 2.23.

Skills Learned

  • Remainder, FSOP and House of Orange.

Enumeration

First of all, we start with a checksec:

pwndbg> checksec
Arch:       amd64
RELRO:      Full RELRO
Stack:      Canary found
NX:         NX enabled
PIE:        PIE enabled
RUNPATH:    b'./glibc/'
SHSTK:      Enabled
IBT:        Enabled
Stripped:   No

Protections 🛡️

As we can see:

Protection Enabled Usage
Canary Prevents Buffer Overflows
NX Disables code execution on stack
PIE Randomizes the base address of the binary
RelRO Full Makes some binary sections read-only

All protections are enabled.

The program's interface

It looks like a heap challenge.

Disassembly

Starting with main():

void main(void)

{
  ulong uVar1;
  void *pvVar2;
  long lVar3;
  void **ppvVar4;
  long in_FS_OFFSET;
  byte bVar5;
  char local_81;
  void *local_78 [11];
  undefined8 local_20;
  
  bVar5 = 0;
  local_20 = *(undefined8 *)(in_FS_OFFSET + 0x28);
  setup();
  banner();
  ppvVar4 = local_78;
  for (lVar3 = 10; lVar3 != 0; lVar3 = lVar3 + -1) {
    *ppvVar4 = (void *)0x0;
    ppvVar4 = ppvVar4 + (ulong)bVar5 * -2 + 1;
  }
  local_81 = '\0';
  while( true ) {
    while( true ) {
      while( true ) {
        printf(&DAT_00101898,(ulong)(uint)(int)local_81);
        uVar1 = read_num();
        if (uVar1 != 2) break;
        printf("\nChoose glass to empty: ");
        uVar1 = read_num();
        if (uVar1 < (ulong)(long)local_81) {
          if (local_78[uVar1] == (void *)0x0) {
            error("This glass is already empty!");
          }
          else {
            free(local_78[uVar1]);
            local_78[uVar1] = (void *)0x0;
            puts("\nGlass is empty now!\n");
          }
        }
        else {
          error("This glass is unavailable!");
        }
      }
      if (uVar1 < 3) break;
      if (uVar1 == 3) {
        printf("\nChoose glass to customize: ");
        uVar1 = read_num();
        if (uVar1 < (ulong)(long)local_81) {
          if (local_78[uVar1] == (void *)0x0) {
            error("Cannot customize empty glass!");
          }
          else {
            printf("\nAdd another drink: ");
            read(0,local_78[uVar1],0x59);
            putchar(10);
          }
        }
        else {
          error("This glass is unavailable!");
        }
      }
      else {
        if (uVar1 != 4) goto LAB_00100faa;
        printf("\nChoose glass: ");
        uVar1 = read_num();
        if (uVar1 < (ulong)(long)local_81) {
          if (local_78[uVar1] == (void *)0x0) {
            error("Cannot view empty glass!");
          }
          else {
            printf("\nGlass content: ");
            write(1,local_78[uVar1],0x58);
            putchar(10);
          }
        }
        else {
          error("This glass is unavailable!");
        }
      }
    }
    if (uVar1 != 1) break;
    if ('\x0f' < local_81) {
      error("You cannot take any more glasses!");
                    /* WARNING: Subroutine does not return */
      exit(0x45);
    }
    pvVar2 = calloc(1,0x58);
    local_78[(int)local_81] = pvVar2;
    if (local_78[(int)local_81] == (void *)0x0) {
      error("Something went wrong with the juice!");
    }
    else {
      local_81 = local_81 + '\x01';
      printf("\nHere is your juice!\n\n");
    }
  }
LAB_00100faa:
  error("Have a great day!\n");
                    /* WARNING: Subroutine does not return */
  exit(0x45);
}

This is the one and only function we will focus on.

Initial Allocations

  • Call fill() five times to allocate chunks (0-4).

  • Chunk 0 is the "tamper" chunk (used for overflow).

  • Chunk 1 will be freed to place it in the unsorted bin.

  • Chunk 2 will be used to leak libc addresses.

Edit with Overflow:

  • Use edit(0) to overflow into chunk 1, modifying its metadata to merge it with chunk 2.

  • Set the size to 0xc1 to make chunk 1 appear larger.

Free the Tamper Chunk:

  • Free chunk 1 to place it in the unsorted bin (empty(1)).

  • This allows unsorted bin metadata to overwrite the content of chunk 2.

Leak libc

  • Allocate another chunk (fill(5)) to reuse the tampered chunk.
  • Use view(2) to print the libc address from the unsorted bin's metadata.

Leak Heap Address: - Free and Reallocate:

  • Allocate and free additional chunks to leak the heap address (view).

House of Orange Setup:

Corrupt Unsorted Bin:

  • Allocate a chunk and use edit to overwrite the unsorted bin's bk pointer with _IO_list_all - 0x10.

Fake _IO_FILE Structure

  • Use edit to craft a fake _IO_FILE structure with a vtable pointing to system.

Trigger Code Execution:

  • Call fill to trigger _IO_OVERFLOW, which calls the fake vtable and executes system("/bin/sh").

PoC

Running solver remotely at 0.0.0.0 1337

Libc address: 0x7f6d8f66f000
Heap address: 0x5648321bf000

Tries: 3

Flag --> HTB{XXXXXXXXXXX}