In generating an executable from the code we can use the maximum potential to make the application safer by understanding and enabling the security and protection features. These features could include activating specific types of warnings or adding procedures to the executable file to manage unexpected situations that may arise as a result of flaws or vulnerabilities being exploited. In the following sections, we will look at some characteristics, how they work, and the role of compilers in secure code.
Stack Protection
One of the most common vulnerability types is buffer overflow, which occurs when a program writes data beyond the bounds of allocated memory, typically on the stack. Compilers have built-in protections to mitigate stack buffer overflows, also known as stack protection or stack smashing protection. These protections either provide checks to ensure the stack is not tampered with or mark stack memory as non-executable.
Example – Stack Protector in GCC: GCC (GNU Compiler Collection) implements the “-fstack-protector” and “-fstack-protector-strong” flags to enable stack protection. When these flags are used, the compiler inserts code to check the integrity of the stack at runtime.
// Compile with: gcc -fstack-protector stack_protection.c -o smashing
// Run: ./smashing TryToAccessOutOfBoundary
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void *__stack_chk_guard = (void *)0xdeadbeef;
void __stack_chk_fail(void)
{
fprintf(stderr, "Stack smashing detected!!!\n");
exit(1);
}
void copyInputData(char *input_data);
int main(int argc, char **argv)
{
if (argc > 1) {
printf("Usage: %s <input_string>\n", argv[1]);
copyInputData(argv[1]);
}
printf("\nTerminated normally!\n");
return 0;
}
void copyInputData(char *input_data)
{
char buffer[8];
strcpy(buffer, input_data);
}
The “-fstack-protector” flag can be used to protect the code against stack buffer overflows.
Address Space Layout Randomization (ASLR)
Another security option available in GCC is the possibility to randomize the memory address of the application which makes it difficult for attackers to find their target address. This option by randomizing the memory addresses of Stack, Heap, and Shared libraries tries to protect the programs to be an easy and simple target of hackers.
Example – ASLR in GCC: To enable ASLR using GCC for your program, you need to compile it as a Position-Independent Executable (PIE). You can do this using the “-pie” and “-fPIE” flags.
- The first implementation of ASLR added to the kernel in 2001 as a patch
- From Android 5.0 the non-PIE is dropped and all the dynamically-linked libraries must support position independence.
// Compile with: gcc -fPIE -pie stack_protection.c -o smashing_nx
#include <stdio.h>
void vulnerable_function() {
printf("This function could be vulnerable to buffer overflows or other memory corruption attacks.\n");
}
int main() {
vulnerable_function();
return 0;
}
In this example, ASLR is enabled by compiling the code with the “-fPIE” and “-pie” flags.
Library order randomization
In November 2003 a new feature OpenBSD 3.4 introduced with a new feature. This new feature randomized the order of loading libraries. A long time after that in August 2016 it was added to Android Nougat. Though it was not a big feature but a bit of improvement alongside ASLR.
Data Execution Prevention (DEP)
To reduce the attack surface and protect software from code injection/ overflow techniques there is an option in CPU called NX or Non-Excutable. This feature that exists in most of the CPUs helps to prevent running the code from non-executable regions of memory. Then Operating system by marking the pages as non-executable prevents running any code from the data area including stack and heap. This feature can be emulated if does not exist in hardware. From Android 2.3 it accessible for any architecture supporting it. Since May 2010 the NX has been enabled by default in Android to lock lots of overflow attacks.
GNU compiler = -z noexecstack prelink package s= –noexecstack
Example – DEP in GCC: To enable DEP using GCC, you can use the “-z noexecstack” flag during the linking stage.
Compile with:
gcc -z execstack stack_protection.c -o smashing
gcc -z noexecstack stack_protection.c -o smashing_nx
Compare the result of the commands below:
readelf -l smashing | grep GNU_STACK -B1
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RWE 0x10
readelf -l smashing_nx | grep GNU_STACK -B1
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
As you can see the second one “smashing_nx” marked as “RW” while the first one is “RWE”
Compiler Warnings
Each compiler supports different levels of warnings that can be utilized to identify the probable problems, issues or vulnerabilities. It’s critical to enable, check and try to fix them since any of the warnings might be a flaw in the code causing a hole.
Example – Wall Flag in GCC: In GCC, the “-Wall” flag enables warnings about various issues in the source code. Some of these warnings are related to potential security vulnerabilities.
// Compile with: gcc -Wall warning_example.c -o output
#include <stdio.h>
#include <stdlib.h>
int main() {
char *str = malloc(50);
printf("This code might generate some compiler warnings related to security.\n");
return 0;
}
The “-Wall” flag enables compiler warnings that could be related to security vulnerabilities in the code, such as memory leaks or unsafe functions.
As I’ve already indicated, using various compiler options and parameters can help to provide more reports, such as warnings or errors. In addition, turning on ASLR, DEP, etc., can aid in making our code more difficult to attack. Although they are not perfect, new tools and compilers offer more and more effective ways to safeguard our software. We must remain updated if we want to use the most recent ones. It is progress.
Link to Book: Secure Android Development: Best Practices for Robust Apps