Common Security Vulnerabilities in Software: Risks and Countermeasures

Lishan

Lishan Dissanayake

2025-02-26

1. Program Security

Program security refers to the practice of designing, developing, and maintaining software applications in a way that prevents unauthorized access, data breaches, and malicious exploitation. Security vulnerabilities in programs can lead to serious consequences, such as data theft, system compromise, and service disruptions. Ensuring program security is crucial in an era where cyber threats are becoming increasingly sophisticated.

Software vulnerabilities can be exploited by attackers to manipulate applications, steal sensitive information, or cause system failures. Therefore, security must be an integral part of the software development lifecycle (SDLC) to minimize risks.

Key Security Properties

A secure program should adhere to three fundamental properties:

  1. Confidentiality – Ensuring that sensitive information is only accessible to authorized users. Attackers should not be able to gain access to confidential data stored in or processed by a program.
  2. Integrity – Ensuring that data and system functions remain unaltered by unauthorized entities. Programs should continue to function correctly even in the presence of an attacker.
  3. Availability – Ensuring that authorized users can access the system and its resources without disruptions caused by malicious attacks.

Incorporating these properties into software development helps build robust applications that are resistant to attacks and data breaches.

 

2. Common Security Vulnerabilities in Programs

Various vulnerabilities can exist in programs due to coding errors, inadequate validation mechanisms, or misconfigurations. Below are some of the most common security vulnerabilities that attackers exploit:

1. Buffer Overflow

Buffer overflow occurs when a program writes more data to a buffer (a temporary storage location in memory) than it was designed to hold. This overflow can overwrite adjacent memory locations, leading to unexpected behavior, crashes, or even code execution by an attacker.

Example of Buffer Overflow Attack:

  • The Morris Worm exploited a buffer overflow in the fingerd service, infecting 10% of the early Internet’s machines.
  • SQL Slammer spread rapidly by exploiting a buffer overflow in Microsoft SQL Server.

2. SQL Injection Attack

SQL Injection (SQLi) occurs when an attacker manipulates SQL queries by injecting malicious input into a web application's database query. This can allow unauthorized access to data, database modification, or even deletion of critical records.

Example of SQL Injection Attack:

  • Attackers enter " OR 1=1" in login forms to bypass authentication.
  • Malicious input such as DROP TABLE Users; can be used to delete entire databases.

3. Incomplete Mediation

Incomplete mediation refers to cases where user input is not properly validated before being processed. Attackers can exploit such vulnerabilities by submitting unexpected input, bypassing restrictions, or manipulating hidden form fields in web applications.

Example of Incomplete Mediation Attack:

  • A website might restrict date inputs in a calendar but still accept out-of-range values through direct URL manipulation, potentially breaking the application.

4. Time-of-Check to Time-of-Use (TOCTTOU) Errors

TOCTTOU vulnerabilities arise when a program checks a condition before performing an operation, but the system state changes between the check and execution. This is particularly dangerous in multi-threaded or multi-user environments.

Example of TOCTTOU Attack:

  • A file access permission check occurs, but before the file is opened, an attacker swaps it with a different file, bypassing security restrictions.

5. Format String Vulnerabilities

Format string vulnerabilities occur when a program improperly handles user-supplied input in functions like printf(), sprintf(), or fprintf(). If an attacker can control the format string, they can read arbitrary memory contents or even write to memory locations, leading to serious security breaches such as information leakage, memory corruption, or code execution.

Example of Format String Attack:

  • Attackers use %x%x%x to leak stack memory contents, which may reveal sensitive data like passwords or memory addresses.
  • Using %n, an attacker can overwrite specific memory locations, potentially altering control flow or escalating privileges.

Now, let’s have a detailed look at each of the above vulnerabilities.

 

3. Buffer Overflow Attacks

A buffer overflow occurs when a program writes more data to a buffer—a contiguous block of memory—than it can hold. This excess data can overwrite adjacent memory, leading to unpredictable behavior, crashes, or the execution of malicious code. Buffer overflows typically result from inadequate bounds checking during operations like copying or concatenating strings.

A computer screen shot of a computer code

AI-generated content may be incorrect.

In this example, the buffer array can hold 10 characters, but strcpy copies a longer string into it, causing a buffer overflow.

