Arbitrary Read/Write

Arbitrary Read

fwrite

  • Set the _fileno to the file descriptor of stdout

  • Set _flag & ~_IO_NO_WRITES

  • Set _flag |= _IO_CURRENTLY_PUTTING

  • Set the write_base and write_ptr to memory address which you want to read

  • Set _IO_read_end equal to _IO_write_base

or though not always reliable, in pwntools:

fp = FileStructure(0x0)
payload = fp.read(addr, 0x120)
io.send(payload)

fclose

upon fclose(), if buffer is not empty, it will be flushed (written to the _fileno)

  • Set the _fileno to the file descriptor of choice

  • Set write_base to the start of write address

  • Set write_ptr to the end of write address

circle-exclamation

explained in-depth in this writeup:

we can trace the function calls as the following,

which then calls _IO_FINISH(),

referring to the jump table, we can see that the implementation for __finish is _IO_new_file_finish()

which will call the flush macro _IO_do_flush() and is defined as follows:

which writes write_base up until write_ptr


puts

refer to fwrite.


Arbitrary Write

fread

  • Set the _fileno to file descriptor of stdin

  • Set _flag &~ _IO_NO_READS

  • Set _flag |= _IO_CURRENTLY_PUTTING

  • Set read_base equals to read_ptr to NULL

  • Set the buf_base and buf_end to memory address which you want to write

  • buf_end - buf_base > size of fread (BUFFER SIZE > READ SIZE) (or other function, scanf, etc)

or though not always reliable, in pwntools:


fwrite

fwrite(const void *buf, size_t size, size_t count, FILE *fp);

  • Set fp->_flags to _IO_MAGIC | ~_IO_LINE_BUF | ~_IO_CURRENTLY_PUTTING

  • Set buf to the data wish to be written

  • Set fp->write_ptr to the start of write address

  • Set fp->write_end to the end of write address

we can trace the function calls as the following,

which calls _IO_sputn() macro:

referring to the jump table, we can see that the implementation for __xsputn is _IO_new_file_xsputn()

our interest lies in __mempcpy (f->_IO_write_ptr, s, count); where it will do memcpy of destination write_ptr and source s (buf).

first, note that initially count and to_do is set to 0 as default, then modified over the course of the function execution.

at [3] we need count to be bigger than to_do which can be easily done because it is 0 as default, so we just need to somehow able to change count to be bigger than 0 which also a condition in [2]

though the block at [0] also modifies count, I think its the route at [1] is more trivial and intuitive. so we need to satisfy [1] while dissatisfy [0].

when all of those requirements are fulfilled, it will then call memcpy which corresponds to write profit.


Last updated