geometry: margin=25mm title: AEV - Lab. 4 author: David Araújo - 93444, Diogo Matos - 102848, Tiago Silvestre - 103554 date: December 20, 2023
The objective of this report is to explore buffer overflows.
By running flawfinder
within the player directory, after running the `make command, the tool returns the following output.
From this, we can identify five possible flaws within the player.c code. Two of these flaws are considered "buffer flaws" and are described as: statically-sized arrays can be improperly restricted, leading to potential overflows or other issues (CWE-119!/CWE-120).
\pagebreak
16 #define FIELD_BUFFER_SIZE 16
...
49 char buffer[FIELD_BUFFER_SIZE];
...
157 static char stream[1152 * 4];
In these lines, we can see that char streams and buffers are being allocated with fixed sizes. This can be a problem in C because, depending on the way that input is being handled, it may allow a user to introduce enough input to exceed the buffer capacity, doing that may lead to the system writing this input to unpredicted spaces in the memory.
By analyzing the execution of the execution of the application, we can discover it prints the metadata of the audio files. We can then also use exiftool
to expose all the metadata and see where are the fields accessed by the application.
By then analyzing the code, we discover that one of the possible buffer overflow flows is exposed and used to print the metadata field. It then stands to reason that if one of the metadata fields were to have a sufficiently large data string, it could overflow the buffer. We can try this with the Title field.
To test this, we created a simple bash script that tries to run the music player with an increasingly large title. If the execution crashes, it outputs the title that has caused it.
\pagebreak
#!/bin/bash
TITLE="A"
while :
do
timeout 1 ./player/player ./player/music.mp3
# Check if timeout successful or execution crashed
if [[ $? -ne 124 ]]
then
echo "Buffer overflow with title of size: ${#TITLE}"
break
fi
# Increase title length
TITLE="${TITLE}A"
id3v2 --TIT2 "${TITLE}" player/music.mp3
done
This test proves successful and we get the following output.
We can do the exact same thing with increasingly large file names, which have the function that prints it using the same buffer flaw as in the metadata tags. We can then add the characters 'B' and 'C' to the end of the title to easily notice them.
Besides the exploit discovered above, there is also one way to crash the application. Which is by running it with a directory as an argument(instead of a normal file).
This crash happens because the program is not validating if the file passed as an argument is a directory. S_ISDIR(m) macro from Linux (https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html) could be used to check if it's a directory and fix the problem.
The filename is being printed as follows:
printf(" Name: ");
printf(filename);
A printf
without arguments is being used, and the user has full access to the variable filename
because is given by him. In formatted strings, the '%x' can be used to print the values in hex of a variable inside the printf
arguments. When the printf
has no arguments, it will go to the stack the get the values.
The same can be done with the metadata field because of this method:
int print_meta(char* filename) {
ID3Tag *tag = ID3Tag_New();
printf("File Information\n");
printf(" Name: ");
printf(filename);
ID3Tag_Link(tag, filename);
ID3Frame* frame = ID3Tag_FindFrameWithID(tag, ID3FID_TITLE);
print_frame("\n Title", frame);
frame = ID3Tag_FindFrameWithID(tag, ID3FID_ALBUM);
print_frame(" Album", frame);
frame = ID3Tag_FindFrameWithID(tag, ID3FID_PERFORMERSORTORDER);
print_frame(" Performer", frame);
frame = ID3Tag_FindFrameWithID(tag, ID3FID_COMMENT);
print_frame(" Comments", frame);
}
As we can see the printf
is being used as before.
So, the same payload was used and introduced in the title field using id3v2 --TIT2 "%x %x %x %x" music.mp
.
As we see, the printf
does not have arguments to try to find them in the stack, but the values that end up being used might not be formatted in such a way that can be converted into a string. So, if a %s
is given to the printf
we can have a DoS (it crashes).
The main steps to perform the exploit are:
-
Inject into the server process a malicious code that we want to execute.
-
Overflow the buffer to reach the return address of the vulnerable function.
-
Overwrite the return address with a pointer to our code, to get it executed.
Using the fuzzer, we can discover that the server crashes with 2000 bytes, so adding 400 bytes to that we can use MetaSploit's pattern creator to generate a 2400 byte pattern to use as payload. Using that as a payload we can find the content of the EIP register, being the offset 1970.
After setting this value in the exploit script and running it again with the value B (in hexadecimal, 0x42) in the retn variable, we can see that the EIP register takes this value.
From that, we can now specify whichever payload we desire. Using a long string of known values on both the server side and the attacker side, we can test which characters do not match, the bad characters.
We do this until there are no more characters that do not match, this way we can ensure that we can fully write to the desired memory space.
Continuing the attack, 9 addresses are exposed to where we can direct our exploitation payload to initiate a connection from the server to the client, a reverse shell
This payload can be crafted with another of MetaSploit's tools.
By initiating netcat
and commanding it to accept incoming connections, we can then send the exploit to the server, and this results in a successful connection between the server and the attacker through a shell.
The other Overflows function the same way, please view the annex at the end of this report to view which offset and bad characters were found for each one.
Consult the annexes