Buffer Overflow Lab Report
Buffer Overflow Lab Report
Software Security
Lab-Report
Code: overflow_example.c
The program declares two 8-byte character arrays, buffer_one and buffer_two, and an
integer variable value initialised to 5. Here’s a breakdown of how it works:
The -fno-stack-protector flag disables GCC's built-in stack protection, which would
otherwise prevent buffer overflows. By disabling this, we can clearly see how an overflow
might affect the program's execution.
Analysis of Output
The output from running ./overflow_example 1234567890 shows the following steps:
In C, local variables are typically stored consecutively in memory on the stack. Here’s how
the memory layout of the stack looks in this example:
0x...ddec value 5
Next, we run the program with a longer input consisting of 38 "A" characters:
Breakdown of the Overflow and Its Effects:
When we pass a 38-byte input, the strcpy function blindly copies all 38 characters, causing
a much more significant overflow. This overflow overwrites not only buffer_two but also
buffer_one and the value variable. Here’s a detailed breakdown of the stack behaviour:
Initial Stack Layout: Before the overflow, buffer_two, buffer_one, and value are laid
out sequentially in memory, just like before:
[buffer_two] [buffer_one] [value]
|'two' | |'one' | |5 |
1. Overflow Effect on Stack Memory: After copying 38 bytes, the memory looks like
this:
[buffer_two] [buffer_one] [value]
Conclusion
Through these examples, I've observed how buffer overflows can have varying impacts,
from minor data corruption to program crashes. The 1234567890 input showed a subtle
effect with a minor overflow, while the long input "AAAAAAAA..." highlighted a more severe
overflow resulting in a segmentation fault.
notesearch.c is a C program designed to manage and search user notes stored in a file
(/var/notes). It allows users to search through their notes based on specific keywords.
However, the program contains vulnerabilities that can be exploited, particularly a buffer
overflow vulnerability.
Detailed Breakdown
Function Prototypes:
main Function:
print_notes Function:
○ Finding Notes: Calls find_user_note to locate a note for the given user ID.
○ Reading Notes: Reads the note into note_buffer. Vulnerability Point: If
note_length is greater than 99, note_buffer can overflow.
○ Null-Termination: Adds a null terminator to ensure the string is properly
terminated.
○ Searching and Printing: If the note contains the search string, it prints the
note.
find_user_note Function:
Reading User ID: Continuously reads user IDs from the file until it finds a match.
○ Reading Note Length: After finding the matching user ID, it reads bytes until it
encounters a newline character, calculating the length of the note.
○ Seeking Back: Uses lseek to move the file pointer back by the length of the
note, preparing to read the actual note content.
○ Debugging Output: Prints a debug message indicating the size and user ID of
the found note.
○ Return Value: Returns the length of the found note or -1 if not found.
search_note Function:
Keyword Search: Iterates through the note to find if the keyword exists within it.
○ Matching Logic: Increments the match counter when characters match and
resets appropriately.
○ Return Value: Returns 1 if the keyword is found, otherwise 0.
Vulnerabilities Identified
1. Buffer Overflow in main: The use of strcpy(searchstring, argv[1]);
without bounds checking allows an attacker to input a string longer than 99
characters, potentially overwriting adjacent memory.
2. Potential Buffer Overflow in print_notes: Reading note_length bytes into
note_buffer[100] without verifying if note_length exceeds the buffer size can
lead to overflow.
Understanding exploit_notesearch.c
Code: exploit_notesearch.c
Overview
Shellcode Definition:
main Function:
Exploit Mechanics
1. Buffer Construction:
○ The exploit constructs an input string that starts with ./notesearch ',
followed by a buffer that contains:
■ A series of return addresses pointing back into the buffer where the
shellcode resides.
■ A NOP sled to provide a landing area for the execution flow.
■ The actual shellcode that spawns a shell.
2. Exploitation Process:
○ Buffer Overflow Trigger: When notesearch processes the input string, it
uses strcpy to copy the malicious input into a fixed-size buffer
(searchstring[100]), exceeding its capacity.
○ Overwriting Return Address: The overflow overwrites the return address on
the stack with the address pointing to the NOP sled within the buffer.
○ Shellcode Execution: When the function returns, it jumps to the overwritten
return address, slides through the NOP sled, and executes the shellcode,
spawning a shell.
Step-by-Step Interaction
● The code did not execute the way it was supposed to. Disabled all detections, still did
not work, also checked using the gdb, but still the return address was 0x00,
technically not writing the shellcode we have inserted.
● Also , I tried with different shell code and also NOP ,but it still did not work.
● Tried it with gcc 3.3.6 , still did not work
Conclusion
Key Takeaways:
3. Authentication overflow
Understanding Auth_overflow.c
Code: auth_overflow.c
Overview
The code auth_overflow.c provided implements a simple C program designed to check if a
password input matches one of two predefined passwords. Here’s a breakdown of what
each part of the code does:
Function: check_authentication
Function Purpose:
● Variables:
○ int auth_flag = 0;: Initialises a variable named auth_flag to 0. This
variable will be used as a flag to indicate whether authentication was
successful (set to 1 if a password match is found).
○ char password_buffer[16];: Declares a buffer with a size of 16 bytes to
store the password input.
● Password Copying (Potential Buffer Overflow):
○ strcpy(password_buffer, password);: This function copies the
content of password into password_buffer.
○ Issue: strcpy does not check the length of password before copying it into
password_buffer. If the password is longer than 16 characters, this will
cause a buffer overflow, potentially overwriting adjacent memory, including
the auth_flag variable, and introducing a security vulnerability.
● Password Comparison:
○ if(strcmp(password_buffer, "brillig") == 0): Compares
password_buffer with the string "brillig". If they match, auth_flag is
set to 1.
○ if(strcmp(password_buffer, "outgrabe") == 0): Compares
password_buffer with the string "outgrabe". If they match, auth_flag
is also set to 1.
○ If either comparison is successful, auth_flag will be 1, indicating
successful authentication.
● Return Statement:
○ return auth_flag;: Returns the value of auth_flag to indicate the result
of the authentication check. A return value of 1 means authentication
succeeded, while 0 means it failed.
Purpose of main:
The main function is the program’s entry point. It expects the user to provide a single
command-line argument (the password) when running the program. This argument is then
passed to the check_authentication function to determine if access should be granted.
Breakdown:
if(argc < 2): Checks if there are fewer than 2 command-line arguments
(argc is less than 2).
i. If argc is less than 2, the program outputs the correct usage format
and exits with exit(0). This ensures the user provides a password
as an argument.
Authentication Check:
Analysis of the result and sets taken when debugging with gdb:
1. Set Breakpoints:
○ break 9: Sets a breakpoint at line 9 of auth_overflow.c.
○ break 16: Sets another breakpoint at line 16 of auth_overflow.c.
2. Run Program with Input:
○ run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: Starts the program with an
input of 30 'A' characters, likely to trigger a buffer overflow in the
password_buffer.
3. First Breakpoint (Line 9):
○ check_authentication (password=0x7fffffffe667 'A'
<repeats 30 times>): Indicates that the check_authentication
function is called with a password input that contains 30 'A' characters. Line
9 in the code contains a strcpy operation where password is copied into
password_buffer, setting up for a potential overflow if password_buffer
is not large enough.
4. Inspect Memory:
○ x/s password_buffer: Examines the password_buffer contents,
showing it holds no data initially.
○ x/w &auth_flag: Examines the auth_flag variable, showing it’s set to
0x00, indicating no access granted at this point.
○ print 0x7fffffffe27c - 0x7fffffffe260: Calculates the size of
password_buffer, showing 28 bytes, which may be smaller than the 30 'A's
passed, leading to overflow.
5. Memory View in Hex:
○ x/16xw password_buffer: Displays password_buffer in hex format
with 16 words (4 bytes each). Initially, password_buffer contains zeros,
and there's no visible overflow at this point.
6. Continue Execution:
○ cont: Continues the program until the next breakpoint.
7. Second Breakpoint (Line 16):
○ Execution halts at line 16 in auth_overflow.c, where it checks if
auth_flag is set, to grant or deny access.
○ x/s password_buffer: Shows password_buffer contains 30 'A'
characters, confirming overflow.
○ x/w &auth_flag: auth_flag is now set to 0x41 (ASCII 'A'), meaning the
buffer overflow has overwritten auth_flag.
8. Memory Dump of password_buffer:
○ x/16xw password_buffer: Memory of password_buffer now shows
0x41414141 values (hex for 'AAAA') and some additional overwritten values,
showing a clear overflow past the buffer’s bounds.
9. Display auth_flag:
○ x/4cb &auth_flag: Displays auth_flag in character format as 'A',
confirming that it has been altered from its initial state.
10. Access Granted:
○ The program continues, and the output message Access Granted.
confirms that the overflow exploit succeeded, setting auth_flag to a value
that granted access.
Conclusion
This code has a significant security vulnerability due to the use of strcpy to copy
password into password_buffer without checking its length. If a user provides a
password longer than 16 characters, it will overflow password_buffer and can overwrite
adjacent memory. Since auth_flag is located close to password_buffer in memory, it
could be overwritten by an attacker.
4. Authentication overflow2
Understanding Auth_overflow2.c
Code: auth_overflow2.c
You might wonder what the difference is between auth_overflow one and two. Well,
here’s the key: in version two, by putting auth_flag before password_buffer in memory,
we prevent the buffer overflow from corrupting the return address or other critical control
points. This small change limits the overflow’s impact—while it might still overwrite
auth_flag, it can’t reach the return address anymore, so the program flow is safer. It’s a
simple tweak, but it reduces the severity of the security risk by making it harder for an
overflow to hijack program execution.
Code Overview
Compared to the previous version of the code, this version makes a subtle but important
change that improves security. Here’s why:
1. Reordering of Variables:
○ In this version, the auth_flag variable is declared after password_buffer
in the check_authentication function.
○ Since auth_flag is placed after password_buffer in memory, a buffer
overflow on password_buffer could overwrite only auth_flag but would
not reach the return address or other critical control points.
2. Prevention of Return Address Overwrite:
○ In the previous version, the auth_flag variable was declared first, placing it
before password_buffer in memory. This allowed an overflow in
password_buffer to potentially overwrite the function’s return address,
creating a vulnerability where an attacker could control program execution.
○ By moving auth_flag after password_buffer, this version of the code
reduces the risk of such an exploit. The overflow might still affect
auth_flag, but it won't impact the return address, making the program more
resistant to attacks.
When we ran the code , this is the result of the compilation and gdb:
In this updated GDB output, we can see that despite the buffer overflow attempt, the
auth_flag remains at 0x00, meaning it has not been overwritten by the overflowed data.
Here’s a summary explaining why this is happening:
Analysis Summary
1. Position of auth_flag:
○ In this code, auth_flag is located just after password_buffer in memory.
○ However, the overflowed data (repeating 0x41 or ASCII 'A') did not reach
auth_flag this time, leaving it untouched as 0x00.
2. Memory Layout and Overflow:
○ The previous attempt demonstrated that auth_flag was overwritten with
0x41414141 (repeating As) because the overflow reached into its memory
space.
○ The repeated 0x41 values stop before reaching auth_flag, preserving its
original value of 0x00.
3. Effect on Authentication:
○ Since auth_flag remains 0x00, the authentication check fails as intended,
and no unauthorised access is granted.
○ This demonstrates that while the buffer overflow is still present, it does not
corrupt the auth_flag as it did previously, thus failing to manipulate the
program’s behaviour.
Key Takeaways
● Variable Ordering Matters: Placing sensitive variables (like auth_flag) after buffers
can help prevent critical memory corruption, such as overwriting the return address.
Conclusion
The reason the overflow fails to modify auth_flag here is due to the memory layout. The
auth_flag variable is positioned at a memory address that the overflowed data does not
reach, leaving it at 0x00. This prevents the overflow from tricking the program into granting
access, unlike in the previous version where auth_flag was overwritten.