Famous Buffer Overflow Attacks

  • Morris Worm: In 1988, the Morris Worm became one of the first widely recognized buffer overflow attacks. It exploited a vulnerability in the Unix finger service, allowing the worm to spread rapidly and cause significant disruptions across the early internet.
  • CodeRed: The CodeRed worm, identified in 2001, targeted Microsoft's Internet Information Services (IIS) by exploiting a buffer overflow in the indexing service. This allowed the worm to execute arbitrary code, deface websites, and propagate itself to other servers.
  • SQL Slammer: SQL Slammer, also in 2001, was a fast-spreading worm that exploited a buffer overflow in Microsoft's SQL Server. The worm generated random IP addresses to infect, causing network congestion and significant disruptions within minutes.
  • Sasser and Conficker: Sasser (2004) and Conficker (2008) are worms that exploited buffer overflow vulnerabilities in Windows operating systems. Sasser targeted a flaw in the Local Security Authority Subsystem Service (LSASS), while Conficker exploited a vulnerability in the Windows Server service, leading to widespread infections and network disruptions.

 

Memory Exploits and Stack Buffers

In many programming languages, especially low-level ones like C and C++, memory management is manual, and buffers are often allocated on the stack. A stack-based buffer overflow occurs when a program writes more data to a stack-allocated buffer than it can hold, potentially overwriting critical control information such as return addresses.

Stack Layout Illustration:

 

In this layout, overflowing the buffer can overwrite the return address, allowing an attacker to redirect the program's execution flow.

 

Techniques Used in Buffer Overflow Attacks

1. Smashing the Stack

"Smashing the stack" involves overwriting the stack memory, particularly the return address, to redirect execution to malicious code. By crafting input data that exceeds the buffer's capacity, an attacker can manipulate the return address to point to their payload.

Example:

A computer screen shot of text

AI-generated content may be incorrect.

Using gets, which doesn't check input length, allows an attacker to input data that overwrites the return address on the stack.

 

2. Variable Overflow

Variable overflow occurs when an attacker overwrites variables adjacent to a buffer in memory, altering the program's behavior.

A pink rectangular object with black text

AI-generated content may be incorrect.

Example:

A computer code on a dark background

AI-generated content may be incorrect.

Here, the overflowing buffer can modify is_admin, granting unauthorized privileges.

 

3. Pointer Variable Manipulation

By overflowing a buffer, an attacker can overwrite function pointers or other critical pointers, redirecting execution flow.

A diagram of a code

AI-generated content may be incorrect.

Example:

A computer code with colorful text

AI-generated content may be incorrect.

Overflowing buffer can overwrite func_ptr, causing it to point to malicious code.

 

4. Frame Pointer Attack

Frame pointer attacks involve overwriting the frame pointer (base pointer) to manipulate the call stack, allowing attackers to control program execution.

A diagram of a computer program

AI-generated content may be incorrect.

Example:

A computer screen shot of code

AI-generated content may be incorrect.

In this scenario, overflowing buffer can overwrite the saved frame pointer, enabling an attacker to alter the execution flow upon function return.

 

5. Integer Overflow

Integer overflow occurs when an arithmetic operation results in a value exceeding the storage capacity of the integer type, potentially leading to buffer overflows.

Example:

 

 

Buffer Overflow Prevention Methods

 

1. Canary Values

One effective technique for preventing buffer overflow attacks is the use of canary values. In the given code, CANARY_VALUE is a predefined constant (0xABCD1234) placed before the buffer inside the SecureFunction structure. Before executing any critical operations, the program checks whether the canary value has been altered. If an overflow occurs and an attacker attempts to overwrite memory beyond the buffer's limit, the canary value is likely to change. The program detects this change and immediately terminates execution (exit(1);), preventing potential exploitation. This method provides a simple yet effective mechanism for identifying and mitigating stack-based buffer overflows.

 

2. Bounds Checking

Another crucial defense mechanism against buffer overflows is bounds checking, which ensures that data written to a buffer does not exceed its allocated size. In the code, the function performOperation() checks the length of the user-provided input before copying it into the buffer (if (strlen(input) >= BUFFER_SIZE)). If the input is too large, the program halts execution with an error message. Additionally, strncpy() is used instead of strcpy() to limit the number of bytes copied, preventing accidental buffer overflow. The string is also explicitly null-terminated (secureFunc->buffer[BUFFER_SIZE - 1] = '\0'), ensuring that even if the input is exactly the buffer's size, it remains properly terminated.

 

3. Tagging Memory

Tagging memory involves assigning special markers or metadata to memory regions to prevent execution of unintended code. While the provided code does not explicitly use memory tagging, it simulates the concept by encapsulating the buffer inside a structured data type (SecureFunction). This method ensures that additional metadata (such as the canary value) is stored alongside the buffer. More advanced implementations might use memory protection techniques, such as marking certain memory regions as non-executable or using hardware-based tagging to prevent malicious code execution. Combined with canary values and bounds checking, memory tagging adds another layer of security against buffer overflow attacks.

 

