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
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.
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.
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.
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
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
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.
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.
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.
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.
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.
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.
This results in this solve script.
Last updated