The answer to this question will be heavily dependent on several factors including the toolchain used, and the target architecture. For simplicity's sake I'll limit this answer to the Arduino Uno and the ATMega328 AVR microcontroller.
tl;dr: Yes, it is possible, but only if you include
WriteFlashPage() in your source, and if the attacker has access to your source, or physical access to your device.
Buffer Overflow Exploits
Buffer overflow exploits are fairly straightforward. The general idea is to write a shellcode payload to a known location on the stack, and overwrite the return address to point at that shellcode. This works because the x86 architecture allows execution from ram. This is not the only exploit of this type, but it is very common.
Why the architecture is important
The AVR8 architecture (the one used by the ATMega micro on the Arduino) is what is known as a Harvard Architecture. The important part to note here is that in the Harvard Architecture the "instruction memory", or "program memory" is completely independent from "data memory". In the AVR8 architecture the program memory is implemented in Flash, the data memory is implemented in SRAM, and instructions can only be fetched and executed from Flash memory. What this means is that the buffer overflow exploit outlined above won't work. You can override the return address, but the address you change will be read from Flash, and not from SRAM. Now, you might think that this would make a buffer overflow exploit impossible, but it does not.
AVR8 buffer overflow exploits
Since the only way to execute code on an ATMega328 is to execute from Flash memory, we will have to put our shellcode into Flash memory. Here, you will find an appnote with the requisite information about reading/writing to flash memory. So, now we know that it is actually possible to write a payload to program memory during runtime, but there is an issue. The only way we can write data to flash is by using either
WriteFlashPage(), and the only way to use those functions is for them to already be in the Flash memory for that device. However, not only does the device need to be programmed with one of these functions, you must also know the address where that function resides in memory. There are two ways of discovering this information:
- Compile the source using the exact same flags, then use
avr-objdump to determine if these functions are present, and what address they reside at.
- Read the contents of the Flash memory present on the micro, then use
avr-objdump to determine if these functions are present, and what address they reside at. Note that if the lock bits are set, you may not be able to read the contents of the Flash memory.
If you do not have access to the source, or you can't read the flash memory to dump it, a buffer overflow exploit of this type would be nigh on impossible.