4. SQL Injection Attacks

SQL Injection (SQLi) is a code injection technique where an attacker inserts malicious SQL statements into an input field, aiming to execute unauthorized commands on a database. This exploitation typically occurs when user inputs are not properly sanitized, allowing attackers to manipulate queries and access, modify, or delete data.

Example of Vulnerable Code in Node.js

In this app.post("/login") function, the SQL query is constructed by directly embedding user input:

A computer screen shot of code

AI-generated content may be incorrect.

If an attacker inputs admin' -- as the username and leaves the password blank, the query becomes:

A computer screen with text

AI-generated content may be incorrect.

The double dash (--) signifies a SQL comment, which causes the password check to be ignored, potentially granting unauthorized access.

 

Different Types of SQL Injection Attacks

Type 1: Always True Condition (1=1)

Attackers inject conditions that are always true, retrieving unintended data.

Example:

If the attacker enters 105 OR 1=1 as the username, the query becomes:

A computer screen with a number of text

AI-generated content may be incorrect.

Since 1=1 is always true, the query returns all records from the users table, potentially exposing sensitive user data.

 

Type 2: Bypassing Authentication (""="")

By injecting conditions that always evaluate to true, attackers can bypass authentication.

Example:

If the attacker inputs ' OR ''=' as both the username and password, the query becomes:

A black background with white text

AI-generated content may be incorrect.

Since ''='' always evaluates to true, the database retrieves all user records, allowing the attacker to log in without valid credentials.

 

Type 3: Batched SQL Statements (Executing Multiple Commands)

Some databases allow multiple SQL statements in a single query, enabling attackers to execute arbitrary commands.

Example:

If an attacker enters '; DROP TABLE users; -- as the username, the query becomes:

A screenshot of a video game

AI-generated content may be incorrect.

The DROP TABLE users; statement deletes the entire users table, causing permanent data loss.

 

Preventive Measures Against SQL Injection

1. Use Prepared Statements (Parameterized Queries)

This login function prevents SQL injection by using parameterized queries:

A computer screen shot of text

AI-generated content may be incorrect.

Why is this secure?

  • User input is treated as data, not part of the SQL statement.
  • The database safely binds user input to placeholders (?), preventing malicious SQL execution.

 

2. Implement Input Validation

  • Use regular expressions to restrict allowed characters in usernames and passwords.
  • Reject inputs containing SQL keywords, special characters, or suspicious patterns.

Example: Restricting User Input

A computer code with colorful text

AI-generated content may be incorrect.

 

3. Apply the Least Privilege Principle

  • Restrict database user permissions (e.g., no DROP or DELETE permissions for web applications).
  • Use a separate database account for web applications with limited privileges.

Example: Granting Least Privilege to a MySQL User

This prevents the web application from executing DELETE, DROP, or ALTER commands even if it’s compromised.

SQL Injection remains one of the most dangerous vulnerabilities in web applications. Attackers exploit poor input handling to bypass authentication, steal data, and execute arbitrary commands. By implementing the above security measures, we can protect applications from SQL Injection attacks and safeguard user data.

 

5. Incomplete Mediation Attacks

Incomplete mediation occurs when an application fails to properly validate user input, allowing malicious data to pass through and potentially exploit the system. This oversight can lead to unauthorized data access, privilege escalation, or code execution.

Example:

A web application expects a date input in the format YYYY-MM-DD. If the application does not validate this input, an attacker could input 2024-02-30 or even inject scripts, leading to unexpected behavior or security breaches.

How Attackers Exploit Input Validation Issues

Attackers manipulate insufficiently validated inputs to alter program execution or access unauthorized data. Common exploitation methods include:

  • SQL Injection: Injecting malicious SQL code through unvalidated input fields.
  • Cross-Site Scripting (XSS): Inserting malicious scripts into web pages viewed by other users.
  • Buffer Overflow: Providing input that exceeds buffer limits, leading to memory corruption.

Countermeasures for Incomplete Mediation

  • Server-Side Validation: Always validate and sanitize inputs on the server side, as client-side validation can be bypassed.
  • Whitelist Input Validation: Define acceptable input criteria and reject anything that does not conform.
  • Regular Expression Filtering: Use regex patterns to enforce input formats.
  • Error Handling: Implement comprehensive error handling to prevent leakage of system information.
  • Security Audits and Testing: Regularly perform security assessments, including code reviews and penetration testing, to identify and remediate validation issues.

By understanding and addressing these vulnerabilities, developers can enhance the security of their applications, protecting both the system and its users from potential attacks.

 

6. Time-of-Check to Time-of-Use (TOCTTOU) Errors

