buffer overrun

Intro

It’s been a while since I last read the notorious Phrack Issue 49 Article By Aleph1: Smashing The Stack For Fun And Profit. In my daily work as a JS/C# dev I almost never think about these things. But recently, I was dabbling in some C code relating to my toy OS. I implemented the strcpy function which copies one char array to another. Simple enough, right?
Well… No.

Buffer Overruns

Example Scenario

Consider the following code using strcpy:

#include <string.h>

void not_good(char *str)
{
   char buff[8];
   strcpy(buff, str);
}

⚠️ not_good just copies whatever is in str to buff, without checking if buff has enough space allocated to hold the value!

The Stack

Let’s consider what the stack looks like given different values for str. We assume a stack that growns down btw.

Input: Hello

Stack looks okay

Input: AAAAAAAAAAAAAAAA\x08\x35\xC0\x80

Stack values are overwritten

Exploitation

In the second scenario, a potential attacker was able to successfully overwrite the return value to \x08\x35\xC0\x80 which is little endian for 0x80C03506. They now have taken over the flow of control of our application. If the values at address0x80C03506 can be controlled by the attacker, e.g., if it points to the address of str, bad things can happen: the attacker can execute any code they wish with the privileg of our application 😱.

Stack Smashing Protector

Fortunately, modern compilers have ways to try and prevent this kind of attack, like GCC’s stack smashing protector option. All you need to do is add the CFLAG -fstack-protector. It adds a secret value to the stack and checks if it was changed at runtime. If it was, a callback function is executed. This turns our previous not_good function into:

extern uintptr_t __stack_chk_guard;
noreturn void __stack_chk_fail(void);
void good(const char* str)
{
	uintptr_t secret_value = __stack_chk_guard;
	char buffer[12];
	strcpy(buffer, str);
	if ( (secret_value = secret_value ^ __stack_chk_guard) != 0 )
		__stack_chk_fail();
}

GCC added __stack_chk_guard, the secret value and __stack_chk_fail(), the callback in case the secret value was changed.
Note 1: This code would most likely be “optimized away” if you added it manually.
Note 2: Note that the secret value is removed from the stack as part of the check.

In LonelyOS

GCC implements __stack_chk_fail and __stack_chk_guard in libssp/ssp.c. Of course the libc in LonelyOS doesn’t have this. And copy/pasting seemed kind of lame, so I went with the minimal implementation given on OSDev:

#include <stdlib.h>
#include <stdio.h>

#if UINT32_MAX == UINTPTR_MAX
#define STACK_CHK_GUARD 0x10101010
#else
#define STACK_CHK_GUARD 0x1010101010101010
#endif
 
unsigned long __stack_chk_guard = STACK_CHK_GUARD;
 
__attribute__((noreturn))
void __stack_chk_fail(void)
{
#if defined(__is_libk)
	// TODOC: Add proper kernel panic.
	printf("kernel: panic: __stack_chk_fail()\n");
#else
    // TODOC: Abnormally terminate the process?? Hit hacker with a stick?
	printf("__stack_chk_fail()\n");
#endif
}

Outlook

The “secret value” in LonelyOS is currently just a bunch of A’s. It would be great to randomize this value on each boot. Also, LonelyOS does not yet have a proper kernel panic implementation, so currently we just printf(). Hopefully I’ll find some time to fix this in the future.
But next up will probably be interrupt handling – I really want the ability to type things 🤷‍♀️

so long

comments powered by Disqus