# File Stream Oriented Programming - study material
- https://elixir.bootlin.com/glibc/glibc-2.31/source/libio
- https://elixir.bootlin.com/glibc/glibc-2.27/source/libio
- https://elixir.bootlin.com/glibc/glibc-2.23/source/libio
## Theory (resources)
- https://ctf-wiki.mahaloz.re/pwn/linux/io_file/introduction/
- https://ctf-wiki.mahaloz.re/pwn/linux/io_file/fake-vtable-exploit/
- https://ctf-wiki.mahaloz.re/pwn/linux/io_file/fsop/
- https://ctf-wiki.mahaloz.re/pwn/linux/io_file/exploit-in-libc2.24/
- https://gsec.hitb.org/materials/sg2018/D1%20-%20FILE%20Structures%20-%20Another%20Binary%20Exploitation%20Technique%20-%20An-Jie%20Yang.pdf
- https://www.youtube.com/watch?v=Fr3VU5hdL4s&t=617s
- https://c4ebt.github.io/2021/01/22/House-of-Rust.html
- https://blog.kylebot.net/2022/10/22/angry-FSROP/
- https://bbs.pediy.com/thread-273832.htm
- https://www.roderickchan.cn/post/house-of-apple-%E4%B8%80%E7%A7%8D%E6%96%B0%E7%9A%84glibc%E4%B8%ADio%E6%94%BB%E5%87%BB%E6%96%B9%E6%B3%95-2/
- https://chovid99.github.io/posts/file-structure-attack-part-1/
## Practice (challenges)
- https://github.com/justcatthefish/ctf-writeups/tree/master/2022-11-12-SECCONQuals/babyfile (libc 2.31)
- https://faraz.faith/2020-10-13-FSOP-lazynote/ (libc 2.27)
- https://ypl.coffee/pwn2win-2020-at-your-command/ (libc 2.27)
- https://github.com/Mymaqn/roadtopwn/tree/main/challenge_writeups/FSOP (libc 2.23)
- https://robertchen.cc/blog/2020/06/28/house-of-red (libc 2.27)
- https://github.com/AndyNovo/UDCTF22/tree/master/pwn/wideopen (libc 2.34)
## Blog Post (we can start adding content here as we go along)
- start by introducing what FILE is, what FSOP is (all according to libc 2.23)
A FILE is a data structure that describes a file stream. Below is the documented structure of a FILE:
```c
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
/* We always allocate an extra word following an _IO_FILE.
This contains a pointer to the function jump table used.
This is for compatibility with C++ streambuf; the word can
be used to smash to a pointer to a virtual function table. */
struct _IO_FILE_plus
{
_IO_FILE file;
const struct _IO_jump_t *vtable;
};
struct _IO_jump_t
{
JUMP_FIELD(size_t, __dummy);
JUMP_FIELD(size_t, __dummy2);
JUMP_FIELD(_IO_finish_t, __finish);
JUMP_FIELD(_IO_overflow_t, __overflow);
JUMP_FIELD(_IO_underflow_t, __underflow);
JUMP_FIELD(_IO_underflow_t, __uflow);
JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
/* showmany */
JUMP_FIELD(_IO_xsputn_t, __xsputn);
JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
JUMP_FIELD(_IO_seekoff_t, __seekoff);
JUMP_FIELD(_IO_seekpos_t, __seekpos);
JUMP_FIELD(_IO_setbuf_t, __setbuf);
JUMP_FIELD(_IO_sync_t, __sync);
JUMP_FIELD(_IO_doallocate_t, __doallocate);
JUMP_FIELD(_IO_read_t, __read);
JUMP_FIELD(_IO_write_t, __write);
JUMP_FIELD(_IO_seek_t, __seek);
JUMP_FIELD(_IO_close_t, __close);
JUMP_FIELD(_IO_stat_t, __stat);
JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
JUMP_FIELD(_IO_imbue_t, __imbue);
#if 0
get_column;
set_column;
#endif
};
```
When certain file-related functions are called on a FILE (like fread, fwrite, fopen, fclose), these functions will be resolved from the virtual function table of the FILE. If we have control over the table, we can control program flow.
Each FILE is chained via linked list, and the head of the linked list points to `_IO_LIST_ALL`
`_IO_FLUSH_ALL_LOCKP` - flushes all FILES from the linked list
Triggered when:
1. LIBC abort due to memory corruption
2. exit()
3. return from main
If you can control the vtable of each FILE, you can get FSOP during events like SIGABRT.
As you might know, stdin stdout and stderr are considered file streams too. Hence they are managed with FILE. Operations like fgets,
can be exploited to trigger FSOP.
### Protections
vtable is protected by
- introduce 2.23 exploit techniques
- introduce 2.27 exploit techniques