TOCTTOU errors are a class of race condition vulnerabilities that occur when a system checks a resource's state (time-of-check) and then uses that resource (time-of-use) with the assumption that its state remains unchanged. If an attacker can alter the resource's state between the check and use phases, they can exploit this window to perform unauthorized actions. This issue is prevalent in file system operations, where the file's state can change between verification and access.

Real-World Example (File Access Vulnerability)

Consider a scenario where a program verifies a user's permission to write to a file before opening it:

A computer screen shot of code

AI-generated content may be incorrect.

In this code, there's a window between the access check and the open call where the file's state can be altered. An attacker could exploit this by replacing example.txt with a symbolic link to a sensitive file, such as /etc/passwd, during this window. The program would then inadvertently open and potentially modify the sensitive file.

 

How an Attack Works

An attacker exploiting a TOCTTOU vulnerability typically follows these steps:

  1. Monitor the Target Program: Identify a program that performs a check on a resource before using it.
  2. Create a Race Condition: Manipulate the system's state between the check and use phases.
  3. Exploit the Window: Replace or modify the resource (e.g., file) after the check but before its use, leading the program to operate on an unintended resource.

This exploitation requires precise timing to successfully interleave the attacker's actions with the program's operations.

 

Prevention Techniques

Preventing TOCTTOU errors involves ensuring that the resource's state cannot change between the check and use phases. Some strategies include:

  • Use Atomic Operations: Employ system calls that combine checking and usage into a single atomic operation. For example, using open() with appropriate flags can ensure the file is created and opened securely.
  • File Descriptors: Operate directly on file descriptors obtained from a secure open() call, rather than repeatedly accessing files by name. This approach reduces the risk of the file being altered between operations.
  • Avoid Insecure Functions: Refrain from using functions like access() that perform separate checks, as they are susceptible to race conditions.
  • Implement Proper Error Handling: Design programs to handle unexpected changes gracefully, ensuring that even if a resource's state changes, it doesn't lead to insecure behavior.

By adopting these practices, developers can mitigate the risks associated with TOCTTOU vulnerabilities.

 

7. Format String Vulnerabilities

Format string vulnerabilities arise when user input is improperly handled in functions that process format strings, such as printf in C. If user-supplied data is used directly as the format string without validation, attackers can manipulate the format specifiers to read from or write to memory, leading to information disclosure or code execution.

Vulnerable Code Example:

A computer code with white text

AI-generated content may be incorrect.

In this example, if user_input contains format specifiers, they will be processed by printf, potentially leading to unintended behaviour.

 

Reading from Stack Using %x%x%x

Attackers can use format specifiers like %x to read values from the stack:

A computer code on a dark background

AI-generated content may be incorrect.

If user_input contains %x %x %x, the program will print the hexadecimal values of the next three items on the stack, potentially revealing sensitive information.

 

Writing to Stack Using %n

The %n format specifier writes the number of characters printed so far into a variable. If exploited, it can allow attackers to write arbitrary values to specific memory addresses:

This code sets target to 5, as five characters were printed before %n. An attacker controlling the format string and the corresponding arguments can write arbitrary values to memory, leading to potential code execution.

 

Exploiting Format Strings for Arbitrary Code Execution

To achieve arbitrary code execution, an attacker can:

  1. Determine Memory Layout: Use format specifiers to read stack and memory contents, mapping out the program's memory.
  2. Overwrite Function Pointers or Return Addresses: Utilize %n to write specific values to memory locations that control execution flow, such as function pointers or return addresses.
  3. Inject Malicious Code:

 

Preventing Format String Vulnerabilities

Format string vulnerabilities can lead to memory leaks, crashes, and code execution. To prevent them:

  1. Use Explicit Format Specifiers: Always define a format string explicitly to prevent user input from being interpreted as a format.
  2. Use Secure Functions: Prefer safer functions that enforce buffer limits and prevent unintended memory manipulation.
  3. Enable Compiler Security Features: Use compiler warnings, stack protection, and runtime sanitizers to detect vulnerabilities.
  4. Validate and Sanitize Input: Restrict user input and filter out dangerous format specifiers to prevent exploitation.

Following these best practices ensures robust protection against format string attacks.

 

8. Conclusion

Program security is a critical aspect of modern software development, as vulnerabilities such as buffer overflow, SQL injection, TOCTTOU errors, and format string attacks can be exploited to compromise system integrity, confidentiality, and availability. Understanding these vulnerabilities and their underlying mechanisms allows developers to implement robust security measures to mitigate risks effectively.

