FILE Structure Attack: Part 1
When reading some of the pwn challenges in the recent Hacklu CTF 2022, the byor
challenge was catching my eye, because it is about a FILE structure attack in the recent glibc. I have played CTF for a while, but I don’t have any idea about it at all.
So, I decided to learn the fundamentals first on what it is and I was writing this note during the process. Some of the attacks or concepts that I will explain in this series won’t work in the recent glibc. But I believe we need to understand the old version attack first so that we have fundamentals of the FILE structure attack which will help us a lot in understanding the newest attack of it. This note is the summary of various resources that I read during learning about it and I hope that this article will help future me and other people who try to learn about it.
FILE Explanation
Intro
FILE
is a data type defined in the glibc which is usually used when we want to open a file in C. Note that this is different from the OS file descriptor that we usually use. The purpose of this data type is basically to make the file operation faster by using a buffer to reduce the number of IO syscall (read, write).
The concept (simplified version) is that rather than you use write
syscall each time you want to write new data to a file (which will directly write the data to the harddisk), by using the defined methods in the stdio lib for FILE
data type operations (in this case is fwrite
), stdio will try to handle all the operations by managing the data in the buffer first (resides in memory), and then will move it to the hard disk (via the OS syscall) when a certain condition is met (For example, when the buffer is full or got flushed).
Implementation in C
Reading through the source code of the glibc, below I try to summarize some notable implementations related to the FILE
structure data type that we need to take a look at so that we can understand more about how it works (We use glibc-2.35 in this specific section, but keep in mind that there might be a little different on how the struct is defined on each glibc version).
|
|
Turn out, FILE
datatype is a struct called _IO_FILE
.
|
|
This is the rough struct of how FILE
is implemented. For now, we can skip first how will the fields be used. We will explain more about it later when we talk about the history of the attacking scenario in glibc via FILE
structure.
|
|
Glibc also has the extended version of _IO_FILE
struct called _IO_FILE_plus
, which is _IO_FILE
+ vtable
(vtable = virtual table = array of pointers to the helper functions during executing the IO operation). The default filestream (stdin
, stdout
, stderr
) is using this extended version instead of the raw _IO_FILE
. Also if you open a file with fopen
, it will use this extended version as well.
Why do we use the extended version (_IO_FILE_plus
)? The purpose is to make the IO operation faster by having the vtable
. The data type for the vtable
is _IO_jump_t
(see below LOCs), which stores the pointer to the needed IO helper methods.
|
|
How will the vtable
be filled during creating a new extended FILE
struct? It depends on the method that you use.
For example, if you try to open a new file via fopen
, based on the below LOCs, you will see that the vtable
will be initialized with the existing vtable
called _IO_file_jumps
. There’s a lot of existing vtable
other than _IO_file_jumps
(for example there is also _IO_str_jumps
).
|
|
How to call it? Turn out it has implemented some definitions to make the jump call easier. Example:
|
|
Above LOC is the example that the glibc implements some definitions for IO Operations which will be translated to jump to the stored pointer based on its key (index).
For example, if it calls _IO_FINISH(FP)
, that means it will call the stored function pointer of the passed FILE
variable, specifically FP.vtable[idx]
entry (idx
is the index of __finish
and vtable
is the _IO_file_jumps
in this case).
|
|
Another key point in FILE
structure is that glibc maintains a linked list of the available FILE
in a binary. Each of them will be connected via the _chain
attribute (Refer to the _IO_FILE
) struct. The linked list header will be the stderr
by default (take a look in the below GDB), and the value will be updated with the most recent FILE
that you open.
|
|
The usage of vtable
in a FILE
structure
To give an example of how the IO Operations work and how the vtable
will be used, let’s take a look at what will happen when we call exit()
in the below C
program.
|
|
What will happen when the binary executes the exit
? Will IO operations take part in it?
Let’s take a look at the glibc implementation (version 2.35) and try to follow the calls (I will skip some LOCs because I only want to showcase how the vtable will be used).
|
|
Okay, it turns out that exit will call __run_exit_handlers
. Let’s move into that method.
|
|
Focusing on that LOC, What does it do?
Inspecting the compiled binary via gdb, turn out it will call _IO_cleanup
|
|
|
|
What will _IO_flush_all_lockp
do?
|
|
Some notes that you could take from reading those LOCs:
_IO_flush_all_lockp
will iterate all availableFILE
(iterating from theFILE
linked list header stored in the_IO_list_all
).- If meeting certain conditions, it will call
_IO_OVERFLOW (fp, EOF)
Remember that _IO_OVERFLOW (fp, EOF)
means that it will try to do the call by jumping to the stored pointer in the fp.vtable[__overflow]
.
This is one of the examples of how the vtable
in a FILE
object will be used, and this kind of IO operation happens in other methods as well, not limited to exit
.
N.B. If you try to explore more by yourself, in the method _IO_unbuffer_all
which is also called during _IO_cleanup
, you will notice that there is a vtable
call as well, which is _IO_SETBUF (fp, NULL, 0);
Possible Attack Scenario
Taking an example from the above scenario on how IO operation works inside the exit
call of C library, there are some possible attack scenarios that we can do to abuse the FILE
structure:
- Hijack the
vtable
of the IO file (For example,stdout
).- Remember that when we call
exit
in the above example, it will iterate theFILE
linked list, and if some constraints are fulfilled, it will callfp.vtable[__overflow]
right? - If we’re able to hijack the file
vtable
entry of__overflow
with let’s say a pointer tosystem
, that means if the binary callexit()
, instead of quitting the binary, it will execute a command instead. Some possible ways to hijack it:- Create a fake
vtable
and overwrite the IO file stored pointer with the address of our fakevtable
, so that when the IO operation tries to call__overflow
, it will jump to our desired function pointer. - Overwrite the
vtable
pointer to another availablevtable
. For example, by default,stdout, stdin, stderr
used_IO_file_jumps
as the storedvtable
. We can try to overwrite it with_IO_str_jumps
, so that let’s say when the IO operation wants to call__overflow
, it will use the__overflow
stored inside the str jumps vtable instead of the file jumps vtable (will be explained more in the next article, but the__overflow
in the str jumps can be abused to call our desired function pointer if we’re able to do some forgery on the file structure metadata). - Misaligned the
vtable
, so that let’s say when the IO operation tries to call let’s say__finish
, instead of calling__finish
, due to the misalignment, it will call__overflow
instead (and continue with the previous point scenario).
- Create a fake
- Remember that when we call
- Forge a fake
FILE
structure with a fakevtable
, and then somehow try to trigger_IO_flush_all_lockp
.- Remember that
_IO_flush_all_lockp
will iterate each availableFILE
in the linked list, so if we’re able to create a fakeFILE
structure and trigger the flush, that means it will use our fakevtable
which will allow us to execute a command as well.
- Remember that
- Use the
FILE
buffer metadata so that we can dowrite
operation in our desired target address (Arbitrary Address Write).
The detail of each attack will be explained in the next part, but spoilers ahead, some of these possible attacks are only working in the old glibc version.
Resources
- https://stackoverflow.com/questions/1658476/c-fopen-vs-open
- https://www.slideshare.net/AngelBoy1/play-with-file-structure-yet-another-binary-exploit-technique
- https://dhavalkapil.com/blogs/FILE-Structure-Exploitation/
- https://ctf-wiki.mahaloz.re/pwn/linux/io_file/introduction/
- https://faraz.faith/2020-10-13-FSOP-lazynote/
- https://blog.kylebot.net/2022/10/22/angry-FSROP/
Social Media
Follow me on twitter