We explored buffer overflow attacks, which exploit improper memory management to overwrite critical data, and SQL injection attacks, where malicious SQL commands can be injected into user inputs to manipulate database queries. Additionally, TOCTTOU errors demonstrated how race conditions between resource checking and access can lead to unauthorized actions, while format string vulnerabilities exposed how improper handling of user-supplied format specifiers can result in arbitrary code execution.

To counteract these threats, we discussed several prevention techniques:

  • Buffer Overflow Prevention: Implementing stack canaries, bounds checking, and memory tagging.
  • SQL Injection Prevention: Using prepared statements, input validation, and sanitization techniques.
  • TOCTTOU Mitigation: Avoiding separate check-and-use steps, employing file descriptors, and ensuring atomic operations.
  • Secure Memory Management: Utilizing smart pointers, freeing allocated memory, and avoiding unsafe functions.

By integrating these best practices into the software development lifecycle, programmers can build secure applications that are resilient to attacks.

 

References

[1] M. Howard and D. LeBlanc, Writing Secure Code, 2nd ed. Redmond, WA, USA: Microsoft Press, 2002.

[2] Aleph One, "Smashing the Stack for Fun and Profit," Phrack Magazine, vol. 7, no. 49, Nov. 1996. [Online]. Available: https://www.phrack.org/issues/49/14.html

[3] C. Cowan et al., "StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks," in Proceedings of the 7th USENIX Security Symposium, San Antonio, TX, USA, Jan. 1998.

[4] R. Seacord, Secure Coding in C and C++, 2nd ed. Boston, MA, USA: Addison-Wesley, 2013.

[5] J. Viega and G. McGraw, Building Secure Software: How to Avoid Security Problems the Right Way. Boston, MA, USA: Addison-Wesley, 2001.

[6] "Common Weakness Enumeration (CWE) - Buffer Overflow," MITRE, 2023. [Online]. Available: https://cwe.mitre.org/data/definitions/120.html

[7] CERT Coordination Center, "Buffer Overflow Attacks," Carnegie Mellon University, 2022. [Online]. Available: https://www.cert.org/articles/buffer-overflow-attacks

[8] "SQL Injection," OWASP Foundation, 2023. [Online]. Available: https://owasp.org/www-community/attacks/SQL_Injection

[9] A. D. Keromytis, "Buffer Overflow Attacks and Defenses," IEEE Security & Privacy, vol. 2, no. 1, pp. 52-58, Jan. 2004.

[10] S. Chen, J. Xu, E. Sezer, P. Gauriar, and R. K. Iyer, "Non-Control-Data Attacks Are Realistic Threats," in Proceedings of the 14th USENIX Security Symposium, Baltimore, MD, USA, 2005.

[11] M. Zalewski, The Tangled Web: A Guide to Securing Modern Web Applications. San Francisco, CA, USA: No Starch Press, 2011.

[12] "Format String Vulnerabilities," OWASP Foundation, 2023. [Online]. Available: https://owasp.org/www-community/vulnerabilities/Format_String

[13] C. Evans, N. S. Narula, and N. A. Rubin, "Buffer Overflow Prevention Using Stack Canaries," in Proceedings of the IEEE Symposium on Security and Privacy, Oakland, CA, USA, 2004.

[14] "Time-of-Check to Time-of-Use (TOCTTOU) Attacks," MITRE, 2023. [Online]. Available: https://cwe.mitre.org/data/definitions/367.html

[15] M. Bishop and M. Dilger, "Checking for Race Conditions in File Access," Computing Systems, vol. 9, no. 2, pp. 131-152, 1996.

[16] J. Pincus and B. Baker, "Beyond Stack Smashing: Recent Advances in Exploiting Buffer Overruns," IEEE Security & Privacy, vol. 2, no. 4, pp. 20-27, Jul. 2004.

[17] S. Bratus, M. E. Locasto, M. Prasad, and S. W. Smith, "The Art of Exploitation: Learning to Exploit Software Security Vulnerabilities," IEEE Security & Privacy, vol. 5, no. 4, pp. 76-79, Jul. 2007.

[18] J. C. Foster, V. Osipov, N. Bhalla, and N. Heinen, Buffer Overflow Attacks: Detect, Exploit, Prevent. Rockland, MA, USA: Syngress, 2005.

[19] R. Wojtczuk, "Defeating Solar Designer's Non-Executable Stack Patch," Phrack Magazine, vol. 8, no. 55, Aug. 1999. [Online]. Available: https://www.phrack.org/issues/55/15.html

[20] B. Schwarz, S. Debray, and G. Andrews, "Disassembly of Executable Code Revisited," in Proceedings of the 9th IEEE Working Conference on Reverse Engineering (WCRE), Richmond, VA, USA, 2002.